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 31 char HTTPServerError::ID = 0; 32 33 HTTPServerError::HTTPServerError(const Twine &Msg) : Msg(Msg.str()) {} 34 35 void HTTPServerError::log(raw_ostream &OS) const { OS << Msg; } 36 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"}); 42 return false; 43 } 44 ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = 45 MemoryBuffer::getOpenFile(*FDOrErr, FilePath, 46 /*FileSize=*/-1, 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"}); 52 return false; 53 } 54 // Lambdas are copied on conversion to std::function, preventing use of 55 // smart pointers. 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); 60 }, 61 [=](bool Success) { delete MB; }}); 62 return true; 63 } 64 65 #ifdef LLVM_ENABLE_HTTPLIB 66 67 bool HTTPServer::isAvailable() { return true; } 68 69 HTTPServer::HTTPServer() { Server = std::make_unique<httplib::Server>(); } 70 71 HTTPServer::~HTTPServer() { stop(); } 72 73 static void expandUrlPathMatches(const std::smatch &Matches, 74 HTTPServerRequest &Request) { 75 bool UrlPathSet = false; 76 for (const auto &it : Matches) { 77 if (UrlPathSet) 78 Request.UrlPathMatches.push_back(it); 79 else { 80 Request.UrlPath = it; 81 UrlPathSet = true; 82 } 83 } 84 } 85 86 HTTPServerRequest::HTTPServerRequest(const httplib::Request &HTTPLibRequest, 87 httplib::Response &HTTPLibResponse) 88 : HTTPLibResponse(HTTPLibResponse) { 89 expandUrlPathMatches(HTTPLibRequest.matches, *this); 90 } 91 92 void HTTPServerRequest::setResponse(HTTPResponse Response) { 93 HTTPLibResponse.set_content(Response.Body.begin(), Response.Body.size(), 94 Response.ContentType); 95 HTTPLibResponse.status = Response.Code; 96 } 97 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()); 105 } 106 return true; 107 }, 108 [=](bool Success) { Response.CompletionHandler(Success); }); 109 110 HTTPLibResponse.status = Response.Code; 111 } 112 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); 121 Handler(Request); 122 }); 123 return Error::success(); 124 } 125 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."); 130 Port = ListenPort; 131 return Error::success(); 132 } 133 134 Expected<unsigned> HTTPServer::bind(const char *HostInterface) { 135 int ListenPort = Server->bind_to_any_port(HostInterface); 136 if (ListenPort < 0) 137 return createStringError(errc::io_error, 138 "Could not assign any port on requested address."); 139 return Port = ListenPort; 140 } 141 142 Error HTTPServer::listen() { 143 if (!Port) 144 return createStringError(errc::io_error, 145 "Cannot listen without first binding to a port."); 146 if (!Server->listen_after_bind()) 147 return createStringError( 148 errc::io_error, 149 "An unknown error occurred when cpp-httplib attempted to listen."); 150 return Error::success(); 151 } 152 153 void HTTPServer::stop() { 154 Server->stop(); 155 Port = 0; 156 } 157 158 #else 159 160 // TODO: Implement barebones standalone HTTP server implementation. 161 bool HTTPServer::isAvailable() { return false; } 162 163 HTTPServer::HTTPServer() = default; 164 165 HTTPServer::~HTTPServer() = default; 166 167 void HTTPServerRequest::setResponse(HTTPResponse Response) { 168 llvm_unreachable("no httplib"); 169 } 170 171 void HTTPServerRequest::setResponse(StreamingHTTPResponse Response) { 172 llvm_unreachable("no httplib"); 173 } 174 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(); 180 } 181 182 Error HTTPServer::bind(unsigned ListenPort, const char *HostInterface) { 183 return make_error<HTTPServerError>("no httplib"); 184 } 185 186 Expected<unsigned> HTTPServer::bind(const char *HostInterface) { 187 return make_error<HTTPServerError>("no httplib"); 188 } 189 190 Error HTTPServer::listen() { 191 return make_error<HTTPServerError>("no httplib"); 192 } 193 194 void HTTPServer::stop() { 195 llvm_unreachable("no httplib"); 196 } 197 198 #endif // LLVM_ENABLE_HTTPLIB 199