blob: 2ea923d5a734c27ae376790f54edb9dd11a361c8 [file] [log] [blame]
//===-- 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