199 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10///11/// This file defines the methods of the HTTPServer class and the streamFile12/// 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_HTTPLIB26#include "httplib.h"27#endif28 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 of55 // 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_HTTPLIB66 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#else159 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 ideally177 // return an error as well but that's going to require refactoring of error178 // 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_HTTPLIB199