190 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			190 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- 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
 |