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