| //===-- llvm/Debuginfod/HTTPServer.cpp - HTTP server library -----*- C++-*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// | |
| /// This file defines the methods of the HTTPServer class and the streamFile | |
| /// function. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/Debuginfod/HTTPServer.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/ADT/StringRef.h" | |
| #include "llvm/Support/Errc.h" | |
| #include "llvm/Support/Error.h" | |
| #include "llvm/Support/FileSystem.h" | |
| #include "llvm/Support/MemoryBuffer.h" | |
| #include "llvm/Support/Regex.h" | |
| #ifdef LLVM_ENABLE_HTTPLIB | |
| #include "httplib.h" | |
| #endif | |
| using namespace llvm; | |
| bool llvm::streamFile(HTTPServerRequest &Request, StringRef FilePath) { | |
| Expected<sys::fs::file_t> FDOrErr = sys::fs::openNativeFileForRead(FilePath); | |
| if (Error Err = FDOrErr.takeError()) { | |
| consumeError(std::move(Err)); | |
| Request.setResponse({404u, "text/plain", "Could not open file to read.\n"}); | |
| return false; | |
| } | |
| ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = | |
| MemoryBuffer::getOpenFile(*FDOrErr, FilePath, | |
| /*FileSize=*/-1, | |
| /*RequiresNullTerminator=*/false); | |
| sys::fs::closeFile(*FDOrErr); | |
| if (Error Err = errorCodeToError(MBOrErr.getError())) { | |
| consumeError(std::move(Err)); | |
| Request.setResponse({404u, "text/plain", "Could not memory-map file.\n"}); | |
| return false; | |
| } | |
| // Lambdas are copied on conversion to to std::function, preventing use of | |
| // smart pointers. | |
| MemoryBuffer *MB = MBOrErr->release(); | |
| Request.setResponse({200u, "application/octet-stream", MB->getBufferSize(), | |
| [=](size_t Offset, size_t Length) -> StringRef { | |
| return MB->getBuffer().substr(Offset, Length); | |
| }, | |
| [=](bool Success) { delete MB; }}); | |
| return true; | |
| } | |
| #ifdef LLVM_ENABLE_HTTPLIB | |
| bool HTTPServer::isAvailable() { return true; } | |
| HTTPServer::HTTPServer() { Server = std::make_unique<httplib::Server>(); } | |
| HTTPServer::~HTTPServer() { stop(); } | |
| static void expandUrlPathMatches(const std::smatch &Matches, | |
| HTTPServerRequest &Request) { | |
| bool UrlPathSet = false; | |
| for (const auto &it : Matches) { | |
| if (UrlPathSet) | |
| Request.UrlPathMatches.push_back(it); | |
| else { | |
| Request.UrlPath = it; | |
| UrlPathSet = true; | |
| } | |
| } | |
| } | |
| HTTPServerRequest::HTTPServerRequest(const httplib::Request &HTTPLibRequest, | |
| httplib::Response &HTTPLibResponse) | |
| : HTTPLibResponse(HTTPLibResponse) { | |
| expandUrlPathMatches(HTTPLibRequest.matches, *this); | |
| } | |
| void HTTPServerRequest::setResponse(HTTPResponse Response) { | |
| HTTPLibResponse.set_content(Response.Body.begin(), Response.Body.size(), | |
| Response.ContentType); | |
| HTTPLibResponse.status = Response.Code; | |
| } | |
| void HTTPServerRequest::setResponse(StreamingHTTPResponse Response) { | |
| HTTPLibResponse.set_content_provider( | |
| Response.ContentLength, Response.ContentType, | |
| [=](size_t Offset, size_t Length, httplib::DataSink &Sink) { | |
| if (Offset < Response.ContentLength) { | |
| StringRef Chunk = Response.Provider(Offset, Length); | |
| Sink.write(Chunk.begin(), Chunk.size()); | |
| } | |
| return true; | |
| }, | |
| [=](bool Success) { Response.CompletionHandler(Success); }); | |
| HTTPLibResponse.status = Response.Code; | |
| } | |
| Error HTTPServer::get(StringRef UrlPathPattern, HTTPRequestHandler Handler) { | |
| std::string ErrorMessage; | |
| if (!Regex(UrlPathPattern).isValid(ErrorMessage)) | |
| return createStringError(errc::argument_out_of_domain, ErrorMessage); | |
| Server->Get(std::string(UrlPathPattern), | |
| [Handler](const httplib::Request &HTTPLibRequest, | |
| httplib::Response &HTTPLibResponse) { | |
| HTTPServerRequest Request(HTTPLibRequest, HTTPLibResponse); | |
| Handler(Request); | |
| }); | |
| return Error::success(); | |
| } | |
| Error HTTPServer::bind(unsigned ListenPort, const char *HostInterface) { | |
| if (!Server->bind_to_port(HostInterface, ListenPort)) | |
| return createStringError(errc::io_error, | |
| "Could not assign requested address."); | |
| Port = ListenPort; | |
| return Error::success(); | |
| } | |
| Expected<unsigned> HTTPServer::bind(const char *HostInterface) { | |
| int ListenPort = Server->bind_to_any_port(HostInterface); | |
| if (ListenPort < 0) | |
| return createStringError(errc::io_error, | |
| "Could not assign any port on requested address."); | |
| return Port = ListenPort; | |
| } | |
| Error HTTPServer::listen() { | |
| if (!Port) | |
| return createStringError(errc::io_error, | |
| "Cannot listen without first binding to a port."); | |
| if (!Server->listen_after_bind()) | |
| return createStringError( | |
| errc::io_error, | |
| "An unknown error occurred when cpp-httplib attempted to listen."); | |
| return Error::success(); | |
| } | |
| void HTTPServer::stop() { | |
| Server->stop(); | |
| Port = 0; | |
| } | |
| #else | |
| // TODO: Implement barebones standalone HTTP server implementation. | |
| bool HTTPServer::isAvailable() { return false; } | |
| HTTPServer::HTTPServer() = default; | |
| HTTPServer::~HTTPServer() = default; | |
| void HTTPServerRequest::setResponse(HTTPResponse Response) { | |
| llvm_unreachable("No HTTP server implementation available"); | |
| } | |
| void HTTPServerRequest::setResponse(StreamingHTTPResponse Response) { | |
| llvm_unreachable("No HTTP server implementation available"); | |
| } | |
| Error HTTPServer::get(StringRef UrlPathPattern, HTTPRequestHandler Handler) { | |
| llvm_unreachable("No HTTP server implementation available"); | |
| } | |
| Error HTTPServer::bind(unsigned ListenPort, const char *HostInterface) { | |
| llvm_unreachable("No HTTP server implementation available"); | |
| } | |
| Expected<unsigned> HTTPServer::bind(const char *HostInterface) { | |
| llvm_unreachable("No HTTP server implementation available"); | |
| } | |
| Error HTTPServer::listen() { | |
| llvm_unreachable("No HTTP server implementation available"); | |
| } | |
| void HTTPServer::stop() { | |
| llvm_unreachable("No HTTP server implementation available"); | |
| } | |
| #endif // LLVM_ENABLE_HTTPLIB |