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