1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #include <grpc/grpc.h>
20 #include <grpc/impl/channel_arg_names.h>
21 #include <grpc/support/alloc.h>
22 #include <grpc/support/sync.h>
23 #include <grpc/support/time.h>
24 #include <gtest/gtest.h>
25 #include <string.h>
26
27 #include <algorithm>
28 #include <functional>
29 #include <memory>
30 #include <string>
31 #include <thread>
32 #include <utility>
33 #include <vector>
34
35 #include "absl/log/check.h"
36 #include "absl/log/log.h"
37 #include "absl/status/statusor.h"
38 #include "absl/strings/str_format.h"
39 #include "absl/time/clock.h"
40 #include "absl/time/time.h"
41 #include "src/core/lib/channel/channel_args.h"
42 #include "src/core/lib/iomgr/closure.h"
43 #include "src/core/lib/iomgr/error.h"
44 #include "src/core/lib/iomgr/exec_ctx.h"
45 #include "src/core/lib/iomgr/iomgr_fwd.h"
46 #include "src/core/lib/iomgr/polling_entity.h"
47 #include "src/core/lib/iomgr/pollset.h"
48 #include "src/core/lib/security/credentials/credentials.h" // IWYU pragma: keep
49 #include "src/core/util/http_client/httpcli.h"
50 #include "src/core/util/http_client/httpcli_ssl_credentials.h"
51 #include "src/core/util/http_client/parser.h"
52 #include "src/core/util/orphanable.h"
53 #include "src/core/util/status_helper.h"
54 #include "src/core/util/subprocess.h"
55 #include "src/core/util/sync.h"
56 #include "src/core/util/time.h"
57 #include "src/core/util/time_util.h"
58 #include "src/core/util/uri.h"
59 #include "test/core/test_util/fake_udp_and_tcp_server.h"
60 #include "test/core/test_util/test_config.h"
61 #include "test/core/util/http_client/httpcli_test_util.h"
62
63 namespace {
64
NSecondsTime(int seconds)65 grpc_core::Timestamp NSecondsTime(int seconds) {
66 return grpc_core::Timestamp::FromTimespecRoundUp(
67 grpc_timeout_seconds_to_deadline(seconds));
68 }
69
AbslDeadlineSeconds(int s)70 absl::Time AbslDeadlineSeconds(int s) {
71 return grpc_core::ToAbslTime(grpc_timeout_seconds_to_deadline(s));
72 }
73
74 int g_argc;
75 char** g_argv;
76 int g_server_port;
77 gpr_subprocess* g_server;
78
79 class HttpsCliTest : public ::testing::Test {
80 public:
HttpsCliTest()81 HttpsCliTest() {
82 grpc_init();
83 grpc_core::ExecCtx exec_ctx;
84 grpc_pollset* pollset =
85 static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
86 grpc_pollset_init(pollset, &mu_);
87 pops_ = grpc_polling_entity_create_from_pollset(pollset);
88 }
~HttpsCliTest()89 ~HttpsCliTest() override {
90 {
91 grpc_core::ExecCtx exec_ctx;
92 grpc_pollset_shutdown(
93 grpc_polling_entity_pollset(&pops_),
94 GRPC_CLOSURE_CREATE(DestroyPops, &pops_, grpc_schedule_on_exec_ctx));
95 }
96 grpc_shutdown();
97 }
98
RunAndKick(const std::function<void ()> & f)99 void RunAndKick(const std::function<void()>& f) {
100 grpc_core::MutexLockForGprMu lock(mu_);
101 f();
102 CHECK(GRPC_LOG_IF_ERROR(
103 "pollset_kick",
104 grpc_pollset_kick(grpc_polling_entity_pollset(&pops_), nullptr)));
105 }
106
PollUntil(const std::function<bool ()> & predicate,absl::Time deadline)107 void PollUntil(const std::function<bool()>& predicate, absl::Time deadline) {
108 gpr_mu_lock(mu_);
109 while (!predicate()) {
110 CHECK(absl::Now() < deadline);
111 grpc_pollset_worker* worker = nullptr;
112 CHECK(GRPC_LOG_IF_ERROR(
113 "pollset_work", grpc_pollset_work(grpc_polling_entity_pollset(&pops_),
114 &worker, NSecondsTime(1))));
115 gpr_mu_unlock(mu_);
116 gpr_mu_lock(mu_);
117 }
118 gpr_mu_unlock(mu_);
119 }
120
pops()121 grpc_polling_entity* pops() { return &pops_; }
122
123 protected:
SetUpTestSuite()124 static void SetUpTestSuite() {
125 auto test_server = grpc_core::testing::StartHttpRequestTestServer(
126 g_argc, g_argv, true /* use_ssl */);
127 g_server = test_server.server;
128 g_server_port = test_server.port;
129 }
130
TearDownTestSuite()131 static void TearDownTestSuite() { gpr_subprocess_destroy(g_server); }
132
133 private:
DestroyPops(void * p,grpc_error_handle)134 static void DestroyPops(void* p, grpc_error_handle /*error*/) {
135 grpc_polling_entity* pops = static_cast<grpc_polling_entity*>(p);
136 grpc_pollset_destroy(grpc_polling_entity_pollset(pops));
137 gpr_free(grpc_polling_entity_pollset(pops));
138 }
139
140 gpr_mu* mu_;
141 grpc_polling_entity pops_;
142 };
143
144 struct RequestState {
RequestState__anondb57369e0111::RequestState145 explicit RequestState(HttpsCliTest* test) : test(test) {}
146
~RequestState__anondb57369e0111::RequestState147 ~RequestState() {
148 grpc_core::ExecCtx exec_ctx;
149 grpc_http_response_destroy(&response);
150 }
151
152 HttpsCliTest* test;
153 bool done = false;
154 grpc_http_response response = {};
155 };
156
OnFinish(void * arg,grpc_error_handle error)157 void OnFinish(void* arg, grpc_error_handle error) {
158 RequestState* request_state = static_cast<RequestState*>(arg);
159 const char* expect =
160 "<html><head><title>Hello world!</title></head>"
161 "<body><p>This is a test</p></body></html>";
162 grpc_http_response response = request_state->response;
163 LOG(INFO) << "response status=" << response.status
164 << " error=" << grpc_core::StatusToString(error);
165 CHECK(error.ok());
166 CHECK_EQ(response.status, 200);
167 CHECK(response.body_length == strlen(expect));
168 CHECK_EQ(memcmp(expect, response.body, response.body_length), 0);
169 request_state->test->RunAndKick(
170 [request_state]() { request_state->done = true; });
171 }
172
OnFinishExpectFailure(void * arg,grpc_error_handle error)173 void OnFinishExpectFailure(void* arg, grpc_error_handle error) {
174 RequestState* request_state = static_cast<RequestState*>(arg);
175 grpc_http_response response = request_state->response;
176 LOG(INFO) << "response status=" << response.status
177 << " error=" << grpc_core::StatusToString(error);
178 CHECK(!error.ok());
179 request_state->test->RunAndKick(
180 [request_state]() { request_state->done = true; });
181 }
182
TEST_F(HttpsCliTest,Get)183 TEST_F(HttpsCliTest, Get) {
184 RequestState request_state(this);
185 grpc_http_request req;
186 grpc_core::ExecCtx exec_ctx;
187 std::string host = absl::StrFormat("localhost:%d", g_server_port);
188 LOG(INFO) << "requesting from " << host;
189 memset(&req, 0, sizeof(req));
190 grpc_arg ssl_override_arg = grpc_channel_arg_string_create(
191 const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG),
192 const_cast<char*>("foo.test.google.fr"));
193 grpc_channel_args args = {1, &ssl_override_arg};
194 auto uri = grpc_core::URI::Create(
195 "https", host, "/get",
196 /*query_parameter_pairs=*/{{"foo", "bar"}, {"baz", "quux"}},
197 /*fragment=*/"");
198 CHECK(uri.ok());
199 grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
200 grpc_core::HttpRequest::Get(
201 std::move(*uri), &args, pops(), &req, NSecondsTime(15),
202 GRPC_CLOSURE_CREATE(OnFinish, &request_state,
203 grpc_schedule_on_exec_ctx),
204 &request_state.response,
205 grpc_core::CreateHttpRequestSSLCredentials());
206 http_request->Start();
207 PollUntil([&request_state]() { return request_state.done; },
208 AbslDeadlineSeconds(60));
209 }
210
TEST_F(HttpsCliTest,Post)211 TEST_F(HttpsCliTest, Post) {
212 RequestState request_state(this);
213 grpc_http_request req;
214 grpc_core::ExecCtx exec_ctx;
215 std::string host = absl::StrFormat("localhost:%d", g_server_port);
216 LOG(INFO) << "posting to " << host;
217 memset(&req, 0, sizeof(req));
218 req.body = const_cast<char*>("hello");
219 req.body_length = 5;
220 grpc_arg ssl_override_arg = grpc_channel_arg_string_create(
221 const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG),
222 const_cast<char*>("foo.test.google.fr"));
223 grpc_channel_args args = {1, &ssl_override_arg};
224 auto uri = grpc_core::URI::Create(
225 "https", host, "/post",
226 /*query_parameter_pairs=*/{{"foo", "bar"}, {"mumble", "frotz"}},
227 /*fragment=*/"");
228 CHECK(uri.ok());
229 grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
230 grpc_core::HttpRequest::Post(
231 std::move(*uri), &args /* channel args */, pops(), &req,
232 NSecondsTime(15),
233 GRPC_CLOSURE_CREATE(OnFinish, &request_state,
234 grpc_schedule_on_exec_ctx),
235 &request_state.response,
236 grpc_core::CreateHttpRequestSSLCredentials());
237 http_request->Start();
238 PollUntil([&request_state]() { return request_state.done; },
239 AbslDeadlineSeconds(60));
240 }
241
242 // The goal of this test is to make sure that we can cancel HTTP requests
243 // while they're waiting for a response from the server to finish their
244 // SSL handshakes. Note that the main focus of this test is to just exercise
245 // the relevant code paths and make sure there aren't any crashes etc., rather
246 // than to make sure that cancellation happens in a timely manner.
TEST_F(HttpsCliTest,CancelGetDuringSSLHandshake)247 TEST_F(HttpsCliTest, CancelGetDuringSSLHandshake) {
248 // Start up a fake TCP server which accepts connections and then hangs,
249 // i.e. it won't send any bytes back to the client.
250 grpc_core::testing::FakeUdpAndTcpServer fake_http_server(
251 grpc_core::testing::FakeUdpAndTcpServer::AcceptMode::
252 kWaitForClientToSendFirstBytes,
253 grpc_core::testing::FakeUdpAndTcpServer::CloseSocketUponCloseFromPeer);
254 // Use multiple threads to try to trigger races etc.
255 int kNumThreads = 10;
256 std::vector<std::thread> threads;
257 threads.reserve(kNumThreads);
258 for (int i = 0; i < kNumThreads; i++) {
259 grpc_core::testing::FakeUdpAndTcpServer* fake_http_server_ptr =
260 &fake_http_server;
261 threads.push_back(std::thread([this, fake_http_server_ptr]() {
262 RequestState request_state(this);
263 grpc_http_request req;
264 grpc_core::ExecCtx exec_ctx;
265 memset(&req, 0, sizeof(req));
266 grpc_arg ssl_override_arg = grpc_channel_arg_string_create(
267 const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG),
268 const_cast<char*>("foo.test.google.fr"));
269 grpc_channel_args args = {1, &ssl_override_arg};
270 auto uri = grpc_core::URI::Create(
271 "https", fake_http_server_ptr->address(), "/get",
272 {} /* query params */, "" /* fragment */);
273 grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
274 grpc_core::HttpRequest::Get(
275 std::move(*uri), &args, pops(), &req, NSecondsTime(120),
276 GRPC_CLOSURE_CREATE(OnFinishExpectFailure, &request_state,
277 grpc_schedule_on_exec_ctx),
278 &request_state.response,
279 grpc_core::CreateHttpRequestSSLCredentials());
280 // Start a request. It will establish a TCP connection to the
281 // server and then begin an SSL handshake. The server won't send
282 // anything back though, so it will be stuck in its SSL handshake,
283 // waiting for the first response from the server.
284 http_request->Start();
285 exec_ctx.Flush();
286 std::thread cancel_thread([&http_request]() {
287 // Give one second to let the client get into the middle of its
288 // SSL handshake, and then cancel the request.
289 gpr_sleep_until(grpc_timeout_seconds_to_deadline(1));
290 grpc_core::ExecCtx exec_ctx;
291 http_request.reset();
292 });
293 // Poll with a deadline explicitly lower than the request timeout, so
294 // that we know that the request timeout isn't just kicking in.
295 PollUntil([&request_state]() { return request_state.done; },
296 AbslDeadlineSeconds(60));
297 cancel_thread.join();
298 }));
299 }
300 for (auto& t : threads) {
301 t.join();
302 }
303 }
304
305 } // namespace
306
main(int argc,char ** argv)307 int main(int argc, char** argv) {
308 ::testing::InitGoogleTest(&argc, argv);
309 grpc::testing::TestEnvironment env(&argc, argv);
310 // launch the test server later, so that --gtest_list_tests works
311 g_argc = argc;
312 g_argv = argv;
313 // run tests
314 return RUN_ALL_TESTS();
315 }
316