forked from OSchip/llvm-project
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
|