The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#) https://grpc.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

314 lines
11 KiB

//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <string.h>
#include <algorithm>
#include <functional>
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_format.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include <grpc/grpc.h>
#include <grpc/impl/channel_arg_names.h>
#include <grpc/support/alloc.h>
#include <grpc/support/sync.h>
#include <grpc/support/time.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/gprpp/orphanable.h"
#include "src/core/lib/gprpp/status_helper.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/gprpp/time_util.h"
#include "src/core/lib/iomgr/closure.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/iomgr/iomgr_fwd.h"
#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/iomgr/pollset.h"
#include "src/core/lib/security/credentials/credentials.h" // IWYU pragma: keep
#include "src/core/lib/uri/uri_parser.h"
#include "src/core/util/http_client/httpcli.h"
#include "src/core/util/http_client/httpcli_ssl_credentials.h"
#include "src/core/util/http_client/parser.h"
#include "src/core/util/subprocess.h"
#include "test/core/http/httpcli_test_util.h"
#include "test/core/test_util/fake_udp_and_tcp_server.h"
#include "test/core/test_util/test_config.h"
namespace {
grpc_core::Timestamp NSecondsTime(int seconds) {
return grpc_core::Timestamp::FromTimespecRoundUp(
grpc_timeout_seconds_to_deadline(seconds));
}
absl::Time AbslDeadlineSeconds(int s) {
return grpc_core::ToAbslTime(grpc_timeout_seconds_to_deadline(s));
}
int g_argc;
char** g_argv;
int g_server_port;
gpr_subprocess* g_server;
class HttpsCliTest : public ::testing::Test {
public:
HttpsCliTest() {
grpc_init();
grpc_core::ExecCtx exec_ctx;
grpc_pollset* pollset =
static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
grpc_pollset_init(pollset, &mu_);
pops_ = grpc_polling_entity_create_from_pollset(pollset);
}
~HttpsCliTest() override {
{
grpc_core::ExecCtx exec_ctx;
grpc_pollset_shutdown(
grpc_polling_entity_pollset(&pops_),
GRPC_CLOSURE_CREATE(DestroyPops, &pops_, grpc_schedule_on_exec_ctx));
}
grpc_shutdown();
}
void RunAndKick(const std::function<void()>& f) {
grpc_core::MutexLockForGprMu lock(mu_);
f();
CHECK(GRPC_LOG_IF_ERROR(
"pollset_kick",
grpc_pollset_kick(grpc_polling_entity_pollset(&pops_), nullptr)));
}
void PollUntil(const std::function<bool()>& predicate, absl::Time deadline) {
gpr_mu_lock(mu_);
while (!predicate()) {
CHECK(absl::Now() < deadline);
grpc_pollset_worker* worker = nullptr;
CHECK(GRPC_LOG_IF_ERROR(
"pollset_work", grpc_pollset_work(grpc_polling_entity_pollset(&pops_),
&worker, NSecondsTime(1))));
gpr_mu_unlock(mu_);
gpr_mu_lock(mu_);
}
gpr_mu_unlock(mu_);
}
grpc_polling_entity* pops() { return &pops_; }
protected:
static void SetUpTestSuite() {
auto test_server = grpc_core::testing::StartHttpRequestTestServer(
g_argc, g_argv, true /* use_ssl */);
g_server = test_server.server;
g_server_port = test_server.port;
}
static void TearDownTestSuite() { gpr_subprocess_destroy(g_server); }
private:
static void DestroyPops(void* p, grpc_error_handle /*error*/) {
grpc_polling_entity* pops = static_cast<grpc_polling_entity*>(p);
grpc_pollset_destroy(grpc_polling_entity_pollset(pops));
gpr_free(grpc_polling_entity_pollset(pops));
}
gpr_mu* mu_;
grpc_polling_entity pops_;
};
struct RequestState {
explicit RequestState(HttpsCliTest* test) : test(test) {}
~RequestState() {
grpc_core::ExecCtx exec_ctx;
grpc_http_response_destroy(&response);
}
HttpsCliTest* test;
bool done = false;
grpc_http_response response = {};
};
void OnFinish(void* arg, grpc_error_handle error) {
RequestState* request_state = static_cast<RequestState*>(arg);
const char* expect =
"<html><head><title>Hello world!</title></head>"
"<body><p>This is a test</p></body></html>";
grpc_http_response response = request_state->response;
LOG(INFO) << "response status=" << response.status
<< " error=" << grpc_core::StatusToString(error);
CHECK(error.ok());
CHECK_EQ(response.status, 200);
CHECK(response.body_length == strlen(expect));
CHECK_EQ(memcmp(expect, response.body, response.body_length), 0);
request_state->test->RunAndKick(
[request_state]() { request_state->done = true; });
}
void OnFinishExpectFailure(void* arg, grpc_error_handle error) {
RequestState* request_state = static_cast<RequestState*>(arg);
grpc_http_response response = request_state->response;
LOG(INFO) << "response status=" << response.status
<< " error=" << grpc_core::StatusToString(error);
CHECK(!error.ok());
request_state->test->RunAndKick(
[request_state]() { request_state->done = true; });
}
TEST_F(HttpsCliTest, Get) {
RequestState request_state(this);
grpc_http_request req;
grpc_core::ExecCtx exec_ctx;
std::string host = absl::StrFormat("localhost:%d", g_server_port);
LOG(INFO) << "requesting from " << host;
memset(&req, 0, sizeof(req));
grpc_arg ssl_override_arg = grpc_channel_arg_string_create(
const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG),
const_cast<char*>("foo.test.google.fr"));
grpc_channel_args args = {1, &ssl_override_arg};
auto uri = grpc_core::URI::Create("https", host, "/get",
{} /* query params */, "" /* fragment */);
CHECK(uri.ok());
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Get(
std::move(*uri), &args, pops(), &req, NSecondsTime(15),
GRPC_CLOSURE_CREATE(OnFinish, &request_state,
grpc_schedule_on_exec_ctx),
&request_state.response,
grpc_core::CreateHttpRequestSSLCredentials());
http_request->Start();
PollUntil([&request_state]() { return request_state.done; },
AbslDeadlineSeconds(60));
}
TEST_F(HttpsCliTest, Post) {
RequestState request_state(this);
grpc_http_request req;
grpc_core::ExecCtx exec_ctx;
std::string host = absl::StrFormat("localhost:%d", g_server_port);
LOG(INFO) << "posting to " << host;
memset(&req, 0, sizeof(req));
req.body = const_cast<char*>("hello");
req.body_length = 5;
grpc_arg ssl_override_arg = grpc_channel_arg_string_create(
const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG),
const_cast<char*>("foo.test.google.fr"));
grpc_channel_args args = {1, &ssl_override_arg};
auto uri = grpc_core::URI::Create("https", host, "/post",
{} /* query params */, "" /* fragment */);
CHECK(uri.ok());
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Post(
std::move(*uri), &args /* channel args */, pops(), &req,
NSecondsTime(15),
GRPC_CLOSURE_CREATE(OnFinish, &request_state,
grpc_schedule_on_exec_ctx),
&request_state.response,
grpc_core::CreateHttpRequestSSLCredentials());
http_request->Start();
PollUntil([&request_state]() { return request_state.done; },
AbslDeadlineSeconds(60));
}
// The goal of this test is to make sure that we can cancel HTTP requests
// while they're waiting for a response from the server to finish their
// SSL handshakes. Note that the main focus of this test is to just exercise
// the relevant code paths and make sure there aren't any crashes etc., rather
// than to make sure that cancellation happens in a timely manner.
TEST_F(HttpsCliTest, CancelGetDuringSSLHandshake) {
// Start up a fake TCP server which accepts connections and then hangs,
// i.e. it won't send any bytes back to the client.
grpc_core::testing::FakeUdpAndTcpServer fake_http_server(
grpc_core::testing::FakeUdpAndTcpServer::AcceptMode::
kWaitForClientToSendFirstBytes,
grpc_core::testing::FakeUdpAndTcpServer::CloseSocketUponCloseFromPeer);
// Use multiple threads to try to trigger races etc.
int kNumThreads = 10;
std::vector<std::thread> threads;
threads.reserve(kNumThreads);
for (int i = 0; i < kNumThreads; i++) {
grpc_core::testing::FakeUdpAndTcpServer* fake_http_server_ptr =
&fake_http_server;
threads.push_back(std::thread([this, fake_http_server_ptr]() {
RequestState request_state(this);
grpc_http_request req;
grpc_core::ExecCtx exec_ctx;
memset(&req, 0, sizeof(req));
grpc_arg ssl_override_arg = grpc_channel_arg_string_create(
const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG),
const_cast<char*>("foo.test.google.fr"));
grpc_channel_args args = {1, &ssl_override_arg};
auto uri = grpc_core::URI::Create(
"https", fake_http_server_ptr->address(), "/get",
{} /* query params */, "" /* fragment */);
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Get(
std::move(*uri), &args, pops(), &req, NSecondsTime(120),
GRPC_CLOSURE_CREATE(OnFinishExpectFailure, &request_state,
grpc_schedule_on_exec_ctx),
&request_state.response,
grpc_core::CreateHttpRequestSSLCredentials());
// Start a request. It will establish a TCP connection to the
// server and then begin an SSL handshake. The server won't send
// anything back though, so it will be stuck in its SSL handshake,
// waiting for the firt response from the server.
http_request->Start();
exec_ctx.Flush();
std::thread cancel_thread([&http_request]() {
// Give one second to let the client get into the middle of its
// SSL handshake, and then cancel the request.
gpr_sleep_until(grpc_timeout_seconds_to_deadline(1));
grpc_core::ExecCtx exec_ctx;
http_request.reset();
});
// Poll with a deadline explicitly lower than the request timeout, so
// that we know that the request timeout isn't just kicking in.
PollUntil([&request_state]() { return request_state.done; },
AbslDeadlineSeconds(60));
cancel_thread.join();
}));
}
for (auto& t : threads) {
t.join();
}
}
} // namespace
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
grpc::testing::TestEnvironment env(&argc, argv);
// launch the test server later, so that --gtest_list_tests works
g_argc = argc;
g_argv = argv;
// run tests
return RUN_ALL_TESTS();
}