diff --git a/src/common/http/test/CMakeLists.txt b/src/common/http/test/CMakeLists.txt index 06142e7da26..b65e0cb4d12 100644 --- a/src/common/http/test/CMakeLists.txt +++ b/src/common/http/test/CMakeLists.txt @@ -10,18 +10,10 @@ nebula_add_test( OBJECTS $ $ - $ - $ - $ - $ - $ - $ - $ - $ - $ + $ LIBRARIES ${PROXYGEN_LIBRARIES} gtest curl - + gtest_main ) diff --git a/src/common/http/test/HttpClientTest.cpp b/src/common/http/test/HttpClientTest.cpp index b671de7ec9f..5264ce24aa3 100644 --- a/src/common/http/test/HttpClientTest.cpp +++ b/src/common/http/test/HttpClientTest.cpp @@ -1,95 +1,54 @@ -/* Copyright (c) 2019 vesoft inc. All rights reserved. +/* Copyright (c) 2022 vesoft inc. All rights reserved. * * This source code is licensed under Apache 2.0 License. */ -#include -#include -#include - #include "common/http/HttpClient.h" -#include "webservice/Common.h" -#include "webservice/Router.h" -#include "webservice/WebService.h" +#include "gtest/gtest.h" +#include "mock/FakeHttpServer.h" namespace nebula { -namespace http { - -class HttpClientHandler : public proxygen::RequestHandler { - public: - HttpClientHandler() = default; - - void onRequest(std::unique_ptr) noexcept override {} - - void onBody(std::unique_ptr) noexcept override {} - - void onEOM() noexcept override { - proxygen::ResponseBuilder(downstream_) - .status(WebServiceUtils::to(HttpStatusCode::OK), - WebServiceUtils::toString(HttpStatusCode::OK)) - .body("HttpClientHandler successfully") - .sendWithEOM(); - } - - void onUpgrade(proxygen::UpgradeProtocol) noexcept override {} - - void requestComplete() noexcept override { - delete this; - } - void onError(proxygen::ProxygenError error) noexcept override { - LOG(ERROR) << "HttpClientHandler Error: " << proxygen::getErrorString(error); - } -}; -class HttpClientTestEnv : public ::testing::Environment { - public: - void SetUp() override { - FLAGS_ws_ip = "127.0.0.1"; - FLAGS_ws_http_port = 0; - LOG(INFO) << "Starting web service..."; - webSvc_ = std::make_unique(); +class HTTPClientTest : public ::testing::Test {}; - auto& router = webSvc_->router(); - router.get("/path").handler([](auto&&) { return new HttpClientHandler(); }); - - auto status = webSvc_->start(); - ASSERT_TRUE(status.ok()) << status; - } +TEST_F(HTTPClientTest, GET) { + FakeHttpServer server(3659); + server.start(); + auto resp = HttpClient::get("http://localhost:3659"); + ASSERT_EQ(resp.curlCode, 0) << resp.curlMessage; + ASSERT_EQ(resp.body, "GET"); + server.stop(); + server.join(); +} - void TearDown() override { - webSvc_.reset(); - VLOG(1) << "Web service stopped"; - } +TEST_F(HTTPClientTest, POST) { + FakeHttpServer server(3660); + server.start(); + auto resp = HttpClient::post("http://localhost:3660", {}, ""); + ASSERT_EQ(resp.curlCode, 0) << resp.curlMessage; + ASSERT_EQ(resp.body, "POST"); + server.stop(); + server.join(); +} - private: - std::unique_ptr webSvc_; -}; +TEST_F(HTTPClientTest, DELETE) { + FakeHttpServer server(3661); + server.start(); + auto resp = HttpClient::delete_("http://localhost:3661", {}); + ASSERT_EQ(resp.curlCode, 0) << resp.curlMessage; + ASSERT_EQ(resp.body, "DELETE"); + server.stop(); + server.join(); +} -TEST(HttpClient, get) { - { - auto url = - folly::stringPrintf("http://%s:%d%s", FLAGS_ws_ip.c_str(), FLAGS_ws_http_port, "/path"); - auto httpResp = HttpClient::get(url); - ASSERT_EQ(httpResp.curlCode, 0); - ASSERT_EQ("HttpClientHandler successfully", httpResp.body); - } - { - auto url = folly::stringPrintf( - "http://%s:%d%s", FLAGS_ws_ip.c_str(), FLAGS_ws_http_port, "/not_exist"); - auto httpResp = HttpClient::get(url); - ASSERT_EQ(httpResp.curlCode, 0); - ASSERT_TRUE(httpResp.body.empty()); - } +TEST_F(HTTPClientTest, PUT) { + FakeHttpServer server(3662); + server.start(); + auto resp = HttpClient::put("http://localhost:3662", {}, ""); + ASSERT_EQ(resp.curlCode, 0) << resp.curlMessage; + ASSERT_EQ(resp.body, "PUT"); + server.stop(); + server.join(); } -} // namespace http } // namespace nebula - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - folly::init(&argc, &argv, true); - google::SetStderrLogging(google::INFO); - - ::testing::AddGlobalTestEnvironment(new nebula::http::HttpClientTestEnv()); - return RUN_ALL_TESTS(); -} diff --git a/src/mock/CMakeLists.txt b/src/mock/CMakeLists.txt index 0eed423d30d..b12601b5be6 100644 --- a/src/mock/CMakeLists.txt +++ b/src/mock/CMakeLists.txt @@ -11,3 +11,7 @@ nebula_add_library( ) +nebula_add_library( + fake_http_server_obj OBJECT + FakeHttpServer.cpp +) diff --git a/src/mock/FakeHttpServer.cpp b/src/mock/FakeHttpServer.cpp new file mode 100644 index 00000000000..c52838f98cd --- /dev/null +++ b/src/mock/FakeHttpServer.cpp @@ -0,0 +1,100 @@ +/* Copyright (c) 2022 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License. + */ +#include "mock/FakeHttpServer.h" + +#include "folly/synchronization/Baton.h" +#include "glog/logging.h" +#include "proxygen/httpserver/HTTPServer.h" +#include "proxygen/httpserver/HTTPServerOptions.h" +#include "proxygen/httpserver/ResponseBuilder.h" + +namespace nebula { + +void FakeHttpHandler::onRequest(std::unique_ptr message) noexcept { + CHECK(message->getMethod()); + method_ = message->getMethod().value(); + auto headers = message->extractHeaders(); + headers.forEach( + [this](std::string name, std::string value) { this->headers_.emplace_back(name, value); }); +} + +void FakeHttpHandler::onBody(std::unique_ptr buf) noexcept { + if (body_) { + body_->appendChain(std::move(buf)); + } else { + body_ = std::move(buf); + } +} + +void FakeHttpHandler::onEOM() noexcept { + std::tuple, std::string> result; + switch (method_) { + case ::proxygen::HTTPMethod::PUT: + result = onPut(); + break; + case ::proxygen::HTTPMethod::GET: + result = onGet(); + break; + case ::proxygen::HTTPMethod::POST: + result = onPost(); + break; + case ::proxygen::HTTPMethod::DELETE: + result = onDelete(); + break; + default: + CHECK(false); + break; + } + auto builder = ::proxygen::ResponseBuilder(downstream_); + builder.status(std::get<0>(result), ""); + for (auto& [name, value] : std::get<1>(result)) { + builder.header(name, value); + } + builder.body(std::get<2>(result)); + builder.sendWithEOM(); +} + +void FakeHttpHandler::onUpgrade(proxygen::UpgradeProtocol) noexcept { + // Do nothing +} + +void FakeHttpHandler::requestComplete() noexcept { + delete this; +} + +void FakeHttpHandler::onError(proxygen::ProxygenError err) noexcept { + LOG(FATAL) << ::proxygen::getErrorString(err); +} + +FakeHttpServer::FakeHttpServer(int port) : port_(port) {} + +void FakeHttpServer::start() { + ::proxygen::HTTPServerOptions options; + options.threads = 2; + options.idleTimeout = std::chrono::milliseconds(60000); + options.enableContentCompression = false; + options.handlerFactories = + proxygen::RequestHandlerChain().addThen().build(); + options.h2cEnabled = true; + + server_ = std::make_unique<::proxygen::HTTPServer>(std::move(options)); + std::vector<::proxygen::HTTPServer::IPConfig> ipconfig = { + {folly::SocketAddress("127.0.0.1", port_, true), ::proxygen::HTTPServer::Protocol::HTTP}}; + + server_->bind(ipconfig); + folly::Baton baton; + t_ = std::thread([this, &baton]() { this->server_->start([&baton]() { baton.post(); }); }); + baton.wait(); +} + +void FakeHttpServer::stop() { + server_->stop(); +} + +void FakeHttpServer::join() { + t_.join(); +} + +} // namespace nebula diff --git a/src/mock/FakeHttpServer.h b/src/mock/FakeHttpServer.h new file mode 100644 index 00000000000..b57565f6aaa --- /dev/null +++ b/src/mock/FakeHttpServer.h @@ -0,0 +1,75 @@ +/* Copyright (c) 2022 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License. + */ + +#ifndef MOCK_FAKEHTTPSERVER_H_ +#define MOCK_FAKEHTTPSERVER_H_ +#include + +#include "proxygen/httpserver/HTTPServer.h" +#include "proxygen/httpserver/RequestHandler.h" +#include "proxygen/httpserver/RequestHandlerFactory.h" +namespace nebula { + +class FakeHttpHandler : public proxygen::RequestHandler { + public: + void onRequest(std::unique_ptr headers) noexcept override; + + void onBody(std::unique_ptr body) noexcept override; + + void onEOM() noexcept override; + + void onUpgrade(proxygen::UpgradeProtocol proto) noexcept override; + + void requestComplete() noexcept override; + + void onError(proxygen::ProxygenError err) noexcept override; + + private: + std::vector> headers_; + std::unique_ptr body_; + ::proxygen::HTTPMethod method_; + + virtual std::tuple, std::string> onPut() { + return {200, {}, "PUT"}; + } + virtual std::tuple, std::string> onGet() { + return {200, {}, "GET"}; + } + virtual std::tuple, std::string> onDelete() { + return {200, {}, "DELETE"}; + } + virtual std::tuple, std::string> onPost() { + return {200, {}, "POST"}; + } +}; + +class FakeHttpHandlerFactory : public ::proxygen::RequestHandlerFactory { + public: + void onServerStart(folly::EventBase*) noexcept override {} + + void onServerStop() noexcept override {} + + ::proxygen::RequestHandler* onRequest(::proxygen::RequestHandler*, + ::proxygen::HTTPMessage*) noexcept override { + return new FakeHttpHandler(); + } +}; + +class FakeHttpServer { + public: + explicit FakeHttpServer(int port); + void start(); + void stop(); + void join(); + + private: + int port_; + std::thread t_; + std::unique_ptr<::proxygen::HTTPServer> server_ = nullptr; +}; + +} // namespace nebula + +#endif