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 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 61 bool HTTPServer::isAvailable() { return true; } 62 63 HTTPServer::HTTPServer() { Server = std::make_unique<httplib::Server>(); } 64 65 HTTPServer::~HTTPServer() { stop(); } 66 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 80 HTTPServerRequest::HTTPServerRequest(const httplib::Request &HTTPLibRequest, 81 httplib::Response &HTTPLibResponse) 82 : HTTPLibResponse(HTTPLibResponse) { 83 expandUrlPathMatches(HTTPLibRequest.matches, *this); 84 } 85 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 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 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 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 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 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 147 void HTTPServer::stop() { 148 Server->stop(); 149 Port = 0; 150 } 151 152 #else 153 154 // TODO: Implement barebones standalone HTTP server implementation. 155 bool HTTPServer::isAvailable() { return false; } 156 157 HTTPServer::HTTPServer() = default; 158 159 HTTPServer::~HTTPServer() = default; 160 161 void HTTPServerRequest::setResponse(HTTPResponse Response) { 162 llvm_unreachable("No HTTP server implementation available"); 163 } 164 165 void HTTPServerRequest::setResponse(StreamingHTTPResponse Response) { 166 llvm_unreachable("No HTTP server implementation available"); 167 } 168 169 Error HTTPServer::get(StringRef UrlPathPattern, HTTPRequestHandler Handler) { 170 llvm_unreachable("No HTTP server implementation available"); 171 } 172 173 Error HTTPServer::bind(unsigned ListenPort, const char *HostInterface) { 174 llvm_unreachable("No HTTP server implementation available"); 175 } 176 177 Expected<unsigned> HTTPServer::bind(const char *HostInterface) { 178 llvm_unreachable("No HTTP server implementation available"); 179 } 180 181 Error HTTPServer::listen() { 182 llvm_unreachable("No HTTP server implementation available"); 183 } 184 185 void HTTPServer::stop() { 186 llvm_unreachable("No HTTP server implementation available"); 187 } 188 189 #endif // LLVM_ENABLE_HTTPLIB 190