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