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