Index: llvm/include/llvm/Support/HTTPServer.h =================================================================== --- /dev/null +++ llvm/include/llvm/Support/HTTPServer.h @@ -0,0 +1,49 @@ +#ifndef LLVM_SUPPORT_HTTP_SERVER_H +#define LLVM_SUPPORT_HTTP_SERVER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +#ifdef LLVM_ENABLE_CPP_HTTPLIB +#include "httplib.h" +#endif + +namespace llvm { + +struct HTTPResponse { + unsigned Code; + std::string ContentType; + std::string Body; +}; + +typedef std::function HTTPRequestHandler; + +typedef std::function + HTTPContentProvider; + +struct StreamingHTTPResponse { + unsigned StatusCode; + std::string ContentType; + HTTPContentProvider Provider; +}; + +typedef std::function + StreamingHTTPRequestHandler; + +class HTTPServer { +#ifdef LLVM_ENABLE_CPP_HTTPLIB + httplib::Server Server; + unsigned Port = 0; +#endif +public: + static bool isAvailable(); + HTTPServer(HTTPRequestHandler Handler); + HTTPServer(StreamingHTTPRequestHandler Handler); + Error bind(unsigned Port, StringRef HostInterface = "0.0.0.0"); + Expected bindAny(StringRef HostInterface = "0.0.0.0"); + Error listen(); + void stop(); +}; +} // end namespace llvm + +#endif // LLVM_SUPPORT_HTTP_SERVER_H Index: llvm/lib/Support/CMakeLists.txt =================================================================== --- llvm/lib/Support/CMakeLists.txt +++ llvm/lib/Support/CMakeLists.txt @@ -161,6 +161,7 @@ GraphWriter.cpp Hashing.cpp HTTPClient.cpp + HTTPServer.cpp InitLLVM.cpp InstructionCost.cpp IntEqClasses.cpp Index: llvm/lib/Support/HTTPServer.cpp =================================================================== --- /dev/null +++ llvm/lib/Support/HTTPServer.cpp @@ -0,0 +1,72 @@ +#include "llvm/Support/HTTPServer.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" + +using namespace llvm; + +#ifdef LLVM_ENABLE_CPP_HTTPLIB + +bool HTTPServer::isAvailable() { return true; } + +HTTPServer::HTTPServer( + std::function StreamingRequestHandler) { + llvm_unreachable("unimplemented"); +} + +HTTPServer::HTTPServer(std::function RequestHandler) { + Server.Get(R"(/(.*))", + [&](const httplib::Request &Request, httplib::Response &Response) { + SmallString<64> UrlPath; + UrlPath = std::string(Request.matches[1]); + HTTPResponse Resp = RequestHandler(UrlPath); + Response.set_content(Resp.Body.data(), Resp.ContentType.data()); + return Response.status = Resp.Code; + }); +} + +Error HTTPServer::bind(unsigned ListenPort, StringRef HostInterface) { + SmallString<16> HostInterfaceStorage; + StringRef S = + Twine(HostInterface).toNullTerminatedStringRef(HostInterfaceStorage); + + if (!Server.bind_to_port(S.begin(), ListenPort)) + return createStringError(errc::io_error, + "Could not assign requested address."); + + Port = ListenPort; + return Error::success(); +} + +Expected HTTPServer::bindAny(StringRef HostInterface) { + SmallString<16> HostInterfaceStorage; + StringRef S = + Twine(HostInterface).toNullTerminatedStringRef(HostInterfaceStorage); + + int ListenPort = Server.bind_to_any_port(S.begin()); + if (ListenPort < 0) + return createStringError(errc::io_error, + "Could not assign any port on requested address."); + return Port = ListenPort; +} + +Error HTTPServer::listen() { + if (Port) + Server.listen_after_bind(); + return Error::success(); +} + +void HTTPServer::stop() { Server.stop(); } + +#else + +// TODO: Implement barebones standalone HTTP server implementation. +bool HTTPServer::isAvailable() { return false; } + +HTTPServer::HTTPServer( + std::function StreamingRequestHandler) { + llvm_unreachable( + "Attempt to instantiate HTTPServer when no implementation is available."); +} +#endif Index: llvm/unittests/Support/CMakeLists.txt =================================================================== --- llvm/unittests/Support/CMakeLists.txt +++ llvm/unittests/Support/CMakeLists.txt @@ -42,6 +42,7 @@ HashBuilderTest.cpp Host.cpp HTTPClient.cpp + HTTPServer.cpp IndexedAccessorTest.cpp InstructionCostTest.cpp ItaniumManglingCanonicalizerTest.cpp Index: llvm/unittests/Support/HTTPServer.cpp =================================================================== --- /dev/null +++ llvm/unittests/Support/HTTPServer.cpp @@ -0,0 +1,55 @@ +//===-- llvm/unittest/Support/HTTPClient.cpp - unit tests -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/HTTPServer.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/HTTPClient.h" +#include "llvm/Support/ThreadPool.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; + +#ifdef LLVM_ENABLE_CPP_HTTPLIB + +TEST(HTTPServer, isAvailable) { EXPECT_TRUE(HTTPServer::isAvailable()); } + +HTTPResponse SimpleResponse = {200u, "text/plain", "hello, world\n"}; +HTTPRequestHandler SimpleHandler = [](StringRef UrlPath) -> HTTPResponse { + return SimpleResponse; +}; + +TEST(HTTPServer, bindAny) { + // test that we can bind to any address + HTTPServer Server(SimpleHandler); + EXPECT_THAT_EXPECTED(Server.bindAny(), Succeeded()); +} + +#ifdef LLVM_ENABLE_CURL + +// Test the client and server against each other. +TEST(HTTPClientServer, HTTPClientServerHello) { + HTTPServer Server(SimpleHandler); + Expected PortOrErr = Server.bindAny(); + EXPECT_THAT_EXPECTED(PortOrErr, Succeeded()); + unsigned &Port = *PortOrErr; + ThreadPool Pool(hardware_concurrency(1)); + Pool.async([&]() { EXPECT_THAT_ERROR(Server.listen(), Succeeded()); }); + std::string Url = "http://localhost:" + utostr(Port); + HTTPClient::initialize(); + Expected BufferOrErr = HTTPClient().get(Url); + EXPECT_THAT_EXPECTED(BufferOrErr, Succeeded()); + HTTPResponseBuffer &Buffer = *BufferOrErr; + EXPECT_EQ(Buffer.Code, SimpleResponse.Code); + EXPECT_EQ(Buffer.Body->MemoryBuffer::getBuffer(), SimpleResponse.Body); + Server.stop(); +} + +#endif + +#endif