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