1753f127fSDimitry Andric //===-- llvm/Debuginfod/HTTPServer.cpp - HTTP server library -----*- C++-*-===//
2753f127fSDimitry Andric //
3753f127fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4753f127fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5753f127fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6753f127fSDimitry Andric //
7753f127fSDimitry Andric //===----------------------------------------------------------------------===//
8753f127fSDimitry Andric ///
9753f127fSDimitry Andric /// \file
10753f127fSDimitry Andric ///
11753f127fSDimitry Andric /// This file defines the methods of the HTTPServer class and the streamFile
12753f127fSDimitry Andric /// function.
13753f127fSDimitry Andric ///
14753f127fSDimitry Andric //===----------------------------------------------------------------------===//
15753f127fSDimitry Andric 
16753f127fSDimitry Andric #include "llvm/Debuginfod/HTTPServer.h"
17753f127fSDimitry Andric #include "llvm/ADT/StringExtras.h"
18753f127fSDimitry Andric #include "llvm/ADT/StringRef.h"
19753f127fSDimitry Andric #include "llvm/Support/Errc.h"
20753f127fSDimitry Andric #include "llvm/Support/Error.h"
21753f127fSDimitry Andric #include "llvm/Support/FileSystem.h"
22753f127fSDimitry Andric #include "llvm/Support/MemoryBuffer.h"
23753f127fSDimitry Andric #include "llvm/Support/Regex.h"
24753f127fSDimitry Andric 
25753f127fSDimitry Andric #ifdef LLVM_ENABLE_HTTPLIB
26753f127fSDimitry Andric #include "httplib.h"
27753f127fSDimitry Andric #endif
28753f127fSDimitry Andric 
29753f127fSDimitry Andric using namespace llvm;
30753f127fSDimitry Andric 
3106c3fb27SDimitry Andric char HTTPServerError::ID = 0;
3206c3fb27SDimitry Andric 
HTTPServerError(const Twine & Msg)3306c3fb27SDimitry Andric HTTPServerError::HTTPServerError(const Twine &Msg) : Msg(Msg.str()) {}
3406c3fb27SDimitry Andric 
log(raw_ostream & OS) const3506c3fb27SDimitry Andric void HTTPServerError::log(raw_ostream &OS) const { OS << Msg; }
3606c3fb27SDimitry Andric 
streamFile(HTTPServerRequest & Request,StringRef FilePath)37753f127fSDimitry Andric bool llvm::streamFile(HTTPServerRequest &Request, StringRef FilePath) {
38753f127fSDimitry Andric   Expected<sys::fs::file_t> FDOrErr = sys::fs::openNativeFileForRead(FilePath);
39753f127fSDimitry Andric   if (Error Err = FDOrErr.takeError()) {
40753f127fSDimitry Andric     consumeError(std::move(Err));
41753f127fSDimitry Andric     Request.setResponse({404u, "text/plain", "Could not open file to read.\n"});
42753f127fSDimitry Andric     return false;
43753f127fSDimitry Andric   }
44753f127fSDimitry Andric   ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
45753f127fSDimitry Andric       MemoryBuffer::getOpenFile(*FDOrErr, FilePath,
46753f127fSDimitry Andric                                 /*FileSize=*/-1,
47753f127fSDimitry Andric                                 /*RequiresNullTerminator=*/false);
48753f127fSDimitry Andric   sys::fs::closeFile(*FDOrErr);
49753f127fSDimitry Andric   if (Error Err = errorCodeToError(MBOrErr.getError())) {
50753f127fSDimitry Andric     consumeError(std::move(Err));
51753f127fSDimitry Andric     Request.setResponse({404u, "text/plain", "Could not memory-map file.\n"});
52753f127fSDimitry Andric     return false;
53753f127fSDimitry Andric   }
54*5f757f3fSDimitry Andric   // Lambdas are copied on conversion to std::function, preventing use of
55753f127fSDimitry Andric   // smart pointers.
56753f127fSDimitry Andric   MemoryBuffer *MB = MBOrErr->release();
57753f127fSDimitry Andric   Request.setResponse({200u, "application/octet-stream", MB->getBufferSize(),
58753f127fSDimitry Andric                        [=](size_t Offset, size_t Length) -> StringRef {
59753f127fSDimitry Andric                          return MB->getBuffer().substr(Offset, Length);
60753f127fSDimitry Andric                        },
61753f127fSDimitry Andric                        [=](bool Success) { delete MB; }});
62753f127fSDimitry Andric   return true;
63753f127fSDimitry Andric }
64753f127fSDimitry Andric 
65753f127fSDimitry Andric #ifdef LLVM_ENABLE_HTTPLIB
66753f127fSDimitry Andric 
isAvailable()67753f127fSDimitry Andric bool HTTPServer::isAvailable() { return true; }
68753f127fSDimitry Andric 
HTTPServer()69753f127fSDimitry Andric HTTPServer::HTTPServer() { Server = std::make_unique<httplib::Server>(); }
70753f127fSDimitry Andric 
~HTTPServer()71753f127fSDimitry Andric HTTPServer::~HTTPServer() { stop(); }
72753f127fSDimitry Andric 
expandUrlPathMatches(const std::smatch & Matches,HTTPServerRequest & Request)73753f127fSDimitry Andric static void expandUrlPathMatches(const std::smatch &Matches,
74753f127fSDimitry Andric                                  HTTPServerRequest &Request) {
75753f127fSDimitry Andric   bool UrlPathSet = false;
76753f127fSDimitry Andric   for (const auto &it : Matches) {
77753f127fSDimitry Andric     if (UrlPathSet)
78753f127fSDimitry Andric       Request.UrlPathMatches.push_back(it);
79753f127fSDimitry Andric     else {
80753f127fSDimitry Andric       Request.UrlPath = it;
81753f127fSDimitry Andric       UrlPathSet = true;
82753f127fSDimitry Andric     }
83753f127fSDimitry Andric   }
84753f127fSDimitry Andric }
85753f127fSDimitry Andric 
HTTPServerRequest(const httplib::Request & HTTPLibRequest,httplib::Response & HTTPLibResponse)86753f127fSDimitry Andric HTTPServerRequest::HTTPServerRequest(const httplib::Request &HTTPLibRequest,
87753f127fSDimitry Andric                                      httplib::Response &HTTPLibResponse)
88753f127fSDimitry Andric     : HTTPLibResponse(HTTPLibResponse) {
89753f127fSDimitry Andric   expandUrlPathMatches(HTTPLibRequest.matches, *this);
90753f127fSDimitry Andric }
91753f127fSDimitry Andric 
setResponse(HTTPResponse Response)92753f127fSDimitry Andric void HTTPServerRequest::setResponse(HTTPResponse Response) {
93753f127fSDimitry Andric   HTTPLibResponse.set_content(Response.Body.begin(), Response.Body.size(),
94753f127fSDimitry Andric                               Response.ContentType);
95753f127fSDimitry Andric   HTTPLibResponse.status = Response.Code;
96753f127fSDimitry Andric }
97753f127fSDimitry Andric 
setResponse(StreamingHTTPResponse Response)98753f127fSDimitry Andric void HTTPServerRequest::setResponse(StreamingHTTPResponse Response) {
99753f127fSDimitry Andric   HTTPLibResponse.set_content_provider(
100753f127fSDimitry Andric       Response.ContentLength, Response.ContentType,
101753f127fSDimitry Andric       [=](size_t Offset, size_t Length, httplib::DataSink &Sink) {
102753f127fSDimitry Andric         if (Offset < Response.ContentLength) {
103753f127fSDimitry Andric           StringRef Chunk = Response.Provider(Offset, Length);
104753f127fSDimitry Andric           Sink.write(Chunk.begin(), Chunk.size());
105753f127fSDimitry Andric         }
106753f127fSDimitry Andric         return true;
107753f127fSDimitry Andric       },
108753f127fSDimitry Andric       [=](bool Success) { Response.CompletionHandler(Success); });
109753f127fSDimitry Andric 
110753f127fSDimitry Andric   HTTPLibResponse.status = Response.Code;
111753f127fSDimitry Andric }
112753f127fSDimitry Andric 
get(StringRef UrlPathPattern,HTTPRequestHandler Handler)113753f127fSDimitry Andric Error HTTPServer::get(StringRef UrlPathPattern, HTTPRequestHandler Handler) {
114753f127fSDimitry Andric   std::string ErrorMessage;
115753f127fSDimitry Andric   if (!Regex(UrlPathPattern).isValid(ErrorMessage))
116753f127fSDimitry Andric     return createStringError(errc::argument_out_of_domain, ErrorMessage);
117753f127fSDimitry Andric   Server->Get(std::string(UrlPathPattern),
118753f127fSDimitry Andric               [Handler](const httplib::Request &HTTPLibRequest,
119753f127fSDimitry Andric                         httplib::Response &HTTPLibResponse) {
120753f127fSDimitry Andric                 HTTPServerRequest Request(HTTPLibRequest, HTTPLibResponse);
121753f127fSDimitry Andric                 Handler(Request);
122753f127fSDimitry Andric               });
123753f127fSDimitry Andric   return Error::success();
124753f127fSDimitry Andric }
125753f127fSDimitry Andric 
bind(unsigned ListenPort,const char * HostInterface)126753f127fSDimitry Andric Error HTTPServer::bind(unsigned ListenPort, const char *HostInterface) {
127753f127fSDimitry Andric   if (!Server->bind_to_port(HostInterface, ListenPort))
128753f127fSDimitry Andric     return createStringError(errc::io_error,
129753f127fSDimitry Andric                              "Could not assign requested address.");
130753f127fSDimitry Andric   Port = ListenPort;
131753f127fSDimitry Andric   return Error::success();
132753f127fSDimitry Andric }
133753f127fSDimitry Andric 
bind(const char * HostInterface)134753f127fSDimitry Andric Expected<unsigned> HTTPServer::bind(const char *HostInterface) {
135753f127fSDimitry Andric   int ListenPort = Server->bind_to_any_port(HostInterface);
136753f127fSDimitry Andric   if (ListenPort < 0)
137753f127fSDimitry Andric     return createStringError(errc::io_error,
138753f127fSDimitry Andric                              "Could not assign any port on requested address.");
139753f127fSDimitry Andric   return Port = ListenPort;
140753f127fSDimitry Andric }
141753f127fSDimitry Andric 
listen()142753f127fSDimitry Andric Error HTTPServer::listen() {
143753f127fSDimitry Andric   if (!Port)
144753f127fSDimitry Andric     return createStringError(errc::io_error,
145753f127fSDimitry Andric                              "Cannot listen without first binding to a port.");
146753f127fSDimitry Andric   if (!Server->listen_after_bind())
147753f127fSDimitry Andric     return createStringError(
148753f127fSDimitry Andric         errc::io_error,
149753f127fSDimitry Andric         "An unknown error occurred when cpp-httplib attempted to listen.");
150753f127fSDimitry Andric   return Error::success();
151753f127fSDimitry Andric }
152753f127fSDimitry Andric 
stop()153753f127fSDimitry Andric void HTTPServer::stop() {
154753f127fSDimitry Andric   Server->stop();
155753f127fSDimitry Andric   Port = 0;
156753f127fSDimitry Andric }
157753f127fSDimitry Andric 
158753f127fSDimitry Andric #else
159753f127fSDimitry Andric 
160753f127fSDimitry Andric // TODO: Implement barebones standalone HTTP server implementation.
isAvailable()161753f127fSDimitry Andric bool HTTPServer::isAvailable() { return false; }
162753f127fSDimitry Andric 
163753f127fSDimitry Andric HTTPServer::HTTPServer() = default;
164753f127fSDimitry Andric 
165753f127fSDimitry Andric HTTPServer::~HTTPServer() = default;
166753f127fSDimitry Andric 
setResponse(HTTPResponse Response)167753f127fSDimitry Andric void HTTPServerRequest::setResponse(HTTPResponse Response) {
16806c3fb27SDimitry Andric   llvm_unreachable("no httplib");
169753f127fSDimitry Andric }
170753f127fSDimitry Andric 
setResponse(StreamingHTTPResponse Response)171753f127fSDimitry Andric void HTTPServerRequest::setResponse(StreamingHTTPResponse Response) {
17206c3fb27SDimitry Andric   llvm_unreachable("no httplib");
173753f127fSDimitry Andric }
174753f127fSDimitry Andric 
get(StringRef UrlPathPattern,HTTPRequestHandler Handler)175753f127fSDimitry Andric Error HTTPServer::get(StringRef UrlPathPattern, HTTPRequestHandler Handler) {
17606c3fb27SDimitry Andric   // TODO(https://github.com/llvm/llvm-project/issues/63873) We would ideally
17706c3fb27SDimitry Andric   // return an error as well but that's going to require refactoring of error
17806c3fb27SDimitry Andric   // handling in DebuginfodServer.
17906c3fb27SDimitry Andric   return Error::success();
180753f127fSDimitry Andric }
181753f127fSDimitry Andric 
bind(unsigned ListenPort,const char * HostInterface)182753f127fSDimitry Andric Error HTTPServer::bind(unsigned ListenPort, const char *HostInterface) {
18306c3fb27SDimitry Andric   return make_error<HTTPServerError>("no httplib");
184753f127fSDimitry Andric }
185753f127fSDimitry Andric 
bind(const char * HostInterface)186753f127fSDimitry Andric Expected<unsigned> HTTPServer::bind(const char *HostInterface) {
18706c3fb27SDimitry Andric   return make_error<HTTPServerError>("no httplib");
188753f127fSDimitry Andric }
189753f127fSDimitry Andric 
listen()190753f127fSDimitry Andric Error HTTPServer::listen() {
19106c3fb27SDimitry Andric   return make_error<HTTPServerError>("no httplib");
192753f127fSDimitry Andric }
193753f127fSDimitry Andric 
stop()194753f127fSDimitry Andric void HTTPServer::stop() {
19506c3fb27SDimitry Andric   llvm_unreachable("no httplib");
196753f127fSDimitry Andric }
197753f127fSDimitry Andric 
198753f127fSDimitry Andric #endif // LLVM_ENABLE_HTTPLIB
199