Skip to content

Commit

Permalink
Http client ut (#4966)
Browse files Browse the repository at this point in the history
* add fake http server

* add http client ut

* remove some useless code

Co-authored-by: Sophie <84560950+Sophie-Xie@users.noreply.github.com>
  • Loading branch information
cangfengzhs and Sophie-Xie authored Dec 1, 2022
1 parent c3048bc commit 4f1fe76
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 90 deletions.
12 changes: 2 additions & 10 deletions src/common/http/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,10 @@ nebula_add_test(
OBJECTS
$<TARGET_OBJECTS:base_obj>
$<TARGET_OBJECTS:http_client_obj>
$<TARGET_OBJECTS:ws_obj>
$<TARGET_OBJECTS:ws_common_obj>
$<TARGET_OBJECTS:process_obj>
$<TARGET_OBJECTS:fs_obj>
$<TARGET_OBJECTS:stats_obj>
$<TARGET_OBJECTS:time_obj>
$<TARGET_OBJECTS:version_obj>
$<TARGET_OBJECTS:datatypes_obj>
$<TARGET_OBJECTS:wkt_wkb_io_obj>
$<TARGET_OBJECTS:fake_http_server_obj>
LIBRARIES
${PROXYGEN_LIBRARIES}
gtest
curl

gtest_main
)
119 changes: 39 additions & 80 deletions src/common/http/test/HttpClientTest.cpp
Original file line number Diff line number Diff line change
@@ -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 <gtest/gtest.h>
#include <proxygen/httpserver/RequestHandler.h>
#include <proxygen/httpserver/ResponseBuilder.h>

#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<proxygen::HTTPMessage>) noexcept override {}

void onBody(std::unique_ptr<folly::IOBuf>) 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<WebService>();
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<WebService> 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();
}
4 changes: 4 additions & 0 deletions src/mock/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ nebula_add_library(
)


nebula_add_library(
fake_http_server_obj OBJECT
FakeHttpServer.cpp
)
100 changes: 100 additions & 0 deletions src/mock/FakeHttpServer.cpp
Original file line number Diff line number Diff line change
@@ -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<proxygen::HTTPMessage> 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<folly::IOBuf> buf) noexcept {
if (body_) {
body_->appendChain(std::move(buf));
} else {
body_ = std::move(buf);
}
}

void FakeHttpHandler::onEOM() noexcept {
std::tuple<int, std::map<std::string, std::string>, 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<FakeHttpHandlerFactory>().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
75 changes: 75 additions & 0 deletions src/mock/FakeHttpServer.h
Original file line number Diff line number Diff line change
@@ -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 <vector>

#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<proxygen::HTTPMessage> headers) noexcept override;

void onBody(std::unique_ptr<folly::IOBuf> 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<std::pair<std::string, std::string>> headers_;
std::unique_ptr<folly::IOBuf> body_;
::proxygen::HTTPMethod method_;

virtual std::tuple<int, std::map<std::string, std::string>, std::string> onPut() {
return {200, {}, "PUT"};
}
virtual std::tuple<int, std::map<std::string, std::string>, std::string> onGet() {
return {200, {}, "GET"};
}
virtual std::tuple<int, std::map<std::string, std::string>, std::string> onDelete() {
return {200, {}, "DELETE"};
}
virtual std::tuple<int, std::map<std::string, std::string>, 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

0 comments on commit 4f1fe76

Please sign in to comment.