xref: /llvm-project/llvm/lib/Debuginfod/HTTPServer.cpp (revision 111fcb0df02db3db8bed1d5db6d911b7ce544d92)
18366e21eSNoah Shutty //===-- llvm/Debuginfod/HTTPServer.cpp - HTTP server library -----*- C++-*-===//
28366e21eSNoah Shutty //
38366e21eSNoah Shutty // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
48366e21eSNoah Shutty // See https://llvm.org/LICENSE.txt for license information.
58366e21eSNoah Shutty // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
68366e21eSNoah Shutty //
78366e21eSNoah Shutty //===----------------------------------------------------------------------===//
88366e21eSNoah Shutty ///
98366e21eSNoah Shutty /// \file
108366e21eSNoah Shutty ///
118366e21eSNoah Shutty /// This file defines the methods of the HTTPServer class and the streamFile
128366e21eSNoah Shutty /// function.
138366e21eSNoah Shutty ///
148366e21eSNoah Shutty //===----------------------------------------------------------------------===//
158366e21eSNoah Shutty 
168366e21eSNoah Shutty #include "llvm/Debuginfod/HTTPServer.h"
178366e21eSNoah Shutty #include "llvm/ADT/StringExtras.h"
188366e21eSNoah Shutty #include "llvm/ADT/StringRef.h"
198366e21eSNoah Shutty #include "llvm/Support/Errc.h"
208366e21eSNoah Shutty #include "llvm/Support/Error.h"
218366e21eSNoah Shutty #include "llvm/Support/FileSystem.h"
228366e21eSNoah Shutty #include "llvm/Support/MemoryBuffer.h"
238366e21eSNoah Shutty #include "llvm/Support/Regex.h"
248366e21eSNoah Shutty 
258366e21eSNoah Shutty #ifdef LLVM_ENABLE_HTTPLIB
268366e21eSNoah Shutty #include "httplib.h"
278366e21eSNoah Shutty #endif
288366e21eSNoah Shutty 
298366e21eSNoah Shutty using namespace llvm;
308366e21eSNoah Shutty 
3151857058SPetr Hosek char HTTPServerError::ID = 0;
3251857058SPetr Hosek 
HTTPServerError(const Twine & Msg)3351857058SPetr Hosek HTTPServerError::HTTPServerError(const Twine &Msg) : Msg(Msg.str()) {}
3451857058SPetr Hosek 
log(raw_ostream & OS) const3551857058SPetr Hosek void HTTPServerError::log(raw_ostream &OS) const { OS << Msg; }
3651857058SPetr Hosek 
streamFile(HTTPServerRequest & Request,StringRef FilePath)378366e21eSNoah Shutty bool llvm::streamFile(HTTPServerRequest &Request, StringRef FilePath) {
388366e21eSNoah Shutty   Expected<sys::fs::file_t> FDOrErr = sys::fs::openNativeFileForRead(FilePath);
398366e21eSNoah Shutty   if (Error Err = FDOrErr.takeError()) {
408366e21eSNoah Shutty     consumeError(std::move(Err));
418366e21eSNoah Shutty     Request.setResponse({404u, "text/plain", "Could not open file to read.\n"});
428366e21eSNoah Shutty     return false;
438366e21eSNoah Shutty   }
448366e21eSNoah Shutty   ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
458366e21eSNoah Shutty       MemoryBuffer::getOpenFile(*FDOrErr, FilePath,
468366e21eSNoah Shutty                                 /*FileSize=*/-1,
478366e21eSNoah Shutty                                 /*RequiresNullTerminator=*/false);
488366e21eSNoah Shutty   sys::fs::closeFile(*FDOrErr);
498366e21eSNoah Shutty   if (Error Err = errorCodeToError(MBOrErr.getError())) {
508366e21eSNoah Shutty     consumeError(std::move(Err));
518366e21eSNoah Shutty     Request.setResponse({404u, "text/plain", "Could not memory-map file.\n"});
528366e21eSNoah Shutty     return false;
538366e21eSNoah Shutty   }
54*111fcb0dSFangrui Song   // Lambdas are copied on conversion to std::function, preventing use of
558366e21eSNoah Shutty   // smart pointers.
568366e21eSNoah Shutty   MemoryBuffer *MB = MBOrErr->release();
578366e21eSNoah Shutty   Request.setResponse({200u, "application/octet-stream", MB->getBufferSize(),
588366e21eSNoah Shutty                        [=](size_t Offset, size_t Length) -> StringRef {
598366e21eSNoah Shutty                          return MB->getBuffer().substr(Offset, Length);
608366e21eSNoah Shutty                        },
618366e21eSNoah Shutty                        [=](bool Success) { delete MB; }});
628366e21eSNoah Shutty   return true;
638366e21eSNoah Shutty }
648366e21eSNoah Shutty 
658366e21eSNoah Shutty #ifdef LLVM_ENABLE_HTTPLIB
668366e21eSNoah Shutty 
isAvailable()678366e21eSNoah Shutty bool HTTPServer::isAvailable() { return true; }
688366e21eSNoah Shutty 
HTTPServer()698366e21eSNoah Shutty HTTPServer::HTTPServer() { Server = std::make_unique<httplib::Server>(); }
708366e21eSNoah Shutty 
~HTTPServer()718366e21eSNoah Shutty HTTPServer::~HTTPServer() { stop(); }
728366e21eSNoah Shutty 
expandUrlPathMatches(const std::smatch & Matches,HTTPServerRequest & Request)738366e21eSNoah Shutty static void expandUrlPathMatches(const std::smatch &Matches,
748366e21eSNoah Shutty                                  HTTPServerRequest &Request) {
758366e21eSNoah Shutty   bool UrlPathSet = false;
768366e21eSNoah Shutty   for (const auto &it : Matches) {
778366e21eSNoah Shutty     if (UrlPathSet)
788366e21eSNoah Shutty       Request.UrlPathMatches.push_back(it);
798366e21eSNoah Shutty     else {
808366e21eSNoah Shutty       Request.UrlPath = it;
818366e21eSNoah Shutty       UrlPathSet = true;
828366e21eSNoah Shutty     }
838366e21eSNoah Shutty   }
848366e21eSNoah Shutty }
858366e21eSNoah Shutty 
HTTPServerRequest(const httplib::Request & HTTPLibRequest,httplib::Response & HTTPLibResponse)868366e21eSNoah Shutty HTTPServerRequest::HTTPServerRequest(const httplib::Request &HTTPLibRequest,
878366e21eSNoah Shutty                                      httplib::Response &HTTPLibResponse)
888366e21eSNoah Shutty     : HTTPLibResponse(HTTPLibResponse) {
898366e21eSNoah Shutty   expandUrlPathMatches(HTTPLibRequest.matches, *this);
908366e21eSNoah Shutty }
918366e21eSNoah Shutty 
setResponse(HTTPResponse Response)928366e21eSNoah Shutty void HTTPServerRequest::setResponse(HTTPResponse Response) {
938366e21eSNoah Shutty   HTTPLibResponse.set_content(Response.Body.begin(), Response.Body.size(),
948366e21eSNoah Shutty                               Response.ContentType);
958366e21eSNoah Shutty   HTTPLibResponse.status = Response.Code;
968366e21eSNoah Shutty }
978366e21eSNoah Shutty 
setResponse(StreamingHTTPResponse Response)988366e21eSNoah Shutty void HTTPServerRequest::setResponse(StreamingHTTPResponse Response) {
998366e21eSNoah Shutty   HTTPLibResponse.set_content_provider(
1008366e21eSNoah Shutty       Response.ContentLength, Response.ContentType,
1018366e21eSNoah Shutty       [=](size_t Offset, size_t Length, httplib::DataSink &Sink) {
1028366e21eSNoah Shutty         if (Offset < Response.ContentLength) {
1038366e21eSNoah Shutty           StringRef Chunk = Response.Provider(Offset, Length);
1048366e21eSNoah Shutty           Sink.write(Chunk.begin(), Chunk.size());
1058366e21eSNoah Shutty         }
1068366e21eSNoah Shutty         return true;
1078366e21eSNoah Shutty       },
1088366e21eSNoah Shutty       [=](bool Success) { Response.CompletionHandler(Success); });
1098366e21eSNoah Shutty 
1108366e21eSNoah Shutty   HTTPLibResponse.status = Response.Code;
1118366e21eSNoah Shutty }
1128366e21eSNoah Shutty 
get(StringRef UrlPathPattern,HTTPRequestHandler Handler)1138366e21eSNoah Shutty Error HTTPServer::get(StringRef UrlPathPattern, HTTPRequestHandler Handler) {
1148366e21eSNoah Shutty   std::string ErrorMessage;
1158366e21eSNoah Shutty   if (!Regex(UrlPathPattern).isValid(ErrorMessage))
1168366e21eSNoah Shutty     return createStringError(errc::argument_out_of_domain, ErrorMessage);
1178366e21eSNoah Shutty   Server->Get(std::string(UrlPathPattern),
1188366e21eSNoah Shutty               [Handler](const httplib::Request &HTTPLibRequest,
1198366e21eSNoah Shutty                         httplib::Response &HTTPLibResponse) {
1208366e21eSNoah Shutty                 HTTPServerRequest Request(HTTPLibRequest, HTTPLibResponse);
1218366e21eSNoah Shutty                 Handler(Request);
1228366e21eSNoah Shutty               });
1238366e21eSNoah Shutty   return Error::success();
1248366e21eSNoah Shutty }
1258366e21eSNoah Shutty 
bind(unsigned ListenPort,const char * HostInterface)1268366e21eSNoah Shutty Error HTTPServer::bind(unsigned ListenPort, const char *HostInterface) {
1278366e21eSNoah Shutty   if (!Server->bind_to_port(HostInterface, ListenPort))
1288366e21eSNoah Shutty     return createStringError(errc::io_error,
1298366e21eSNoah Shutty                              "Could not assign requested address.");
1308366e21eSNoah Shutty   Port = ListenPort;
1318366e21eSNoah Shutty   return Error::success();
1328366e21eSNoah Shutty }
1338366e21eSNoah Shutty 
bind(const char * HostInterface)1348366e21eSNoah Shutty Expected<unsigned> HTTPServer::bind(const char *HostInterface) {
1358366e21eSNoah Shutty   int ListenPort = Server->bind_to_any_port(HostInterface);
1368366e21eSNoah Shutty   if (ListenPort < 0)
1378366e21eSNoah Shutty     return createStringError(errc::io_error,
1388366e21eSNoah Shutty                              "Could not assign any port on requested address.");
1398366e21eSNoah Shutty   return Port = ListenPort;
1408366e21eSNoah Shutty }
1418366e21eSNoah Shutty 
listen()1428366e21eSNoah Shutty Error HTTPServer::listen() {
1438366e21eSNoah Shutty   if (!Port)
1448366e21eSNoah Shutty     return createStringError(errc::io_error,
1458366e21eSNoah Shutty                              "Cannot listen without first binding to a port.");
1468366e21eSNoah Shutty   if (!Server->listen_after_bind())
1478366e21eSNoah Shutty     return createStringError(
1488366e21eSNoah Shutty         errc::io_error,
1498366e21eSNoah Shutty         "An unknown error occurred when cpp-httplib attempted to listen.");
1508366e21eSNoah Shutty   return Error::success();
1518366e21eSNoah Shutty }
1528366e21eSNoah Shutty 
stop()1538366e21eSNoah Shutty void HTTPServer::stop() {
1548366e21eSNoah Shutty   Server->stop();
1558366e21eSNoah Shutty   Port = 0;
1568366e21eSNoah Shutty }
1578366e21eSNoah Shutty 
1588366e21eSNoah Shutty #else
1598366e21eSNoah Shutty 
1608366e21eSNoah Shutty // TODO: Implement barebones standalone HTTP server implementation.
isAvailable()1618366e21eSNoah Shutty bool HTTPServer::isAvailable() { return false; }
1628366e21eSNoah Shutty 
1638366e21eSNoah Shutty HTTPServer::HTTPServer() = default;
1648366e21eSNoah Shutty 
1658366e21eSNoah Shutty HTTPServer::~HTTPServer() = default;
1668366e21eSNoah Shutty 
setResponse(HTTPResponse Response)1678366e21eSNoah Shutty void HTTPServerRequest::setResponse(HTTPResponse Response) {
16851857058SPetr Hosek   llvm_unreachable("no httplib");
1698366e21eSNoah Shutty }
1708366e21eSNoah Shutty 
setResponse(StreamingHTTPResponse Response)1718366e21eSNoah Shutty void HTTPServerRequest::setResponse(StreamingHTTPResponse Response) {
17251857058SPetr Hosek   llvm_unreachable("no httplib");
1738366e21eSNoah Shutty }
1748366e21eSNoah Shutty 
get(StringRef UrlPathPattern,HTTPRequestHandler Handler)1758366e21eSNoah Shutty Error HTTPServer::get(StringRef UrlPathPattern, HTTPRequestHandler Handler) {
17651857058SPetr Hosek   // TODO(https://github.com/llvm/llvm-project/issues/63873) We would ideally
17751857058SPetr Hosek   // return an error as well but that's going to require refactoring of error
17851857058SPetr Hosek   // handling in DebuginfodServer.
17951857058SPetr Hosek   return Error::success();
1808366e21eSNoah Shutty }
1818366e21eSNoah Shutty 
bind(unsigned ListenPort,const char * HostInterface)1828366e21eSNoah Shutty Error HTTPServer::bind(unsigned ListenPort, const char *HostInterface) {
18351857058SPetr Hosek   return make_error<HTTPServerError>("no httplib");
1848366e21eSNoah Shutty }
1858366e21eSNoah Shutty 
bind(const char * HostInterface)1868366e21eSNoah Shutty Expected<unsigned> HTTPServer::bind(const char *HostInterface) {
18751857058SPetr Hosek   return make_error<HTTPServerError>("no httplib");
1888366e21eSNoah Shutty }
1898366e21eSNoah Shutty 
listen()1908366e21eSNoah Shutty Error HTTPServer::listen() {
19151857058SPetr Hosek   return make_error<HTTPServerError>("no httplib");
1928366e21eSNoah Shutty }
1938366e21eSNoah Shutty 
stop()1948366e21eSNoah Shutty void HTTPServer::stop() {
19551857058SPetr Hosek   llvm_unreachable("no httplib");
1968366e21eSNoah Shutty }
1978366e21eSNoah Shutty 
1988366e21eSNoah Shutty #endif // LLVM_ENABLE_HTTPLIB
199