Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix: Fix some HTTP-related issues #152

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion trpc/client/http/http_service_proxy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,15 @@ Future<ProtocolPtr> HttpServiceProxy::AsyncInnerUnaryInvoke(const ClientContextP
ProtocolPtr p = rsp.GetValue0();
// When the call is successful, set the response data (for use by the `CLIENT_POST_RPC_INVOKE` filter).
context->SetResponseData(&p);
filter_controller_.RunMessageClientFilters(FilterPoint::CLIENT_POST_RPC_INVOKE, context);
if (!CheckHttpResponse(context, p)) {
std::string error = fmt::format("service name:{},check http reply failed,{}", GetServiceName(),
context->GetStatus().ToString());
TRPC_LOG_ERROR(error);
filter_controller_.RunMessageClientFilters(FilterPoint::CLIENT_POST_RPC_INVOKE, context);
return MakeExceptionFuture<ProtocolPtr>(
CommonException(context->GetStatus().ErrorMessage().c_str(), context->GetStatus().GetFuncRetCode()));
}
filter_controller_.RunMessageClientFilters(FilterPoint::CLIENT_POST_RPC_INVOKE, context);
return MakeReadyFuture<ProtocolPtr>(std::move(p));
}

Expand Down
136 changes: 136 additions & 0 deletions trpc/client/http/http_service_proxy_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "trpc/client/make_client_context.h"
#include "trpc/client/service_proxy_option_setter.h"
#include "trpc/codec/codec_manager.h"
#include "trpc/filter/filter_manager.h"
#include "trpc/future/future_utility.h"
#include "trpc/naming/trpc_naming_registry.h"
#include "trpc/proto/testing/helloworld.pb.h"
Expand All @@ -28,6 +29,32 @@

