xref: /openbsd-src/gnu/llvm/llvm/lib/Debuginfod/HTTPServer.cpp (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
1*d415bd75Srobert //===-- llvm/Debuginfod/HTTPServer.cpp - HTTP server library -----*- C++-*-===//
2*d415bd75Srobert //
3*d415bd75Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*d415bd75Srobert // See https://llvm.org/LICENSE.txt for license information.
5*d415bd75Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*d415bd75Srobert //
7*d415bd75Srobert //===----------------------------------------------------------------------===//
8*d415bd75Srobert ///
9*d415bd75Srobert /// \file
10*d415bd75Srobert ///
11*d415bd75Srobert /// This file defines the methods of the HTTPServer class and the streamFile
12*d415bd75Srobert /// function.
13*d415bd75Srobert ///
14*d415bd75Srobert //===----------------------------------------------------------------------===//
15*d415bd75Srobert 
16*d415bd75Srobert #include "llvm/Debuginfod/HTTPServer.h"
17*d415bd75Srobert #include "llvm/ADT/StringExtras.h"
18*d415bd75Srobert #include "llvm/ADT/StringRef.h"
19*d415bd75Srobert #include "llvm/Support/Errc.h"
20*d415bd75Srobert #include "llvm/Support/Error.h"
21*d415bd75Srobert #include "llvm/Support/FileSystem.h"
22*d415bd75Srobert #include "llvm/Support/MemoryBuffer.h"
23*d415bd75Srobert #include "llvm/Support/Regex.h"
24*d415bd75Srobert 
25*d415bd75Srobert #ifdef LLVM_ENABLE_HTTPLIB
26*d415bd75Srobert #include "httplib.h"
27*d415bd75Srobert #endif
28*d415bd75Srobert 
29*d415bd75Srobert using namespace llvm;
30*d415bd75Srobert 
streamFile(HTTPServerRequest & Request,StringRef FilePath)31*d415bd75Srobert bool llvm::streamFile(HTTPServerRequest &Request, StringRef FilePath) {
32*d415bd75Srobert   Expected<sys::fs::file_t> FDOrErr = sys::fs::openNativeFileForRead(FilePath);
33*d415bd75Srobert   if (Error Err = FDOrErr.takeError()) {
34*d415bd75Srobert     consumeError(std::move(Err));
35*d415bd75Srobert     Request.setResponse({404u, "text/plain", "Could not open file to read.\n"});
36*d415bd75Srobert     return false;
37*d415bd75Srobert   }
38*d415bd75Srobert   ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
39*d415bd75Srobert       MemoryBuffer::getOpenFile(*FDOrErr, FilePath,
40*d415bd75Srobert                                 /*FileSize=*/-1,
41*d415bd75Srobert                                 /*RequiresNullTerminator=*/false);
42*d415bd75Srobert   sys::fs::closeFile(*FDOrErr);
43*d415bd75Srobert   if (Error Err = errorCodeToError(MBOrErr.getError())) {
44*d415bd75Srobert     consumeError(std::move(Err));
45*d415bd75Srobert     Request.setResponse({404u, "text/plain", "Could not memory-map file.\n"});
46*d415bd75Srobert     return false;
47*d415bd75Srobert   }
48*d415bd75Srobert   // Lambdas are copied on conversion to to std::function, preventing use of
49*d415bd75Srobert   // smart pointers.
50*d415bd75Srobert   MemoryBuffer *MB = MBOrErr->release();
51*d415bd75Srobert   Request.setResponse({200u, "application/octet-stream", MB->getBufferSize(),
52*d415bd75Srobert                        [=](size_t Offset, size_t Length) -> StringRef {
53*d415bd75Srobert                          return MB->getBuffer().substr(Offset, Length);
54*d415bd75Srobert                        },
55*d415bd75Srobert                        [=](bool Success) { delete MB; }});
56*d415bd75Srobert   return true;
57*d415bd75Srobert }
58*d415bd75Srobert 
59*d415bd75Srobert #ifdef LLVM_ENABLE_HTTPLIB
60*d415bd75Srobert 
isAvailable()61*d415bd75Srobert bool HTTPServer::isAvailable() { return true; }
62*d415bd75Srobert 
HTTPServer()63*d415bd75Srobert HTTPServer::HTTPServer() { Server = std::make_unique<httplib::Server>(); }
64*d415bd75Srobert 
~HTTPServer()65*d415bd75Srobert HTTPServer::~HTTPServer() { stop(); }
66*d415bd75Srobert 
expandUrlPathMatches(const std::smatch & Matches,HTTPServerRequest & Request)67*d415bd75Srobert static void expandUrlPathMatches(const std::smatch &Matches,
68*d415bd75Srobert                                  HTTPServerRequest &Request) {
69*d415bd75Srobert   bool UrlPathSet = false;
70*d415bd75Srobert   for (const auto &it : Matches) {
71*d415bd75Srobert     if (UrlPathSet)
72*d415bd75Srobert       Request.UrlPathMatches.push_back(it);
73*d415bd75Srobert     else {
74*d415bd75Srobert       Request.UrlPath = it;
75*d415bd75Srobert       UrlPathSet = true;
76*d415bd75Srobert     }
77*d415bd75Srobert   }
78*d415bd75Srobert }
79*d415bd75Srobert 
HTTPServerRequest(const httplib::Request & HTTPLibRequest,httplib::Response & HTTPLibResponse)80*d415bd75Srobert HTTPServerRequest::HTTPServerRequest(const httplib::Request &HTTPLibRequest,
81*d415bd75Srobert                                      httplib::Response &HTTPLibResponse)
82*d415bd75Srobert     : HTTPLibResponse(HTTPLibResponse) {
83*d415bd75Srobert   expandUrlPathMatches(HTTPLibRequest.matches, *this);
84*d415bd75Srobert }
85*d415bd75Srobert 
setResponse(HTTPResponse Response)86*d415bd75Srobert void HTTPServerRequest::setResponse(HTTPResponse Response) {
87*d415bd75Srobert   HTTPLibResponse.set_content(Response.Body.begin(), Response.Body.size(),
88*d415bd75Srobert                               Response.ContentType);
89*d415bd75Srobert   HTTPLibResponse.status = Response.Code;
90*d415bd75Srobert }
91*d415bd75Srobert 
setResponse(StreamingHTTPResponse Response)92*d415bd75Srobert void HTTPServerRequest::setResponse(StreamingHTTPResponse Response) {
93*d415bd75Srobert   HTTPLibResponse.set_content_provider(
94*d415bd75Srobert       Response.ContentLength, Response.ContentType,
95*d415bd75Srobert       [=](size_t Offset, size_t Length, httplib::DataSink &Sink) {
96*d415bd75Srobert         if (Offset < Response.ContentLength) {
97*d415bd75Srobert           StringRef Chunk = Response.Provider(Offset, Length);
98*d415bd75Srobert           Sink.write(Chunk.begin(), Chunk.size());
99*d415bd75Srobert         }
100*d415bd75Srobert         return true;
101*d415bd75Srobert       },
102*d415bd75Srobert       [=](bool Success) { Response.CompletionHandler(Success); });
103*d415bd75Srobert 
104*d415bd75Srobert   HTTPLibResponse.status = Response.Code;
105*d415bd75Srobert }
106*d415bd75Srobert 
get(StringRef UrlPathPattern,HTTPRequestHandler Handler)107*d415bd75Srobert Error HTTPServer::get(StringRef UrlPathPattern, HTTPRequestHandler Handler) {
108*d415bd75Srobert   std::string ErrorMessage;
109*d415bd75Srobert   if (!Regex(UrlPathPattern).isValid(ErrorMessage))
110*d415bd75Srobert     return createStringError(errc::argument_out_of_domain, ErrorMessage);
111*d415bd75Srobert   Server->Get(std::string(UrlPathPattern),
112*d415bd75Srobert               [Handler](const httplib::Request &HTTPLibRequest,
113*d415bd75Srobert                         httplib::Response &HTTPLibResponse) {
114*d415bd75Srobert                 HTTPServerRequest Request(HTTPLibRequest, HTTPLibResponse);
115*d415bd75Srobert                 Handler(Request);
116*d415bd75Srobert               });
117*d415bd75Srobert   return Error::success();
118*d415bd75Srobert }
119*d415bd75Srobert 
bind(unsigned ListenPort,const char * HostInterface)120*d415bd75Srobert Error HTTPServer::bind(unsigned ListenPort, const char *HostInterface) {
121*d415bd75Srobert   if (!Server->bind_to_port(HostInterface, ListenPort))
122*d415bd75Srobert     return createStringError(errc::io_error,
123*d415bd75Srobert                              "Could not assign requested address.");
124*d415bd75Srobert   Port = ListenPort;
125*d415bd75Srobert   return Error::success();
126*d415bd75Srobert }
127*d415bd75Srobert 
bind(const char * HostInterface)128*d415bd75Srobert Expected<unsigned> HTTPServer::bind(const char *HostInterface) {
129*d415bd75Srobert   int ListenPort = Server->bind_to_any_port(HostInterface);
130*d415bd75Srobert   if (ListenPort < 0)
131*d415bd75Srobert     return createStringError(errc::io_error,
132*d415bd75Srobert                              "Could not assign any port on requested address.");
133*d415bd75Srobert   return Port = ListenPort;
134*d415bd75Srobert }
135*d415bd75Srobert 
listen()136*d415bd75Srobert Error HTTPServer::listen() {
137*d415bd75Srobert   if (!Port)
138*d415bd75Srobert     return createStringError(errc::io_error,
139*d415bd75Srobert                              "Cannot listen without first binding to a port.");
140*d415bd75Srobert   if (!Server->listen_after_bind())
141*d415bd75Srobert     return createStringError(
142*d415bd75Srobert         errc::io_error,
143*d415bd75Srobert         "An unknown error occurred when cpp-httplib attempted to listen.");
144*d415bd75Srobert   return Error::success();
145*d415bd75Srobert }
146*d415bd75Srobert 
stop()147*d415bd75Srobert void HTTPServer::stop() {
148*d415bd75Srobert   Server->stop();
149*d415bd75Srobert   Port = 0;
150*d415bd75Srobert }
151*d415bd75Srobert 
152*d415bd75Srobert #else
153*d415bd75Srobert 
154*d415bd75Srobert // TODO: Implement barebones standalone HTTP server implementation.
isAvailable()155*d415bd75Srobert bool HTTPServer::isAvailable() { return false; }
156*d415bd75Srobert 
157*d415bd75Srobert HTTPServer::HTTPServer() = default;
158*d415bd75Srobert 
159*d415bd75Srobert HTTPServer::~HTTPServer() = default;
160*d415bd75Srobert 
setResponse(HTTPResponse Response)161*d415bd75Srobert void HTTPServerRequest::setResponse(HTTPResponse Response) {
162*d415bd75Srobert   llvm_unreachable("No HTTP server implementation available");
163*d415bd75Srobert }
164*d415bd75Srobert 
setResponse(StreamingHTTPResponse Response)165*d415bd75Srobert void HTTPServerRequest::setResponse(StreamingHTTPResponse Response) {
166*d415bd75Srobert   llvm_unreachable("No HTTP server implementation available");
167*d415bd75Srobert }
168*d415bd75Srobert 
get(StringRef UrlPathPattern,HTTPRequestHandler Handler)169*d415bd75Srobert Error HTTPServer::get(StringRef UrlPathPattern, HTTPRequestHandler Handler) {
170*d415bd75Srobert   llvm_unreachable("No HTTP server implementation available");
171*d415bd75Srobert }
172*d415bd75Srobert 
bind(unsigned ListenPort,const char * HostInterface)173*d415bd75Srobert Error HTTPServer::bind(unsigned ListenPort, const char *HostInterface) {
174*d415bd75Srobert   llvm_unreachable("No HTTP server implementation available");
175*d415bd75Srobert }
176*d415bd75Srobert 
bind(const char * HostInterface)177*d415bd75Srobert Expected<unsigned> HTTPServer::bind(const char *HostInterface) {
178*d415bd75Srobert   llvm_unreachable("No HTTP server implementation available");
179*d415bd75Srobert }
180*d415bd75Srobert 
listen()181*d415bd75Srobert Error HTTPServer::listen() {
182*d415bd75Srobert   llvm_unreachable("No HTTP server implementation available");
183*d415bd75Srobert }
184*d415bd75Srobert 
stop()185*d415bd75Srobert void HTTPServer::stop() {
186*d415bd75Srobert   llvm_unreachable("No HTTP server implementation available");
187*d415bd75Srobert }
188*d415bd75Srobert 
189*d415bd75Srobert #endif // LLVM_ENABLE_HTTPLIB
190