| //===-- 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 |