1 //===-- llvm/Debuginfod/HTTPServer.cpp - HTTP server library -----*- C++-*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
11 /// This file defines the methods of the HTTPServer class and the streamFile
14 //===----------------------------------------------------------------------===//
16 #include "llvm/Debuginfod/HTTPServer.h"
17 #include "llvm/ADT/StringExtras.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/Support/Errc.h"
20 #include "llvm/Support/Error.h"
21 #include "llvm/Support/FileSystem.h"
22 #include "llvm/Support/MemoryBuffer.h"
23 #include "llvm/Support/Regex.h"
25 #ifdef LLVM_ENABLE_HTTPLIB
31 char HTTPServerError::ID
= 0;
33 HTTPServerError::HTTPServerError(const Twine
&Msg
) : Msg(Msg
.str()) {}
35 void HTTPServerError::log(raw_ostream
&OS
) const { OS
<< Msg
; }
37 bool llvm::streamFile(HTTPServerRequest
&Request
, StringRef FilePath
) {
38 Expected
<sys::fs::file_t
> FDOrErr
= sys::fs::openNativeFileForRead(FilePath
);
39 if (Error Err
= FDOrErr
.takeError()) {
40 consumeError(std::move(Err
));
41 Request
.setResponse({404u, "text/plain", "Could not open file to read.\n"});
44 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> MBOrErr
=
45 MemoryBuffer::getOpenFile(*FDOrErr
, FilePath
,
47 /*RequiresNullTerminator=*/false);
48 sys::fs::closeFile(*FDOrErr
);
49 if (Error Err
= errorCodeToError(MBOrErr
.getError())) {
50 consumeError(std::move(Err
));
51 Request
.setResponse({404u, "text/plain", "Could not memory-map file.\n"});
54 // Lambdas are copied on conversion to std::function, preventing use of
56 MemoryBuffer
*MB
= MBOrErr
->release();
57 Request
.setResponse({200u, "application/octet-stream", MB
->getBufferSize(),
58 [=](size_t Offset
, size_t Length
) -> StringRef
{
59 return MB
->getBuffer().substr(Offset
, Length
);
61 [=](bool Success
) { delete MB
; }});
65 #ifdef LLVM_ENABLE_HTTPLIB
67 bool HTTPServer::isAvailable() { return true; }
69 HTTPServer::HTTPServer() { Server
= std::make_unique
<httplib::Server
>(); }
71 HTTPServer::~HTTPServer() { stop(); }
73 static void expandUrlPathMatches(const std::smatch
&Matches
,
74 HTTPServerRequest
&Request
) {
75 bool UrlPathSet
= false;
76 for (const auto &it
: Matches
) {
78 Request
.UrlPathMatches
.push_back(it
);
86 HTTPServerRequest::HTTPServerRequest(const httplib::Request
&HTTPLibRequest
,
87 httplib::Response
&HTTPLibResponse
)
88 : HTTPLibResponse(HTTPLibResponse
) {
89 expandUrlPathMatches(HTTPLibRequest
.matches
, *this);
92 void HTTPServerRequest::setResponse(HTTPResponse Response
) {
93 HTTPLibResponse
.set_content(Response
.Body
.begin(), Response
.Body
.size(),
94 Response
.ContentType
);
95 HTTPLibResponse
.status
= Response
.Code
;
98 void HTTPServerRequest::setResponse(StreamingHTTPResponse Response
) {
99 HTTPLibResponse
.set_content_provider(
100 Response
.ContentLength
, Response
.ContentType
,
101 [=](size_t Offset
, size_t Length
, httplib::DataSink
&Sink
) {
102 if (Offset
< Response
.ContentLength
) {
103 StringRef Chunk
= Response
.Provider(Offset
, Length
);
104 Sink
.write(Chunk
.begin(), Chunk
.size());
108 [=](bool Success
) { Response
.CompletionHandler(Success
); });
110 HTTPLibResponse
.status
= Response
.Code
;
113 Error
HTTPServer::get(StringRef UrlPathPattern
, HTTPRequestHandler Handler
) {
114 std::string ErrorMessage
;
115 if (!Regex(UrlPathPattern
).isValid(ErrorMessage
))
116 return createStringError(errc::argument_out_of_domain
, ErrorMessage
);
117 Server
->Get(std::string(UrlPathPattern
),
118 [Handler
](const httplib::Request
&HTTPLibRequest
,
119 httplib::Response
&HTTPLibResponse
) {
120 HTTPServerRequest
Request(HTTPLibRequest
, HTTPLibResponse
);
123 return Error::success();
126 Error
HTTPServer::bind(unsigned ListenPort
, const char *HostInterface
) {
127 if (!Server
->bind_to_port(HostInterface
, ListenPort
))
128 return createStringError(errc::io_error
,
129 "Could not assign requested address.");
131 return Error::success();
134 Expected
<unsigned> HTTPServer::bind(const char *HostInterface
) {
135 int ListenPort
= Server
->bind_to_any_port(HostInterface
);
137 return createStringError(errc::io_error
,
138 "Could not assign any port on requested address.");
139 return Port
= ListenPort
;
142 Error
HTTPServer::listen() {
144 return createStringError(errc::io_error
,
145 "Cannot listen without first binding to a port.");
146 if (!Server
->listen_after_bind())
147 return createStringError(
149 "An unknown error occurred when cpp-httplib attempted to listen.");
150 return Error::success();
153 void HTTPServer::stop() {
160 // TODO: Implement barebones standalone HTTP server implementation.
161 bool HTTPServer::isAvailable() { return false; }
163 HTTPServer::HTTPServer() = default;
165 HTTPServer::~HTTPServer() = default;
167 void HTTPServerRequest::setResponse(HTTPResponse Response
) {
168 llvm_unreachable("no httplib");
171 void HTTPServerRequest::setResponse(StreamingHTTPResponse Response
) {
172 llvm_unreachable("no httplib");
175 Error
HTTPServer::get(StringRef UrlPathPattern
, HTTPRequestHandler Handler
) {
176 // TODO(https://github.com/llvm/llvm-project/issues/63873) We would ideally
177 // return an error as well but that's going to require refactoring of error
178 // handling in DebuginfodServer.
179 return Error::success();
182 Error
HTTPServer::bind(unsigned ListenPort
, const char *HostInterface
) {
183 return make_error
<HTTPServerError
>("no httplib");
186 Expected
<unsigned> HTTPServer::bind(const char *HostInterface
) {
187 return make_error
<HTTPServerError
>("no httplib");
190 Error
HTTPServer::listen() {
191 return make_error
<HTTPServerError
>("no httplib");
194 void HTTPServer::stop() {
195 llvm_unreachable("no httplib");
198 #endif // LLVM_ENABLE_HTTPLIB