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 "src/core/util/http_client/httpcli.h"
20
21 #include <ares.h>
22 #include <grpc/credentials.h>
23 #include <grpc/grpc.h>
24 #include <grpc/grpc_security.h>
25 #include <grpc/support/alloc.h>
26 #include <grpc/support/sync.h>
27 #include <grpc/support/time.h>
28 #include <gtest/gtest.h>
29 #include <string.h>
30 #include <sys/socket.h>
31
32 #include <memory>
33 #include <string>
34 #include <thread>
35 #include <utility>
36
37 #include "absl/log/check.h"
38 #include "absl/log/log.h"
39 #include "absl/strings/str_cat.h"
40 #include "absl/strings/str_format.h"
41 #include "absl/time/clock.h"
42 #include "absl/time/time.h"
43 #include "src/core/lib/iomgr/pollset.h"
44 #include "src/core/lib/iomgr/pollset_set.h"
45 #include "src/core/lib/security/credentials/credentials.h"
46 #include "src/core/resolver/dns/c_ares/grpc_ares_wrapper.h"
47 #include "src/core/util/status_helper.h"
48 #include "src/core/util/subprocess.h"
49 #include "src/core/util/time.h"
50 #include "src/core/util/time_util.h"
51 #include "test/core/test_util/fake_udp_and_tcp_server.h"
52 #include "test/core/test_util/port.h"
53 #include "test/core/test_util/test_config.h"
54 #include "test/core/util/http_client/httpcli_test_util.h"
55
56 namespace {
57
NSecondsTime(int seconds)58 grpc_core::Timestamp NSecondsTime(int seconds) {
59 return grpc_core::Timestamp::FromTimespecRoundUp(
60 grpc_timeout_seconds_to_deadline(seconds));
61 }
62
AbslDeadlineSeconds(int s)63 absl::Time AbslDeadlineSeconds(int s) {
64 return grpc_core::ToAbslTime(grpc_timeout_seconds_to_deadline(s));
65 }
66
67 int g_argc;
68 char** g_argv;
69 int g_server_port;
70 gpr_subprocess* g_server;
71
72 class HttpRequestTest : public ::testing::Test {
73 public:
HttpRequestTest()74 HttpRequestTest() {
75 grpc_init();
76 grpc_core::ExecCtx exec_ctx;
77 grpc_pollset* pollset =
78 static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
79 grpc_pollset_init(pollset, &mu_);
80 pops_ = grpc_polling_entity_create_from_pollset(pollset);
81 }
~HttpRequestTest()82 ~HttpRequestTest() override {
83 {
84 grpc_core::ExecCtx exec_ctx;
85 grpc_pollset_shutdown(
86 grpc_polling_entity_pollset(&pops_),
87 GRPC_CLOSURE_CREATE(DestroyPops, &pops_, grpc_schedule_on_exec_ctx));
88 }
89 grpc_shutdown();
90 }
91
RunAndKick(const std::function<void ()> & f)92 void RunAndKick(const std::function<void()>& f) {
93 grpc_core::MutexLockForGprMu lock(mu_);
94 f();
95 CHECK(GRPC_LOG_IF_ERROR(
96 "pollset_kick",
97 grpc_pollset_kick(grpc_polling_entity_pollset(&pops_), nullptr)));
98 }
99
PollUntil(const std::function<bool ()> & predicate,absl::Time deadline)100 void PollUntil(const std::function<bool()>& predicate, absl::Time deadline) {
101 gpr_mu_lock(mu_);
102 while (!predicate()) {
103 CHECK(absl::Now() < deadline);
104 grpc_pollset_worker* worker = nullptr;
105 CHECK(GRPC_LOG_IF_ERROR(
106 "pollset_work", grpc_pollset_work(grpc_polling_entity_pollset(&pops_),
107 &worker, NSecondsTime(1))));
108 gpr_mu_unlock(mu_);
109 gpr_mu_lock(mu_);
110 }
111 gpr_mu_unlock(mu_);
112 }
113
pops()114 grpc_polling_entity* pops() { return &pops_; }
115
116 protected:
SetUpTestSuite()117 static void SetUpTestSuite() {
118 auto test_server = grpc_core::testing::StartHttpRequestTestServer(
119 g_argc, g_argv, false /* use_ssl */);
120 g_server = test_server.server;
121 g_server_port = test_server.port;
122 }
123
TearDownTestSuite()124 static void TearDownTestSuite() { gpr_subprocess_destroy(g_server); }
125
126 private:
DestroyPops(void * p,grpc_error_handle)127 static void DestroyPops(void* p, grpc_error_handle /*error*/) {
128 grpc_polling_entity* pops = static_cast<grpc_polling_entity*>(p);
129 grpc_pollset_destroy(grpc_polling_entity_pollset(pops));
130 gpr_free(grpc_polling_entity_pollset(pops));
131 }
132
133 gpr_mu* mu_;
134 grpc_polling_entity pops_;
135 };
136
137 struct RequestState {
RequestState__anon44ee580b0111::RequestState138 explicit RequestState(HttpRequestTest* test) : test(test) {}
139
~RequestState__anon44ee580b0111::RequestState140 ~RequestState() {
141 grpc_core::ExecCtx exec_ctx;
142 grpc_http_response_destroy(&response);
143 }
144
145 HttpRequestTest* test;
146 bool done = false;
147 grpc_http_response response = {};
148 grpc_pollset_set* pollset_set_to_destroy_eagerly = nullptr;
149 };
150
OnFinish(void * arg,grpc_error_handle error)151 void OnFinish(void* arg, grpc_error_handle error) {
152 RequestState* request_state = static_cast<RequestState*>(arg);
153 if (request_state->pollset_set_to_destroy_eagerly != nullptr) {
154 // Destroy the request's polling entity param. The goal is to try to catch a
155 // bug where we might still be referencing the polling entity by
156 // a pending TCP connect.
157 grpc_pollset_set_destroy(request_state->pollset_set_to_destroy_eagerly);
158 }
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 if (request_state->pollset_set_to_destroy_eagerly != nullptr) {
176 // Destroy the request's polling entity param. The goal is to try to catch a
177 // bug where we might still be referencing the polling entity by
178 // a pending TCP connect.
179 grpc_pollset_set_destroy(request_state->pollset_set_to_destroy_eagerly);
180 }
181 grpc_http_response response = request_state->response;
182 LOG(INFO) << "response status=" << response.status
183 << " error=" << grpc_core::StatusToString(error);
184 CHECK(!error.ok());
185 request_state->test->RunAndKick(
186 [request_state]() { request_state->done = true; });
187 }
188
TEST_F(HttpRequestTest,Get)189 TEST_F(HttpRequestTest, Get) {
190 RequestState request_state(this);
191 grpc_http_request req;
192 grpc_core::ExecCtx exec_ctx;
193 std::string host = absl::StrFormat("localhost:%d", g_server_port);
194 LOG(INFO) << "requesting from " << host;
195 memset(&req, 0, sizeof(req));
196 auto uri = grpc_core::URI::Create(
197 "http", host, "/get",
198 /*query_parameter_pairs=*/{{"foo", "bar"}, {"baz", "quux"}},
199 /*fragment=*/"");
200 CHECK(uri.ok());
201 grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
202 grpc_core::HttpRequest::Get(
203 std::move(*uri), nullptr /* channel args */, pops(), &req,
204 NSecondsTime(15),
205 GRPC_CLOSURE_CREATE(OnFinish, &request_state,
206 grpc_schedule_on_exec_ctx),
207 &request_state.response,
208 grpc_core::RefCountedPtr<grpc_channel_credentials>(
209 grpc_insecure_credentials_create()));
210 http_request->Start();
211 PollUntil([&request_state]() { return request_state.done; },
212 AbslDeadlineSeconds(60));
213 }
214
TEST_F(HttpRequestTest,Post)215 TEST_F(HttpRequestTest, Post) {
216 RequestState request_state(this);
217 grpc_http_request req;
218 grpc_core::ExecCtx exec_ctx;
219 std::string host = absl::StrFormat("localhost:%d", g_server_port);
220 LOG(INFO) << "posting to " << host;
221 memset(&req, 0, sizeof(req));
222 req.body = const_cast<char*>("hello");
223 req.body_length = 5;
224 auto uri = grpc_core::URI::Create(
225 "http", 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), nullptr /* 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::RefCountedPtr<grpc_channel_credentials>(
237 grpc_insecure_credentials_create()));
238 http_request->Start();
239 PollUntil([&request_state]() { return request_state.done; },
240 AbslDeadlineSeconds(60));
241 }
242
243 int g_fake_non_responsive_dns_server_port;
244
InjectNonResponsiveDNSServer(ares_channel * channel)245 void InjectNonResponsiveDNSServer(ares_channel* channel) {
246 VLOG(2) << "Injecting broken nameserver list. Bad server address:|[::1]:"
247 << g_fake_non_responsive_dns_server_port << "|.";
248 // Configure a non-responsive DNS server at the front of c-ares's nameserver
249 // list.
250 struct ares_addr_port_node dns_server_addrs[1];
251 dns_server_addrs[0].family = AF_INET6;
252 (reinterpret_cast<char*>(&dns_server_addrs[0].addr.addr6))[15] = 0x1;
253 dns_server_addrs[0].tcp_port = g_fake_non_responsive_dns_server_port;
254 dns_server_addrs[0].udp_port = g_fake_non_responsive_dns_server_port;
255 dns_server_addrs[0].next = nullptr;
256 CHECK(ares_set_servers_ports(*channel, dns_server_addrs) == ARES_SUCCESS);
257 }
258
TEST_F(HttpRequestTest,CancelGetDuringDNSResolution)259 TEST_F(HttpRequestTest, CancelGetDuringDNSResolution) {
260 // Inject an unresponsive DNS server into the resolver's DNS server config
261 grpc_core::testing::FakeUdpAndTcpServer fake_dns_server(
262 grpc_core::testing::FakeUdpAndTcpServer::AcceptMode::
263 kWaitForClientToSendFirstBytes,
264 grpc_core::testing::FakeUdpAndTcpServer::CloseSocketUponCloseFromPeer);
265 g_fake_non_responsive_dns_server_port = fake_dns_server.port();
266 void (*prev_test_only_inject_config)(ares_channel* channel) =
267 grpc_ares_test_only_inject_config;
268 grpc_ares_test_only_inject_config = InjectNonResponsiveDNSServer;
269 // Run the same test on several threads in parallel to try to trigger races
270 // etc.
271 int kNumThreads = 10;
272 std::vector<std::thread> threads;
273 threads.reserve(kNumThreads);
274 for (int i = 0; i < kNumThreads; i++) {
275 threads.push_back(std::thread([this]() {
276 RequestState request_state(this);
277 grpc_http_request req;
278 grpc_core::ExecCtx exec_ctx;
279 memset(&req, 0, sizeof(grpc_http_request));
280 auto uri = grpc_core::URI::Create(
281 "http", "dont-care-since-wont-be-resolved.test.com:443", "/get",
282 {} /* query params */, "" /* fragment */);
283 CHECK(uri.ok());
284 grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
285 grpc_core::HttpRequest::Get(
286 std::move(*uri), nullptr /* channel args */, pops(), &req,
287 NSecondsTime(120),
288 GRPC_CLOSURE_CREATE(OnFinishExpectFailure, &request_state,
289 grpc_schedule_on_exec_ctx),
290 &request_state.response,
291 grpc_core::RefCountedPtr<grpc_channel_credentials>(
292 grpc_insecure_credentials_create()));
293 http_request->Start();
294 std::thread cancel_thread([&http_request]() {
295 gpr_sleep_until(grpc_timeout_seconds_to_deadline(1));
296 grpc_core::ExecCtx exec_ctx;
297 http_request.reset();
298 });
299 // Poll with a deadline explicitly lower than the request timeout, so
300 // that we know that the request timeout isn't just kicking in.
301 PollUntil([&request_state]() { return request_state.done; },
302 AbslDeadlineSeconds(60));
303 cancel_thread.join();
304 }));
305 }
306 for (auto& t : threads) {
307 t.join();
308 }
309 grpc_ares_test_only_inject_config = prev_test_only_inject_config;
310 }
311
TEST_F(HttpRequestTest,CancelGetWhileReadingResponse)312 TEST_F(HttpRequestTest, CancelGetWhileReadingResponse) {
313 // Start up a fake HTTP server which just accepts connections
314 // and then hangs, i.e. does not send back any bytes to the client.
315 // The goal here is to get the client to connect to this fake server
316 // and send a request, and then sit waiting for a response. Then, a
317 // separate thread will cancel the HTTP request, and that should let it
318 // complete.
319 grpc_core::testing::FakeUdpAndTcpServer fake_http_server(
320 grpc_core::testing::FakeUdpAndTcpServer::AcceptMode::
321 kWaitForClientToSendFirstBytes,
322 grpc_core::testing::FakeUdpAndTcpServer::CloseSocketUponCloseFromPeer);
323 // Run the same test on several threads in parallel to try to trigger races
324 // etc.
325 int kNumThreads = 10;
326 std::vector<std::thread> threads;
327 threads.reserve(kNumThreads);
328 for (int i = 0; i < kNumThreads; i++) {
329 grpc_core::testing::FakeUdpAndTcpServer* fake_http_server_ptr =
330 &fake_http_server;
331 threads.push_back(std::thread([this, fake_http_server_ptr]() {
332 RequestState request_state(this);
333 grpc_http_request req;
334 grpc_core::ExecCtx exec_ctx;
335 memset(&req, 0, sizeof(req));
336 auto uri = grpc_core::URI::Create("http", fake_http_server_ptr->address(),
337 "/get", {} /* query params */,
338 "" /* fragment */);
339 CHECK(uri.ok());
340 grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
341 grpc_core::HttpRequest::Get(
342 std::move(*uri), nullptr /* channel args */, pops(), &req,
343 NSecondsTime(120),
344 GRPC_CLOSURE_CREATE(OnFinishExpectFailure, &request_state,
345 grpc_schedule_on_exec_ctx),
346 &request_state.response,
347 grpc_core::RefCountedPtr<grpc_channel_credentials>(
348 grpc_insecure_credentials_create()));
349 http_request->Start();
350 exec_ctx.Flush();
351 std::thread cancel_thread([&http_request]() {
352 gpr_sleep_until(grpc_timeout_seconds_to_deadline(1));
353 grpc_core::ExecCtx exec_ctx;
354 http_request.reset();
355 });
356 // Poll with a deadline explicitly lower than the request timeout, so
357 // that we know that the request timeout isn't just kicking in.
358 PollUntil([&request_state]() { return request_state.done; },
359 AbslDeadlineSeconds(60));
360 cancel_thread.join();
361 }));
362 }
363 for (auto& t : threads) {
364 t.join();
365 }
366 }
367
368 // The main point of this test is just to exercise the machinery around
369 // cancellation during TCP connection establishment, to make sure there are no
370 // crashes/races etc. This test doesn't actually verify that cancellation during
371 // TCP setup is happening, though. For that, we would need to induce packet loss
372 // in the test.
TEST_F(HttpRequestTest,CancelGetRacesWithConnectionFailure)373 TEST_F(HttpRequestTest, CancelGetRacesWithConnectionFailure) {
374 // Grab an unoccupied port but don't listen on it. The goal
375 // here is just to have a server address that will reject
376 // TCP connection setups.
377 // Note that because the server is rejecting TCP connections, we
378 // don't really need to cancel the HTTP requests in this test case
379 // in order for them proceed i.e. in order for them to pass. The test
380 // is still beneficial though because it can exercise the same code paths
381 // that would get taken if the HTTP request was cancelled while the TCP
382 // connect attempt was actually hanging.
383 int fake_server_port = grpc_pick_unused_port_or_die();
384 std::string fake_server_address =
385 absl::StrCat("[::1]:", std::to_string(fake_server_port));
386 // Run the same test on several threads in parallel to try to trigger races
387 // etc.
388 int kNumThreads = 10;
389 std::vector<std::thread> threads;
390 threads.reserve(kNumThreads);
391 for (int i = 0; i < kNumThreads; i++) {
392 threads.push_back(std::thread([this, fake_server_address]() {
393 RequestState request_state(this);
394 grpc_http_request req;
395 grpc_core::ExecCtx exec_ctx;
396 memset(&req, 0, sizeof(req));
397 auto uri =
398 grpc_core::URI::Create("http", fake_server_address, "/get",
399 {} /* query params */, "" /* fragment */);
400 CHECK(uri.ok());
401 grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
402 grpc_core::HttpRequest::Get(
403 std::move(*uri), nullptr /* channel args */, pops(), &req,
404 NSecondsTime(120),
405 GRPC_CLOSURE_CREATE(OnFinishExpectFailure, &request_state,
406 grpc_schedule_on_exec_ctx),
407 &request_state.response,
408 grpc_core::RefCountedPtr<grpc_channel_credentials>(
409 grpc_insecure_credentials_create()));
410 // Start the HTTP request. We will ~immediately begin a TCP connect
411 // attempt because there's no name to resolve.
412 http_request->Start();
413 exec_ctx.Flush();
414 // Spawn a separate thread which ~immediately cancels the HTTP request.
415 // Note that even though the server is rejecting TCP connections, it can
416 // still take some time for the client to receive that rejection. So
417 // cancelling the request now can trigger the code paths that would get
418 // taken if the TCP connection was truly hanging e.g. from packet loss.
419 // The goal is just to make sure there are no crashes, races, etc.
420 std::thread cancel_thread([&http_request]() {
421 grpc_core::ExecCtx exec_ctx;
422 http_request.reset();
423 });
424 // Poll with a deadline explicitly lower than the request timeout, so
425 // that we know that the request timeout isn't just kicking in.
426 PollUntil([&request_state]() { return request_state.done; },
427 AbslDeadlineSeconds(60));
428 cancel_thread.join();
429 }));
430 }
431 for (auto& t : threads) {
432 t.join();
433 }
434 }
435
436 // The pollent parameter passed to HttpRequest::Get or Post is owned by
437 // the caller and must not be referenced by the HttpRequest after the
438 // requests's on_done callback is invoked. This test verifies that this
439 // isn't happening by destroying the request's pollset set within the
440 // on_done callback.
TEST_F(HttpRequestTest,CallerPollentsAreNotReferencedAfterCallbackIsRan)441 TEST_F(HttpRequestTest, CallerPollentsAreNotReferencedAfterCallbackIsRan) {
442 // Grab an unoccupied port but don't listen on it. The goal
443 // here is just to have a server address that will reject
444 // TCP connection setups.
445 // Note that we could have used a different server for this test case, e.g.
446 // one which accepts TCP connections. All we need here is something for the
447 // client to connect to, since it will be cancelled roughly during the
448 // connection attempt anyways.
449 int fake_server_port = grpc_pick_unused_port_or_die();
450 std::string fake_server_address =
451 absl::StrCat("[::1]:", std::to_string(fake_server_port));
452 RequestState request_state(this);
453 grpc_http_request req;
454 grpc_core::ExecCtx exec_ctx;
455 memset(&req, 0, sizeof(req));
456 req.path = const_cast<char*>("/get");
457 request_state.pollset_set_to_destroy_eagerly = grpc_pollset_set_create();
458 grpc_polling_entity_add_to_pollset_set(
459 pops(), request_state.pollset_set_to_destroy_eagerly);
460 grpc_polling_entity wrapped_pollset_set_to_destroy_eagerly =
461 grpc_polling_entity_create_from_pollset_set(
462 request_state.pollset_set_to_destroy_eagerly);
463 auto uri = grpc_core::URI::Create("http", fake_server_address, "/get",
464 {} /* query params */, "" /* fragment */);
465 CHECK(uri.ok());
466 grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
467 grpc_core::HttpRequest::Get(
468 std::move(*uri), nullptr /* channel args */,
469 &wrapped_pollset_set_to_destroy_eagerly, &req, NSecondsTime(15),
470 GRPC_CLOSURE_CREATE(OnFinishExpectFailure, &request_state,
471 grpc_schedule_on_exec_ctx),
472 &request_state.response,
473 grpc_core::RefCountedPtr<grpc_channel_credentials>(
474 grpc_insecure_credentials_create()));
475 // Start the HTTP request. We'll start the TCP connect attempt right away.
476 http_request->Start();
477 exec_ctx.Flush();
478 http_request.reset(); // cancel the request
479 // With iomgr polling:
480 // Since the request was cancelled, the on_done callback should be flushed
481 // out on the ExecCtx flush below. When the on_done callback is ran, it will
482 // eagerly destroy 'request_state.pollset_set_to_destroy_eagerly'. PollUntil's
483 // predicate should return true immediately.
484 //
485 // With EventEngine polling:
486 // Since the callback will be run asynchronously in another thread, with an
487 // independent ExecCtx, PollUntil is used here to ensure this test does not
488 // finish before the callback is run.
489 exec_ctx.Flush();
490 PollUntil([&request_state]() { return request_state.done; },
491 AbslDeadlineSeconds(60));
492 }
493
CancelRequest(grpc_core::HttpRequest * req)494 void CancelRequest(grpc_core::HttpRequest* req) {
495 LOG(INFO) << "test only HttpRequest::OnHandshakeDone intercept orphaning "
496 "request: "
497 << req;
498 req->Orphan();
499 }
500
501 // This exercises the code paths that happen when we cancel an HTTP request
502 // before the security handshake callback runs, but after that callback has
503 // already been scheduled with a success result. This case is interesting
504 // because the current security handshake API transfers ownership of output
505 // arguments to the caller only if the handshake is successful, rendering
506 // this code path as something that only occurs with just the right timing.
TEST_F(HttpRequestTest,CancelDuringSecurityHandshakeButHandshakeStillSucceeds)507 TEST_F(HttpRequestTest,
508 CancelDuringSecurityHandshakeButHandshakeStillSucceeds) {
509 RequestState request_state(this);
510 grpc_http_request req;
511 grpc_core::ExecCtx exec_ctx;
512 std::string host = absl::StrFormat("localhost:%d", g_server_port);
513 LOG(INFO) << "requesting from " << host;
514 memset(&req, 0, sizeof(req));
515 auto uri = grpc_core::URI::Create("http", host, "/get", {} /* query params */,
516 "" /* fragment */);
517 CHECK(uri.ok());
518 grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
519 grpc_core::HttpRequest::Get(
520 std::move(*uri), nullptr /* channel args */, pops(), &req,
521 NSecondsTime(15),
522 GRPC_CLOSURE_CREATE(OnFinishExpectFailure, &request_state,
523 grpc_schedule_on_exec_ctx),
524 &request_state.response,
525 grpc_core::RefCountedPtr<grpc_channel_credentials>(
526 grpc_insecure_credentials_create()));
527 grpc_core::HttpRequest::TestOnlySetOnHandshakeDoneIntercept(CancelRequest);
528 http_request->Start();
529 (void)http_request.release(); // request will be orphaned by CancelRequest
530 exec_ctx.Flush();
531 PollUntil([&request_state]() { return request_state.done; },
532 AbslDeadlineSeconds(60));
533 grpc_core::HttpRequest::TestOnlySetOnHandshakeDoneIntercept(nullptr);
534 }
535
536 } // namespace
537
main(int argc,char ** argv)538 int main(int argc, char** argv) {
539 ::testing::InitGoogleTest(&argc, argv);
540 grpc::testing::TestEnvironment env(&argc, argv);
541 // launch the test server later, so that --gtest_list_tests works
542 g_argc = argc;
543 g_argv = argv;
544 // run tests
545 return RUN_ALL_TESTS();
546 }
547