namespace trpc::testing {

class TestClientFilter : public trpc::MessageClientFilter {
public:
std::string Name() override { return "test_filter"; }

std::vector<trpc::FilterPoint> GetFilterPoint() override {
std::vector<FilterPoint> points = {trpc::FilterPoint::CLIENT_PRE_RPC_INVOKE,
trpc::FilterPoint::CLIENT_POST_RPC_INVOKE};
return points;
}

void operator()(trpc::FilterStatus& status, trpc::FilterPoint point, const trpc::ClientContextPtr& context) override {
status = FilterStatus::CONTINUE;
// record the status upon entering the CLIENT_POST_RPC_INVOKE point
if (point == FilterPoint::CLIENT_POST_RPC_INVOKE) {
status_ = context->GetStatus();
}
}

void SetStatus(trpc::Status&& status) { status_ = std::move(status); }

Status GetStatus() { return status_; }

private:
trpc::Status status_;
};

class HttpServiceProxyTest : public ::testing::Test {
public:
static void SetUpTestCase() {
Expand All @@ -46,6 +73,10 @@ class HttpServiceProxyTest : public ::testing::Test {
codec::Init();
serialization::Init();
naming::Init();

filter_ = std::make_shared<TestClientFilter>();
trpc::FilterManager::GetInstance()->AddMessageClientFilter(filter_);
option_->service_filters.push_back(filter_->Name());
}

static void TearDownTestCase() {
Expand All @@ -58,9 +89,11 @@ class HttpServiceProxyTest : public ::testing::Test {

protected:
static std::shared_ptr<ServiceProxyOption> option_;
static std::shared_ptr<TestClientFilter> filter_;
};

std::shared_ptr<ServiceProxyOption> HttpServiceProxyTest::option_ = std::make_shared<ServiceProxyOption>();
std::shared_ptr<TestClientFilter> HttpServiceProxyTest::filter_;

class MockHttpServiceProxy : public trpc::http::HttpServiceProxy {
public:
Expand Down Expand Up @@ -3891,4 +3924,107 @@ TEST_F(HttpServiceProxyTest, ConstructPBRequest) {
}
}

TEST_F(HttpServiceProxyTest, FilterExecWhenSuccess) {
auto proxy = std::make_shared<MockHttpServiceProxy>();
proxy->SetMockServiceProxyOption(option_);
auto ctx = MakeClientContext(proxy);
ctx->SetStatus(Status());
ctx->SetAddr("127.0.0.1", 10002);

trpc::http::HttpResponse reply;
reply.SetVersion("1.1");
reply.SetStatus(trpc::http::HttpResponse::StatusCode::kOk);
proxy->SetReplyError(false);
proxy->SetReply(reply);

std::string rspStr;
filter_->SetStatus(Status());
auto st = proxy->GetString(ctx, "http://127.0.0.1:10002/hello", &rspStr);
ASSERT_TRUE(st.OK());
// verify that the status is OK upon entering the RPC post-filter point
ASSERT_TRUE(filter_->GetStatus().OK());

proxy->SetReplyError(false);
proxy->SetReply(reply);
ctx = MakeClientContext(proxy);
ctx->SetStatus(Status());
ctx->SetAddr("127.0.0.1", 10002);
filter_->SetStatus(Status());
auto async_get_string_fut =
proxy->AsyncGetString(ctx, "http://127.0.0.1:10002/hello").Then([](Future<std::string>&& future) {
EXPECT_TRUE(future.IsReady());
// verify that the status is OK upon entering the RPC post-filter point
EXPECT_TRUE(filter_->GetStatus().OK());

return MakeReadyFuture<>();
});
future::BlockingGet(std::move(async_get_string_fut));
}

TEST_F(HttpServiceProxyTest, FilterExecWithFrameworkErr) {
auto proxy = std::make_shared<MockHttpServiceProxy>();
proxy->SetMockServiceProxyOption(option_);
auto ctx = MakeClientContext(proxy);
ctx->SetStatus(Status());
ctx->SetAddr("127.0.0.1", 10002);

proxy->SetReplyError(true);
std::string rspStr;
filter_->SetStatus(Status());
auto st = proxy->GetString(ctx, "http://127.0.0.1:10002/hello", &rspStr);
ASSERT_FALSE(st.OK());
// verify that the status is already failed upon entering the RPC post-filter point
ASSERT_FALSE(filter_->GetStatus().OK());

proxy->SetReplyError(true);
ctx = MakeClientContext(proxy);
ctx->SetStatus(Status());
ctx->SetAddr("127.0.0.1", 10002);
filter_->SetStatus(Status());
auto async_get_string_fut =
proxy->AsyncGetString(ctx, "http://127.0.0.1:10002/hello").Then([](Future<std::string>&& future) {
EXPECT_TRUE(future.IsFailed());
// verify that the status is already failed upon entering the RPC post-filter point
EXPECT_FALSE(filter_->GetStatus().OK());
return MakeReadyFuture<>();
});
future::BlockingGet(std::move(async_get_string_fut));
}

TEST_F(HttpServiceProxyTest, FilterExecWithHttpErr) {
auto proxy = std::make_shared<MockHttpServiceProxy>();
proxy->SetMockServiceProxyOption(option_);
auto ctx = MakeClientContext(proxy);
ctx->SetStatus(Status());
ctx->SetAddr("127.0.0.1", 10002);

trpc::http::HttpResponse reply;
reply.SetVersion("1.1");
reply.SetStatus(trpc::http::HttpResponse::StatusCode::kForbidden);
proxy->SetReplyError(false);
proxy->SetReply(reply);

std::string rspStr;
filter_->SetStatus(Status());
auto st = proxy->GetString(ctx, "http://127.0.0.1:10002/hello", &rspStr);
ASSERT_FALSE(st.OK());
// verify that the status is already failed upon entering the RPC post-filter point
ASSERT_FALSE(filter_->GetStatus().OK());

proxy->SetReplyError(false);
proxy->SetReply(reply);
ctx = MakeClientContext(proxy);
ctx->SetStatus(Status());
ctx->SetAddr("127.0.0.1", 10002);
filter_->SetStatus(Status());
auto async_get_string_fut =
proxy->AsyncGetString(ctx, "http://127.0.0.1:10002/hello").Then([](Future<std::string>&& future) {
EXPECT_TRUE(future.IsFailed());
// verify that the status is already failed upon entering the RPC post-filter point
EXPECT_FALSE(filter_->GetStatus().OK());
return MakeReadyFuture<>();
});
future::BlockingGet(std::move(async_get_string_fut));
}

} // namespace trpc::testing
4 changes: 4 additions & 0 deletions trpc/codec/http/http_protocol.cc
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ bool HttpRequestProtocol::WaitForFullRequest() {
return false;
}

uint32_t HttpRequestProtocol::GetMessageSize() const { return request->ContentLength(); }

uint32_t HttpResponseProtocol::GetMessageSize() const { return response.ContentLength(); }

namespace internal {
const std::string& GetHeaderString(const http::HeaderPairs& header, const std::string& name) {
return header.Get(name);
Expand Down
6 changes: 6 additions & 0 deletions trpc/codec/http/http_protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ class HttpRequestProtocol : public Protocol {
void SetFromHttpServiceProxy(bool from_http_service_proxy) { from_http_service_proxy_ = from_http_service_proxy; }
bool IsFromHttpServiceProxy() { return from_http_service_proxy_; }

/// @brief Get size of message
uint32_t GetMessageSize() const override;

public:
uint32_t request_id{0};
http::RequestPtr request{nullptr};
Expand Down Expand Up @@ -101,6 +104,9 @@ class HttpResponseProtocol : public Protocol {
return std::move(*response.GetMutableNonContiguousBufferContent());
}

/// @brief Get size of message
uint32_t GetMessageSize() const override;

public:
http::Response response;
};
Expand Down
22 changes: 12 additions & 10 deletions trpc/codec/http/http_protocol_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ TEST_F(HttpProtoFixture, HttpRequestProtocolTest) {
HttpRequestProtocol req = HttpRequestProtocol(std::make_shared<http::Request>());
req.request->SetContent(test);
NoncontiguousBuffer buff;
EXPECT_FALSE(req.ZeroCopyDecode(buff));
EXPECT_FALSE(req.ZeroCopyEncode(buff));
ASSERT_FALSE(req.ZeroCopyDecode(buff));
ASSERT_FALSE(req.ZeroCopyEncode(buff));
ASSERT_NE(0, req.GetMessageSize());
}

TEST_F(HttpProtoFixture, HttpResponseProtocolTest) {
Expand All @@ -47,8 +48,9 @@ TEST_F(HttpProtoFixture, HttpResponseProtocolTest) {
reply.SetStatus(trpc::http::HttpResponse::StatusCode::kOk);
reply.SetContent("{\"age\":\"18\",\"height\":180}");
NoncontiguousBuffer buff;
EXPECT_FALSE(rsp.ZeroCopyDecode(buff));
EXPECT_FALSE(rsp.ZeroCopyEncode(buff));
ASSERT_FALSE(rsp.ZeroCopyDecode(buff));
ASSERT_FALSE(rsp.ZeroCopyEncode(buff));
ASSERT_NE(0, rsp.GetMessageSize());
}

TEST(HttpRequestProtocolTest, GetOkNonContiguousProtocolBody) {
Expand All @@ -59,13 +61,13 @@ TEST(HttpRequestProtocolTest, GetOkNonContiguousProtocolBody) {
request_protocol.SetNonContiguousProtocolBody(builder.DestructiveGet());

auto body_buffer = request_protocol.GetNonContiguousProtocolBody();
EXPECT_EQ(greetings.size(), body_buffer.ByteSize());
ASSERT_EQ(greetings.size(), body_buffer.ByteSize());
}

TEST(HttpRequestProtocolTest, GetEmptyNonContiguousProtocolBody) {
HttpRequestProtocol request_protocol{std::make_shared<http::HttpRequest>()};
auto body_buffer = request_protocol.GetNonContiguousProtocolBody();
EXPECT_EQ(0, body_buffer.size());
ASSERT_EQ(0, body_buffer.size());
}

TEST(HttpResponseProtocolTest, GetOkNonContiguousProtocolBody) {
Expand All @@ -74,8 +76,8 @@ TEST(HttpResponseProtocolTest, GetOkNonContiguousProtocolBody) {

response_protocol.SetNonContiguousProtocolBody(CreateBufferSlow(greetings));

EXPECT_EQ(greetings.size(), response_protocol.GetNonContiguousProtocolBody().ByteSize());
EXPECT_TRUE(response_protocol.response.GetContent().empty());
ASSERT_EQ(greetings.size(), response_protocol.GetNonContiguousProtocolBody().ByteSize());
ASSERT_TRUE(response_protocol.response.GetContent().empty());
}

TEST(EncodeTypeToMimeTest, EncodeTypeToMime) {
Expand All @@ -92,7 +94,7 @@ TEST(EncodeTypeToMimeTest, EncodeTypeToMime) {
};

for (const auto& t : testings) {
EXPECT_EQ(t.expect, EncodeTypeToMime(t.encode_type));
ASSERT_EQ(t.expect, EncodeTypeToMime(t.encode_type));
}
}

Expand All @@ -112,7 +114,7 @@ TEST(MimeToEncodeTypeTest, MimeToEncodeType) {
};

for (const auto& t : testings) {
EXPECT_EQ(t.expect, MimeToEncodeType(t.mime));
ASSERT_EQ(t.expect, MimeToEncodeType(t.mime));
}
}

Expand Down
2 changes: 1 addition & 1 deletion trpc/util/http/field_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class FieldMap {
/// @brief Does same thing as Set method, but only works when key does not exist in the map.
template <typename K, typename V>
void SetIfNotPresent(K&& key, V&& value) {
if (auto it = pairs_.lower_bound(key); it == pairs_.end() || it->first != key) {
if (auto it = pairs_.lower_bound(key); it == pairs_.end() || !CaseInsensitiveEqualTo()(it->first, key)) {
pairs_.emplace_hint(it, std::forward<K>(key), std::forward<V>(value));
}
}
Expand Down
18 changes: 18 additions & 0 deletions trpc/util/http/field_map_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -207,4 +207,22 @@ TEST_F(FieldMapTest, FlatPairsCount) {
ASSERT_EQ(17, header_.FlatPairsCount());
}

TEST_F(FieldMapTest, SetIfNotPresentOk) {
EXPECT_EQ(0, header_.Values("User-Defined-Key99").size());
header_.SetIfNotPresent("User-Defined-Key99", "user-defined-value99");
EXPECT_EQ(1, header_.Values("User-Defined-Key99").size());
EXPECT_EQ("user-defined-value99", header_.Get("User-Defined-Key99"));

EXPECT_EQ(1, header_.Values("User-Defined-Key01").size());
header_.SetIfNotPresent("User-Defined-Key01", "user-defined-value01-new");
EXPECT_EQ(1, header_.Values("User-Defined-Key01").size());
EXPECT_EQ("user-defined-value01", header_.Get("User-Defined-Key01"));

// case insensitivity test
EXPECT_EQ(1, header_.Values("user-defined-key01").size());
header_.SetIfNotPresent("user-defined-key01", "user-defined-value01-new");
EXPECT_EQ(1, header_.Values("user-defined-key01").size());
EXPECT_EQ("user-defined-value01", header_.Get("user-defined-key01"));
}

} // namespace trpc::http::testing
Loading