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