311 lines · cpp
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-exception6//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 18using namespace llvm;19 20#ifdef LLVM_ENABLE_HTTPLIB21 22TEST(HTTPServer, IsAvailable) { EXPECT_TRUE(HTTPServer::isAvailable()); }23 24HTTPResponse Response = {200u, "text/plain", "hello, world\n"};25std::string UrlPathPattern = R"(/(.*))";26std::string InvalidUrlPathPattern = R"(/(.*)";27 28HTTPRequestHandler Handler = [](HTTPServerRequest &Request) {29 Request.setResponse(Response);30};31 32HTTPRequestHandler DelayHandler = [](HTTPServerRequest &Request) {33 std::this_thread::sleep_for(std::chrono::milliseconds(50));34 Request.setResponse(Response);35};36 37HTTPRequestHandler 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 44TEST(HTTPServer, InvalidUrlPath) {45 // test that we can bind to any address46 HTTPServer Server;47 EXPECT_THAT_ERROR(Server.get(InvalidUrlPathPattern, Handler),48 Failed<StringError>());49 EXPECT_THAT_EXPECTED(Server.bind(), Succeeded());50}51 52TEST(HTTPServer, bind) {53 // test that we can bind to any address54 HTTPServer Server;55 EXPECT_THAT_ERROR(Server.get(UrlPathPattern, Handler), Succeeded());56 EXPECT_THAT_EXPECTED(Server.bind(), Succeeded());57}58 59TEST(HTTPServer, ListenBeforeBind) {60 // test that we can bind to any address61 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_CURL67// Test the client and server against each other.68 69// Test fixture to initialize and teardown the HTTP client for each70// client-server test71class HTTPClientServerTest : public ::testing::Test {72protected:73 void SetUp() override { HTTPClient::initialize(); }74 void TearDown() override { HTTPClient::cleanup(); }75};76 77/// A simple handler which writes returned data to a string.78struct StringHTTPResponseHandler final : public HTTPResponseHandler {79 std::string ResponseBody = "";80 /// These callbacks store the body and status code in an HTTPResponseBuffer81 /// allocated based on Content-Length. The Content-Length header must be82 /// handled by handleHeaderLine before any calls to handleBodyChunk.83 Error handleBodyChunk(StringRef BodyChunk) override {84 ResponseBody = ResponseBody + BodyChunk.str();85 return Error::success();86 }87};88 89TEST_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 107TEST_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.132TEST_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.151HTTPRequestHandler 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.163TEST_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 not183// found status code.184HTTPRequestHandler MissingTempFileStreamingHandler =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 file194 sys::fs::remove(TempFilePath);195 streamFile(Request, TempFilePath);196 };197 198// Streaming a missing file should give a 404.199TEST_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 217TEST_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 fail228 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 provide236// the correct path pattern match components.237TEST_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 272TEST_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#endif305 306#else307 308TEST(HTTPServer, IsAvailable) { EXPECT_FALSE(HTTPServer::isAvailable()); }309 310#endif // LLVM_ENABLE_HTTPLIB311