1 //===-- llvm/Debuginfod/HTTPServer.cpp - HTTP server library -----*- C++-*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 ///
11 /// This file defines the methods of the HTTPServer class and the streamFile
12 /// function.
13 ///
14 //===----------------------------------------------------------------------===//
15
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"
24
25 #ifdef LLVM_ENABLE_HTTPLIB
26 #include "httplib.h"
27 #endif
28
29 using namespace llvm;
30
streamFile(HTTPServerRequest & Request,StringRef FilePath)31 bool llvm::streamFile(HTTPServerRequest &Request, StringRef FilePath) {
32 Expected<sys::fs::file_t> FDOrErr = sys::fs::openNativeFileForRead(FilePath);
33 if (Error Err = FDOrErr.takeError()) {
34 consumeError(std::move(Err));
35 Request.setResponse({404u, "text/plain", "Could not open file to read.\n"});
36 return false;
37 }
38 ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
39 MemoryBuffer::getOpenFile(*FDOrErr, FilePath,
40 /*FileSize=*/-1,
41 /*RequiresNullTerminator=*/false);
42 sys::fs::closeFile(*FDOrErr);
43 if (Error Err = errorCodeToError(MBOrErr.getError())) {
44 consumeError(std::move(Err));
45 Request.setResponse({404u, "text/plain", "Could not memory-map file.\n"});
46 return false;
47 }
48 // Lambdas are copied on conversion to to std::function, preventing use of
49 // smart pointers.
50 MemoryBuffer *MB = MBOrErr->release();
51 Request.setResponse({200u, "application/octet-stream", MB->getBufferSize(),
52 [=](size_t Offset, size_t Length) -> StringRef {
53 return MB->getBuffer().substr(Offset, Length);
54 },
55 [=](bool Success) { delete MB; }});
56 return true;
57 }
58
59 #ifdef LLVM_ENABLE_HTTPLIB
60
isAvailable()61 bool HTTPServer::isAvailable() { return true; }
62
HTTPServer()63 HTTPServer::HTTPServer() { Server = std::make_unique<httplib::Server>(); }
64
~HTTPServer()65 HTTPServer::~HTTPServer() { stop(); }
66
expandUrlPathMatches(const std::smatch & Matches,HTTPServerRequest & Request)67 static void expandUrlPathMatches(const std::smatch &Matches,
68 HTTPServerRequest &Request) {
69 bool UrlPathSet = false;
70 for (const auto &it : Matches) {
71 if (UrlPathSet)
72 Request.UrlPathMatches.push_back(it);
73 else {
74 Request.UrlPath = it;
75 UrlPathSet = true;
76 }
77 }
78 }
79
HTTPServerRequest(const httplib::Request & HTTPLibRequest,httplib::Response & HTTPLibResponse)80 HTTPServerRequest::HTTPServerRequest(const httplib::Request &HTTPLibRequest,
81 httplib::Response &HTTPLibResponse)
82 : HTTPLibResponse(HTTPLibResponse) {
83 expandUrlPathMatches(HTTPLibRequest.matches, *this);
84 }
85
setResponse(HTTPResponse Response)86 void HTTPServerRequest::setResponse(HTTPResponse Response) {
87 HTTPLibResponse.set_content(Response.Body.begin(), Response.Body.size(),
88 Response.ContentType);
89 HTTPLibResponse.status = Response.Code;
90 }
91
setResponse(StreamingHTTPResponse Response)92 void HTTPServerRequest::setResponse(StreamingHTTPResponse Response) {
93 HTTPLibResponse.set_content_provider(
94 Response.ContentLength, Response.ContentType,
95 [=](size_t Offset, size_t Length, httplib::DataSink &Sink) {
96 if (Offset < Response.ContentLength) {
97 StringRef Chunk = Response.Provider(Offset, Length);
98 Sink.write(Chunk.begin(), Chunk.size());
99 }
100 return true;
101 },
102 [=](bool Success) { Response.CompletionHandler(Success); });
103
104 HTTPLibResponse.status = Response.Code;
105 }
106
get(StringRef UrlPathPattern,HTTPRequestHandler Handler)107 Error HTTPServer::get(StringRef UrlPathPattern, HTTPRequestHandler Handler) {
108 std::string ErrorMessage;
109 if (!Regex(UrlPathPattern).isValid(ErrorMessage))
110 return createStringError(errc::argument_out_of_domain, ErrorMessage);
111 Server->Get(std::string(UrlPathPattern),
112 [Handler](const httplib::Request &HTTPLibRequest,
113 httplib::Response &HTTPLibResponse) {
114 HTTPServerRequest Request(HTTPLibRequest, HTTPLibResponse);
115 Handler(Request);
116 });
117 return Error::success();
118 }
119
bind(unsigned ListenPort,const char * HostInterface)120 Error HTTPServer::bind(unsigned ListenPort, const char *HostInterface) {
121 if (!Server->bind_to_port(HostInterface, ListenPort))
122 return createStringError(errc::io_error,
123 "Could not assign requested address.");
124 Port = ListenPort;
125 return Error::success();
126 }
127
bind(const char * HostInterface)128 Expected<unsigned> HTTPServer::bind(const char *HostInterface) {
129 int ListenPort = Server->bind_to_any_port(HostInterface);
130 if (ListenPort < 0)
131 return createStringError(errc::io_error,
132 "Could not assign any port on requested address.");
133 return Port = ListenPort;
134 }
135
listen()136 Error HTTPServer::listen() {
137 if (!Port)
138 return createStringError(errc::io_error,
139 "Cannot listen without first binding to a port.");
140 if (!Server->listen_after_bind())
141 return createStringError(
142 errc::io_error,
143 "An unknown error occurred when cpp-httplib attempted to listen.");
144 return Error::success();
145 }
146
stop()147 void HTTPServer::stop() {
148 Server->stop();
149 Port = 0;
150 }
151
152 #else
153
154 // TODO: Implement barebones standalone HTTP server implementation.
isAvailable()155 bool HTTPServer::isAvailable() { return false; }
156
157 HTTPServer::HTTPServer() = default;
158
159 HTTPServer::~HTTPServer() = default;
160
setResponse(HTTPResponse Response)161 void HTTPServerRequest::setResponse(HTTPResponse Response) {
162 llvm_unreachable("No HTTP server implementation available");
163 }
164
setResponse(StreamingHTTPResponse Response)165 void HTTPServerRequest::setResponse(StreamingHTTPResponse Response) {
166 llvm_unreachable("No HTTP server implementation available");
167 }
168
get(StringRef UrlPathPattern,HTTPRequestHandler Handler)169 Error HTTPServer::get(StringRef UrlPathPattern, HTTPRequestHandler Handler) {
170 llvm_unreachable("No HTTP server implementation available");
171 }
172
bind(unsigned ListenPort,const char * HostInterface)173 Error HTTPServer::bind(unsigned ListenPort, const char *HostInterface) {
174 llvm_unreachable("No HTTP server implementation available");
175 }
176
bind(const char * HostInterface)177 Expected<unsigned> HTTPServer::bind(const char *HostInterface) {
178 llvm_unreachable("No HTTP server implementation available");
179 }
180
listen()181 Error HTTPServer::listen() {
182 llvm_unreachable("No HTTP server implementation available");
183 }
184
stop()185 void HTTPServer::stop() {
186 llvm_unreachable("No HTTP server implementation available");
187 }
188
189 #endif // LLVM_ENABLE_HTTPLIB
190