1 //===-- llvm/unittest/Support/HTTPServer.cpp - unit tests -------*- 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 #include "llvm/Debuginfod/HTTPClient.h"
10 #include "llvm/Debuginfod/HTTPServer.h"
11 #include "llvm/Support/Error.h"
12 #include "llvm/Support/ThreadPool.h"
13 #include "llvm/Testing/Support/Error.h"
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
16 
17 using namespace llvm;
18 
19 #ifdef LLVM_ENABLE_HTTPLIB
20 
TEST(HTTPServer,IsAvailable)21 TEST(HTTPServer, IsAvailable) { EXPECT_TRUE(HTTPServer::isAvailable()); }
22 
23 HTTPResponse Response = {200u, "text/plain", "hello, world\n"};
24 std::string UrlPathPattern = R"(/(.*))";
25 std::string InvalidUrlPathPattern = R"(/(.*)";
26 
__anon6196692c0102(HTTPServerRequest &Request) 27 HTTPRequestHandler Handler = [](HTTPServerRequest &Request) {
28   Request.setResponse(Response);
29 };
30 
__anon6196692c0202(HTTPServerRequest &Request) 31 HTTPRequestHandler DelayHandler = [](HTTPServerRequest &Request) {
32   std::this_thread::sleep_for(std::chrono::milliseconds(50));
33   Request.setResponse(Response);
34 };
35 
__anon6196692c0302(HTTPServerRequest &Request) 36 HTTPRequestHandler StreamingHandler = [](HTTPServerRequest &Request) {
37   Request.setResponse({200, "text/plain", Response.Body.size(),
38                        [=](size_t Offset, size_t Length) -> StringRef {
39                          return Response.Body.substr(Offset, Length);
40                        }});
41 };
42 
TEST(HTTPServer,InvalidUrlPath)43 TEST(HTTPServer, InvalidUrlPath) {
44   // test that we can bind to any address
45   HTTPServer Server;
46   EXPECT_THAT_ERROR(Server.get(InvalidUrlPathPattern, Handler),
47                     Failed<StringError>());
48   EXPECT_THAT_EXPECTED(Server.bind(), Succeeded());
49 }
50 
TEST(HTTPServer,bind)51 TEST(HTTPServer, bind) {
52   // test that we can bind to any address
53   HTTPServer Server;
54   EXPECT_THAT_ERROR(Server.get(UrlPathPattern, Handler), Succeeded());
55   EXPECT_THAT_EXPECTED(Server.bind(), Succeeded());
56 }
57 
TEST(HTTPServer,ListenBeforeBind)58 TEST(HTTPServer, ListenBeforeBind) {
59   // test that we can bind to any address
60   HTTPServer Server;
61   EXPECT_THAT_ERROR(Server.get(UrlPathPattern, Handler), Succeeded());
62   EXPECT_THAT_ERROR(Server.listen(), Failed<StringError>());
63 }
64 
65 #ifdef LLVM_ENABLE_CURL
66 // Test the client and server against each other.
67 
68 // Test fixture to initialize and teardown the HTTP client for each
69 // client-server test
70 class HTTPClientServerTest : public ::testing::Test {
71 protected:
SetUp()72   void SetUp() override { HTTPClient::initialize(); }
TearDown()73   void TearDown() override { HTTPClient::cleanup(); }
74 };
75 
76 /// A simple handler which writes returned data to a string.
77 struct StringHTTPResponseHandler final : public HTTPResponseHandler {
78   std::string ResponseBody = "";
79   /// These callbacks store the body and status code in an HTTPResponseBuffer
80   /// allocated based on Content-Length. The Content-Length header must be
81   /// handled by handleHeaderLine before any calls to handleBodyChunk.
handleBodyChunkStringHTTPResponseHandler82   Error handleBodyChunk(StringRef BodyChunk) override {
83     ResponseBody = ResponseBody + BodyChunk.str();
84     return Error::success();
85   }
86 };
87 
TEST_F(HTTPClientServerTest,Hello)88 TEST_F(HTTPClientServerTest, Hello) {
89   HTTPServer Server;
90   EXPECT_THAT_ERROR(Server.get(UrlPathPattern, Handler), Succeeded());
91   Expected<unsigned> PortOrErr = Server.bind();
92   EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());
93   unsigned Port = *PortOrErr;
94   ThreadPool Pool(hardware_concurrency(1));
95   Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });
96   std::string Url = "http://localhost:" + utostr(Port);
97   HTTPRequest Request(Url);
98   StringHTTPResponseHandler Handler;
99   HTTPClient Client;
100   EXPECT_THAT_ERROR(Client.perform(Request, Handler), Succeeded());
101   EXPECT_EQ(Handler.ResponseBody, Response.Body);
102   EXPECT_EQ(Client.responseCode(), Response.Code);
103   Server.stop();
104 }
105 
TEST_F(HTTPClientServerTest,LambdaHandlerHello)106 TEST_F(HTTPClientServerTest, LambdaHandlerHello) {
107   HTTPServer Server;
108   HTTPResponse LambdaResponse = {200u, "text/plain",
109                                  "hello, world from a lambda\n"};
110   EXPECT_THAT_ERROR(Server.get(UrlPathPattern,
111                                [LambdaResponse](HTTPServerRequest &Request) {
112                                  Request.setResponse(LambdaResponse);
113                                }),
114                     Succeeded());
115   Expected<unsigned> PortOrErr = Server.bind();
116   EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());
117   unsigned Port = *PortOrErr;
118   ThreadPool Pool(hardware_concurrency(1));
119   Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });
120   std::string Url = "http://localhost:" + utostr(Port);
121   HTTPRequest Request(Url);
122   StringHTTPResponseHandler Handler;
123   HTTPClient Client;
124   EXPECT_THAT_ERROR(Client.perform(Request, Handler), Succeeded());
125   EXPECT_EQ(Handler.ResponseBody, LambdaResponse.Body);
126   EXPECT_EQ(Client.responseCode(), LambdaResponse.Code);
127   Server.stop();
128 }
129 
130 // Test the streaming response.
TEST_F(HTTPClientServerTest,StreamingHello)131 TEST_F(HTTPClientServerTest, StreamingHello) {
132   HTTPServer Server;
133   EXPECT_THAT_ERROR(Server.get(UrlPathPattern, StreamingHandler), Succeeded());
134   Expected<unsigned> PortOrErr = Server.bind();
135   EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());
136   unsigned Port = *PortOrErr;
137   ThreadPool Pool(hardware_concurrency(1));
138   Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });
139   std::string Url = "http://localhost:" + utostr(Port);
140   HTTPRequest Request(Url);
141   StringHTTPResponseHandler Handler;
142   HTTPClient Client;
143   EXPECT_THAT_ERROR(Client.perform(Request, Handler), Succeeded());
144   EXPECT_EQ(Handler.ResponseBody, Response.Body);
145   EXPECT_EQ(Client.responseCode(), Response.Code);
146   Server.stop();
147 }
148 
149 // Writes a temporary file and streams it back using streamFile.
__anon6196692c0902(HTTPServerRequest Request) 150 HTTPRequestHandler TempFileStreamingHandler = [](HTTPServerRequest Request) {
151   int FD;
152   SmallString<64> TempFilePath;
153   sys::fs::createTemporaryFile("http-stream-file-test", "temp", FD,
154                                TempFilePath);
155   raw_fd_ostream OS(FD, true, /*unbuffered=*/true);
156   OS << Response.Body;
157   OS.close();
158   streamFile(Request, TempFilePath);
159 };
160 
161 // Test streaming back chunks of a file.
TEST_F(HTTPClientServerTest,StreamingFileResponse)162 TEST_F(HTTPClientServerTest, StreamingFileResponse) {
163   HTTPServer Server;
164   EXPECT_THAT_ERROR(Server.get(UrlPathPattern, TempFileStreamingHandler),
165                     Succeeded());
166   Expected<unsigned> PortOrErr = Server.bind();
167   EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());
168   unsigned Port = *PortOrErr;
169   ThreadPool Pool(hardware_concurrency(1));
170   Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });
171   std::string Url = "http://localhost:" + utostr(Port);
172   HTTPRequest Request(Url);
173   StringHTTPResponseHandler Handler;
174   HTTPClient Client;
175   EXPECT_THAT_ERROR(Client.perform(Request, Handler), Succeeded());
176   EXPECT_EQ(Handler.ResponseBody, Response.Body);
177   EXPECT_EQ(Client.responseCode(), Response.Code);
178   Server.stop();
179 }
180 
181 // Deletes the temporary file before streaming it back, should give a 404 not
182 // found status code.
183 HTTPRequestHandler MissingTempFileStreamingHandler =
__anon6196692c0b02(HTTPServerRequest Request) 184     [](HTTPServerRequest Request) {
185       int FD;
186       SmallString<64> TempFilePath;
187       sys::fs::createTemporaryFile("http-stream-file-test", "temp", FD,
188                                    TempFilePath);
189       raw_fd_ostream OS(FD, true, /*unbuffered=*/true);
190       OS << Response.Body;
191       OS.close();
192       // delete the file
193       sys::fs::remove(TempFilePath);
194       streamFile(Request, TempFilePath);
195     };
196 
197 // Streaming a missing file should give a 404.
TEST_F(HTTPClientServerTest,StreamingMissingFileResponse)198 TEST_F(HTTPClientServerTest, StreamingMissingFileResponse) {
199   HTTPServer Server;
200   EXPECT_THAT_ERROR(Server.get(UrlPathPattern, MissingTempFileStreamingHandler),
201                     Succeeded());
202   Expected<unsigned> PortOrErr = Server.bind();
203   EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());
204   unsigned Port = *PortOrErr;
205   ThreadPool Pool(hardware_concurrency(1));
206   Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });
207   std::string Url = "http://localhost:" + utostr(Port);
208   HTTPRequest Request(Url);
209   StringHTTPResponseHandler Handler;
210   HTTPClient Client;
211   EXPECT_THAT_ERROR(Client.perform(Request, Handler), Succeeded());
212   EXPECT_EQ(Client.responseCode(), 404u);
213   Server.stop();
214 }
215 
TEST_F(HTTPClientServerTest,ClientTimeout)216 TEST_F(HTTPClientServerTest, ClientTimeout) {
217   HTTPServer Server;
218   EXPECT_THAT_ERROR(Server.get(UrlPathPattern, DelayHandler), Succeeded());
219   Expected<unsigned> PortOrErr = Server.bind();
220   EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());
221   unsigned Port = *PortOrErr;
222   ThreadPool Pool(hardware_concurrency(1));
223   Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });
224   std::string Url = "http://localhost:" + utostr(Port);
225   HTTPClient Client;
226   // Timeout below 50ms, request should fail
227   Client.setTimeout(std::chrono::milliseconds(40));
228   HTTPRequest Request(Url);
229   StringHTTPResponseHandler Handler;
230   EXPECT_THAT_ERROR(Client.perform(Request, Handler), Failed<StringError>());
231   Server.stop();
232 }
233 
234 // Check that Url paths are dispatched to the first matching handler and provide
235 // the correct path pattern match components.
TEST_F(HTTPClientServerTest,PathMatching)236 TEST_F(HTTPClientServerTest, PathMatching) {
237   HTTPServer Server;
238 
239   EXPECT_THAT_ERROR(
240       Server.get(R"(/abc/(.*)/(.*))",
241                  [&](HTTPServerRequest &Request) {
242                    EXPECT_EQ(Request.UrlPath, "/abc/1/2");
243                    ASSERT_THAT(Request.UrlPathMatches,
244                                testing::ElementsAre("1", "2"));
245                    Request.setResponse({200u, "text/plain", Request.UrlPath});
246                  }),
247       Succeeded());
248   EXPECT_THAT_ERROR(Server.get(UrlPathPattern,
249                                [&](HTTPServerRequest &Request) {
250                                  llvm_unreachable(
251                                      "Should not reach this handler");
252                                  Handler(Request);
253                                }),
254                     Succeeded());
255 
256   Expected<unsigned> PortOrErr = Server.bind();
257   EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());
258   unsigned Port = *PortOrErr;
259   ThreadPool Pool(hardware_concurrency(1));
260   Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });
261   std::string Url = "http://localhost:" + utostr(Port) + "/abc/1/2";
262   HTTPRequest Request(Url);
263   StringHTTPResponseHandler Handler;
264   HTTPClient Client;
265   EXPECT_THAT_ERROR(Client.perform(Request, Handler), Succeeded());
266   EXPECT_EQ(Handler.ResponseBody, "/abc/1/2");
267   EXPECT_EQ(Client.responseCode(), 200u);
268   Server.stop();
269 }
270 
TEST_F(HTTPClientServerTest,FirstPathMatched)271 TEST_F(HTTPClientServerTest, FirstPathMatched) {
272   HTTPServer Server;
273 
274   EXPECT_THAT_ERROR(
275       Server.get(UrlPathPattern,
276                  [&](HTTPServerRequest Request) { Handler(Request); }),
277       Succeeded());
278 
279   EXPECT_THAT_ERROR(
280       Server.get(R"(/abc/(.*)/(.*))",
281                  [&](HTTPServerRequest Request) {
282                    EXPECT_EQ(Request.UrlPathMatches.size(), 2u);
283                    llvm_unreachable("Should not reach this handler");
284                    Request.setResponse({200u, "text/plain", Request.UrlPath});
285                  }),
286       Succeeded());
287 
288   Expected<unsigned> PortOrErr = Server.bind();
289   EXPECT_THAT_EXPECTED(PortOrErr, Succeeded());
290   unsigned Port = *PortOrErr;
291   ThreadPool Pool(hardware_concurrency(1));
292   Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); });
293   std::string Url = "http://localhost:" + utostr(Port) + "/abc/1/2";
294   HTTPRequest Request(Url);
295   StringHTTPResponseHandler Handler;
296   HTTPClient Client;
297   EXPECT_THAT_ERROR(Client.perform(Request, Handler), Succeeded());
298   EXPECT_EQ(Handler.ResponseBody, Response.Body);
299   EXPECT_EQ(Client.responseCode(), Response.Code);
300   Server.stop();
301 }
302 
303 #endif
304 
305 #else
306 
TEST(HTTPServer,IsAvailable)307 TEST(HTTPServer, IsAvailable) { EXPECT_FALSE(HTTPServer::isAvailable()); }
308 
309 #endif // LLVM_ENABLE_HTTPLIB
310