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 29using namespace llvm; 30 31char HTTPServerError::ID = 0; 32 33HTTPServerError::HTTPServerError(const Twine &Msg) : Msg(Msg.str()) {} 34 35void HTTPServerError::log(raw_ostream &OS) const { OS << Msg; } 36 37bool 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 67bool HTTPServer::isAvailable() { return true; } 68 69HTTPServer::HTTPServer() { Server = std::make_unique<httplib::Server>(); } 70 71HTTPServer::~HTTPServer() { stop(); } 72 73static 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 86HTTPServerRequest::HTTPServerRequest(const httplib::Request &HTTPLibRequest, 87 httplib::Response &HTTPLibResponse) 88 : HTTPLibResponse(HTTPLibResponse) { 89 expandUrlPathMatches(HTTPLibRequest.matches, *this); 90} 91 92void HTTPServerRequest::setResponse(HTTPResponse Response) { 93 HTTPLibResponse.set_content(Response.Body.begin(), Response.Body.size(), 94 Response.ContentType); 95 HTTPLibResponse.status = Response.Code; 96} 97 98void 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 113Error 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 126Error 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 134Expected<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 142Error 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 153void HTTPServer::stop() { 154 Server->stop(); 155 Port = 0; 156} 157 158#else 159 160// TODO: Implement barebones standalone HTTP server implementation. 161bool HTTPServer::isAvailable() { return false; } 162 163HTTPServer::HTTPServer() = default; 164 165HTTPServer::~HTTPServer() = default; 166 167void HTTPServerRequest::setResponse(HTTPResponse Response) { 168 llvm_unreachable("no httplib"); 169} 170 171void HTTPServerRequest::setResponse(StreamingHTTPResponse Response) { 172 llvm_unreachable("no httplib"); 173} 174 175Error 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 182Error HTTPServer::bind(unsigned ListenPort, const char *HostInterface) { 183 return make_error<HTTPServerError>("no httplib"); 184} 185 186Expected<unsigned> HTTPServer::bind(const char *HostInterface) { 187 return make_error<HTTPServerError>("no httplib"); 188} 189 190Error HTTPServer::listen() { 191 return make_error<HTTPServerError>("no httplib"); 192} 193 194void HTTPServer::stop() { 195 llvm_unreachable("no httplib"); 196} 197 198#endif // LLVM_ENABLE_HTTPLIB 199