1753f127fSDimitry Andric //===-- llvm/Debuginfod/HTTPServer.cpp - HTTP server library -----*- C++-*-===// 2753f127fSDimitry Andric // 3753f127fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4753f127fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5753f127fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6753f127fSDimitry Andric // 7753f127fSDimitry Andric //===----------------------------------------------------------------------===// 8753f127fSDimitry Andric /// 9753f127fSDimitry Andric /// \file 10753f127fSDimitry Andric /// 11753f127fSDimitry Andric /// This file defines the methods of the HTTPServer class and the streamFile 12753f127fSDimitry Andric /// function. 13753f127fSDimitry Andric /// 14753f127fSDimitry Andric //===----------------------------------------------------------------------===// 15753f127fSDimitry Andric 16753f127fSDimitry Andric #include "llvm/Debuginfod/HTTPServer.h" 17753f127fSDimitry Andric #include "llvm/ADT/StringExtras.h" 18753f127fSDimitry Andric #include "llvm/ADT/StringRef.h" 19753f127fSDimitry Andric #include "llvm/Support/Errc.h" 20753f127fSDimitry Andric #include "llvm/Support/Error.h" 21753f127fSDimitry Andric #include "llvm/Support/FileSystem.h" 22753f127fSDimitry Andric #include "llvm/Support/MemoryBuffer.h" 23753f127fSDimitry Andric #include "llvm/Support/Regex.h" 24753f127fSDimitry Andric 25753f127fSDimitry Andric #ifdef LLVM_ENABLE_HTTPLIB 26753f127fSDimitry Andric #include "httplib.h" 27753f127fSDimitry Andric #endif 28753f127fSDimitry Andric 29753f127fSDimitry Andric using namespace llvm; 30753f127fSDimitry Andric 31*06c3fb27SDimitry Andric char HTTPServerError::ID = 0; 32*06c3fb27SDimitry Andric 33*06c3fb27SDimitry Andric HTTPServerError::HTTPServerError(const Twine &Msg) : Msg(Msg.str()) {} 34*06c3fb27SDimitry Andric 35*06c3fb27SDimitry Andric void HTTPServerError::log(raw_ostream &OS) const { OS << Msg; } 36*06c3fb27SDimitry Andric 37753f127fSDimitry Andric bool llvm::streamFile(HTTPServerRequest &Request, StringRef FilePath) { 38753f127fSDimitry Andric Expected<sys::fs::file_t> FDOrErr = sys::fs::openNativeFileForRead(FilePath); 39753f127fSDimitry Andric if (Error Err = FDOrErr.takeError()) { 40753f127fSDimitry Andric consumeError(std::move(Err)); 41753f127fSDimitry Andric Request.setResponse({404u, "text/plain", "Could not open file to read.\n"}); 42753f127fSDimitry Andric return false; 43753f127fSDimitry Andric } 44753f127fSDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = 45753f127fSDimitry Andric MemoryBuffer::getOpenFile(*FDOrErr, FilePath, 46753f127fSDimitry Andric /*FileSize=*/-1, 47753f127fSDimitry Andric /*RequiresNullTerminator=*/false); 48753f127fSDimitry Andric sys::fs::closeFile(*FDOrErr); 49753f127fSDimitry Andric if (Error Err = errorCodeToError(MBOrErr.getError())) { 50753f127fSDimitry Andric consumeError(std::move(Err)); 51753f127fSDimitry Andric Request.setResponse({404u, "text/plain", "Could not memory-map file.\n"}); 52753f127fSDimitry Andric return false; 53753f127fSDimitry Andric } 54753f127fSDimitry Andric // Lambdas are copied on conversion to to std::function, preventing use of 55753f127fSDimitry Andric // smart pointers. 56753f127fSDimitry Andric MemoryBuffer *MB = MBOrErr->release(); 57753f127fSDimitry Andric Request.setResponse({200u, "application/octet-stream", MB->getBufferSize(), 58753f127fSDimitry Andric [=](size_t Offset, size_t Length) -> StringRef { 59753f127fSDimitry Andric return MB->getBuffer().substr(Offset, Length); 60753f127fSDimitry Andric }, 61753f127fSDimitry Andric [=](bool Success) { delete MB; }}); 62753f127fSDimitry Andric return true; 63753f127fSDimitry Andric } 64753f127fSDimitry Andric 65753f127fSDimitry Andric #ifdef LLVM_ENABLE_HTTPLIB 66753f127fSDimitry Andric 67753f127fSDimitry Andric bool HTTPServer::isAvailable() { return true; } 68753f127fSDimitry Andric 69753f127fSDimitry Andric HTTPServer::HTTPServer() { Server = std::make_unique<httplib::Server>(); } 70753f127fSDimitry Andric 71753f127fSDimitry Andric HTTPServer::~HTTPServer() { stop(); } 72753f127fSDimitry Andric 73753f127fSDimitry Andric static void expandUrlPathMatches(const std::smatch &Matches, 74753f127fSDimitry Andric HTTPServerRequest &Request) { 75753f127fSDimitry Andric bool UrlPathSet = false; 76753f127fSDimitry Andric for (const auto &it : Matches) { 77753f127fSDimitry Andric if (UrlPathSet) 78753f127fSDimitry Andric Request.UrlPathMatches.push_back(it); 79753f127fSDimitry Andric else { 80753f127fSDimitry Andric Request.UrlPath = it; 81753f127fSDimitry Andric UrlPathSet = true; 82753f127fSDimitry Andric } 83753f127fSDimitry Andric } 84753f127fSDimitry Andric } 85753f127fSDimitry Andric 86753f127fSDimitry Andric HTTPServerRequest::HTTPServerRequest(const httplib::Request &HTTPLibRequest, 87753f127fSDimitry Andric httplib::Response &HTTPLibResponse) 88753f127fSDimitry Andric : HTTPLibResponse(HTTPLibResponse) { 89753f127fSDimitry Andric expandUrlPathMatches(HTTPLibRequest.matches, *this); 90753f127fSDimitry Andric } 91753f127fSDimitry Andric 92753f127fSDimitry Andric void HTTPServerRequest::setResponse(HTTPResponse Response) { 93753f127fSDimitry Andric HTTPLibResponse.set_content(Response.Body.begin(), Response.Body.size(), 94753f127fSDimitry Andric Response.ContentType); 95753f127fSDimitry Andric HTTPLibResponse.status = Response.Code; 96753f127fSDimitry Andric } 97753f127fSDimitry Andric 98753f127fSDimitry Andric void HTTPServerRequest::setResponse(StreamingHTTPResponse Response) { 99753f127fSDimitry Andric HTTPLibResponse.set_content_provider( 100753f127fSDimitry Andric Response.ContentLength, Response.ContentType, 101753f127fSDimitry Andric [=](size_t Offset, size_t Length, httplib::DataSink &Sink) { 102753f127fSDimitry Andric if (Offset < Response.ContentLength) { 103753f127fSDimitry Andric StringRef Chunk = Response.Provider(Offset, Length); 104753f127fSDimitry Andric Sink.write(Chunk.begin(), Chunk.size()); 105753f127fSDimitry Andric } 106753f127fSDimitry Andric return true; 107753f127fSDimitry Andric }, 108753f127fSDimitry Andric [=](bool Success) { Response.CompletionHandler(Success); }); 109753f127fSDimitry Andric 110753f127fSDimitry Andric HTTPLibResponse.status = Response.Code; 111753f127fSDimitry Andric } 112753f127fSDimitry Andric 113753f127fSDimitry Andric Error HTTPServer::get(StringRef UrlPathPattern, HTTPRequestHandler Handler) { 114753f127fSDimitry Andric std::string ErrorMessage; 115753f127fSDimitry Andric if (!Regex(UrlPathPattern).isValid(ErrorMessage)) 116753f127fSDimitry Andric return createStringError(errc::argument_out_of_domain, ErrorMessage); 117753f127fSDimitry Andric Server->Get(std::string(UrlPathPattern), 118753f127fSDimitry Andric [Handler](const httplib::Request &HTTPLibRequest, 119753f127fSDimitry Andric httplib::Response &HTTPLibResponse) { 120753f127fSDimitry Andric HTTPServerRequest Request(HTTPLibRequest, HTTPLibResponse); 121753f127fSDimitry Andric Handler(Request); 122753f127fSDimitry Andric }); 123753f127fSDimitry Andric return Error::success(); 124753f127fSDimitry Andric } 125753f127fSDimitry Andric 126753f127fSDimitry Andric Error HTTPServer::bind(unsigned ListenPort, const char *HostInterface) { 127753f127fSDimitry Andric if (!Server->bind_to_port(HostInterface, ListenPort)) 128753f127fSDimitry Andric return createStringError(errc::io_error, 129753f127fSDimitry Andric "Could not assign requested address."); 130753f127fSDimitry Andric Port = ListenPort; 131753f127fSDimitry Andric return Error::success(); 132753f127fSDimitry Andric } 133753f127fSDimitry Andric 134753f127fSDimitry Andric Expected<unsigned> HTTPServer::bind(const char *HostInterface) { 135753f127fSDimitry Andric int ListenPort = Server->bind_to_any_port(HostInterface); 136753f127fSDimitry Andric if (ListenPort < 0) 137753f127fSDimitry Andric return createStringError(errc::io_error, 138753f127fSDimitry Andric "Could not assign any port on requested address."); 139753f127fSDimitry Andric return Port = ListenPort; 140753f127fSDimitry Andric } 141753f127fSDimitry Andric 142753f127fSDimitry Andric Error HTTPServer::listen() { 143753f127fSDimitry Andric if (!Port) 144753f127fSDimitry Andric return createStringError(errc::io_error, 145753f127fSDimitry Andric "Cannot listen without first binding to a port."); 146753f127fSDimitry Andric if (!Server->listen_after_bind()) 147753f127fSDimitry Andric return createStringError( 148753f127fSDimitry Andric errc::io_error, 149753f127fSDimitry Andric "An unknown error occurred when cpp-httplib attempted to listen."); 150753f127fSDimitry Andric return Error::success(); 151753f127fSDimitry Andric } 152753f127fSDimitry Andric 153753f127fSDimitry Andric void HTTPServer::stop() { 154753f127fSDimitry Andric Server->stop(); 155753f127fSDimitry Andric Port = 0; 156753f127fSDimitry Andric } 157753f127fSDimitry Andric 158753f127fSDimitry Andric #else 159753f127fSDimitry Andric 160753f127fSDimitry Andric // TODO: Implement barebones standalone HTTP server implementation. 161753f127fSDimitry Andric bool HTTPServer::isAvailable() { return false; } 162753f127fSDimitry Andric 163753f127fSDimitry Andric HTTPServer::HTTPServer() = default; 164753f127fSDimitry Andric 165753f127fSDimitry Andric HTTPServer::~HTTPServer() = default; 166753f127fSDimitry Andric 167753f127fSDimitry Andric void HTTPServerRequest::setResponse(HTTPResponse Response) { 168*06c3fb27SDimitry Andric llvm_unreachable("no httplib"); 169753f127fSDimitry Andric } 170753f127fSDimitry Andric 171753f127fSDimitry Andric void HTTPServerRequest::setResponse(StreamingHTTPResponse Response) { 172*06c3fb27SDimitry Andric llvm_unreachable("no httplib"); 173753f127fSDimitry Andric } 174753f127fSDimitry Andric 175753f127fSDimitry Andric Error HTTPServer::get(StringRef UrlPathPattern, HTTPRequestHandler Handler) { 176*06c3fb27SDimitry Andric // TODO(https://github.com/llvm/llvm-project/issues/63873) We would ideally 177*06c3fb27SDimitry Andric // return an error as well but that's going to require refactoring of error 178*06c3fb27SDimitry Andric // handling in DebuginfodServer. 179*06c3fb27SDimitry Andric return Error::success(); 180753f127fSDimitry Andric } 181753f127fSDimitry Andric 182753f127fSDimitry Andric Error HTTPServer::bind(unsigned ListenPort, const char *HostInterface) { 183*06c3fb27SDimitry Andric return make_error<HTTPServerError>("no httplib"); 184753f127fSDimitry Andric } 185753f127fSDimitry Andric 186753f127fSDimitry Andric Expected<unsigned> HTTPServer::bind(const char *HostInterface) { 187*06c3fb27SDimitry Andric return make_error<HTTPServerError>("no httplib"); 188753f127fSDimitry Andric } 189753f127fSDimitry Andric 190753f127fSDimitry Andric Error HTTPServer::listen() { 191*06c3fb27SDimitry Andric return make_error<HTTPServerError>("no httplib"); 192753f127fSDimitry Andric } 193753f127fSDimitry Andric 194753f127fSDimitry Andric void HTTPServer::stop() { 195*06c3fb27SDimitry Andric llvm_unreachable("no httplib"); 196753f127fSDimitry Andric } 197753f127fSDimitry Andric 198753f127fSDimitry Andric #endif // LLVM_ENABLE_HTTPLIB 199