1 //===-- llvm/unittest/Support/HTTPServer.cpp - unit tests -------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
20 #ifdef LLVM_ENABLE_HTTPLIB
22 TEST(HTTPServer
, IsAvailable
) { EXPECT_TRUE(HTTPServer::isAvailable()); }
24 HTTPResponse Response
= {200u, "text/plain", "hello, world\n"};
25 std::string UrlPathPattern
= R
"(/(.*))";
26 std::string InvalidUrlPathPattern
= R
"(/(.*)";
28 HTTPRequestHandler Handler
= [](HTTPServerRequest
&Request
) {
29 Request
.setResponse(Response
);
32 HTTPRequestHandler DelayHandler
= [](HTTPServerRequest
&Request
) {
33 std::this_thread::sleep_for(std::chrono::milliseconds(50));
34 Request
.setResponse(Response
);
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
);
44 TEST(HTTPServer
, InvalidUrlPath
) {
45 // test that we can bind to any address
47 EXPECT_THAT_ERROR(Server
.get(InvalidUrlPathPattern
, Handler
),
48 Failed
<StringError
>());
49 EXPECT_THAT_EXPECTED(Server
.bind(), Succeeded());
52 TEST(HTTPServer
, bind
) {
53 // test that we can bind to any address
55 EXPECT_THAT_ERROR(Server
.get(UrlPathPattern
, Handler
), Succeeded());
56 EXPECT_THAT_EXPECTED(Server
.bind(), Succeeded());
59 TEST(HTTPServer
, ListenBeforeBind
) {
60 // test that we can bind to any address
62 EXPECT_THAT_ERROR(Server
.get(UrlPathPattern
, Handler
), Succeeded());
63 EXPECT_THAT_ERROR(Server
.listen(), Failed
<StringError
>());
66 #ifdef LLVM_ENABLE_CURL
67 // Test the client and server against each other.
69 // Test fixture to initialize and teardown the HTTP client for each
71 class HTTPClientServerTest
: public ::testing::Test
{
73 void SetUp() override
{ HTTPClient::initialize(); }
74 void TearDown() override
{ HTTPClient::cleanup(); }
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.
83 Error
handleBodyChunk(StringRef BodyChunk
) override
{
84 ResponseBody
= ResponseBody
+ BodyChunk
.str();
85 return Error::success();
89 TEST_F(HTTPClientServerTest
, Hello
) {
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
;
101 EXPECT_THAT_ERROR(Client
.perform(Request
, Handler
), Succeeded());
102 EXPECT_EQ(Handler
.ResponseBody
, Response
.Body
);
103 EXPECT_EQ(Client
.responseCode(), Response
.Code
);
107 TEST_F(HTTPClientServerTest
, LambdaHandlerHello
) {
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
);
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
;
125 EXPECT_THAT_ERROR(Client
.perform(Request
, Handler
), Succeeded());
126 EXPECT_EQ(Handler
.ResponseBody
, LambdaResponse
.Body
);
127 EXPECT_EQ(Client
.responseCode(), LambdaResponse
.Code
);
131 // Test the streaming response.
132 TEST_F(HTTPClientServerTest
, StreamingHello
) {
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
;
144 EXPECT_THAT_ERROR(Client
.perform(Request
, Handler
), Succeeded());
145 EXPECT_EQ(Handler
.ResponseBody
, Response
.Body
);
146 EXPECT_EQ(Client
.responseCode(), Response
.Code
);
150 // Writes a temporary file and streams it back using streamFile.
151 HTTPRequestHandler TempFileStreamingHandler
= [](HTTPServerRequest Request
) {
153 SmallString
<64> TempFilePath
;
154 sys::fs::createTemporaryFile("http-stream-file-test", "temp", FD
,
156 raw_fd_ostream
OS(FD
, true, /*unbuffered=*/true);
159 streamFile(Request
, TempFilePath
);
162 // Test streaming back chunks of a file.
163 TEST_F(HTTPClientServerTest
, StreamingFileResponse
) {
165 EXPECT_THAT_ERROR(Server
.get(UrlPathPattern
, TempFileStreamingHandler
),
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
;
176 EXPECT_THAT_ERROR(Client
.perform(Request
, Handler
), Succeeded());
177 EXPECT_EQ(Handler
.ResponseBody
, Response
.Body
);
178 EXPECT_EQ(Client
.responseCode(), Response
.Code
);
182 // Deletes the temporary file before streaming it back, should give a 404 not
183 // found status code.
184 HTTPRequestHandler MissingTempFileStreamingHandler
=
185 [](HTTPServerRequest Request
) {
187 SmallString
<64> TempFilePath
;
188 sys::fs::createTemporaryFile("http-stream-file-test", "temp", FD
,
190 raw_fd_ostream
OS(FD
, true, /*unbuffered=*/true);
194 sys::fs::remove(TempFilePath
);
195 streamFile(Request
, TempFilePath
);
198 // Streaming a missing file should give a 404.
199 TEST_F(HTTPClientServerTest
, StreamingMissingFileResponse
) {
201 EXPECT_THAT_ERROR(Server
.get(UrlPathPattern
, MissingTempFileStreamingHandler
),
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
;
212 EXPECT_THAT_ERROR(Client
.perform(Request
, Handler
), Succeeded());
213 EXPECT_EQ(Client
.responseCode(), 404u);
217 TEST_F(HTTPClientServerTest
, ClientTimeout
) {
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
);
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
>());
235 // Check that Url paths are dispatched to the first matching handler and provide
236 // the correct path pattern match components.
237 TEST_F(HTTPClientServerTest
, PathMatching
) {
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
});
249 EXPECT_THAT_ERROR(Server
.get(UrlPathPattern
,
250 [&](HTTPServerRequest
&Request
) {
252 "Should not reach this handler");
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
;
266 EXPECT_THAT_ERROR(Client
.perform(Request
, Handler
), Succeeded());
267 EXPECT_EQ(Handler
.ResponseBody
, "/abc/1/2");
268 EXPECT_EQ(Client
.responseCode(), 200u);
272 TEST_F(HTTPClientServerTest
, FirstPathMatched
) {
276 Server
.get(UrlPathPattern
,
277 [&](HTTPServerRequest Request
) { Handler(Request
); }),
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
});
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
;
298 EXPECT_THAT_ERROR(Client
.perform(Request
, Handler
), Succeeded());
299 EXPECT_EQ(Handler
.ResponseBody
, Response
.Body
);
300 EXPECT_EQ(Client
.responseCode(), Response
.Code
);
308 TEST(HTTPServer
, IsAvailable
) { EXPECT_FALSE(HTTPServer::isAvailable()); }
310 #endif // LLVM_ENABLE_HTTPLIB