xref: /aosp_15_r20/external/grpc-grpc/test/core/end2end/fixtures/http_proxy_fixture.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 //
2 //
3 // Copyright 2016 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 "test/core/end2end/fixtures/http_proxy_fixture.h"
20 
21 #include <limits.h>
22 #include <string.h>
23 
24 #include <atomic>
25 #include <memory>
26 #include <string>
27 #include <vector>
28 
29 #include "absl/status/status.h"
30 #include "absl/status/statusor.h"
31 #include "absl/strings/escaping.h"
32 #include "absl/strings/str_cat.h"
33 #include "absl/strings/strip.h"
34 
35 #include <grpc/grpc.h>
36 #include <grpc/slice.h>
37 #include <grpc/slice_buffer.h>
38 #include <grpc/support/alloc.h>
39 #include <grpc/support/log.h>
40 #include <grpc/support/sync.h>
41 
42 #include "src/core/lib/address_utils/sockaddr_utils.h"
43 #include "src/core/lib/channel/channel_args.h"
44 #include "src/core/lib/channel/channel_args_preconditioning.h"
45 #include "src/core/lib/config/core_configuration.h"
46 #include "src/core/lib/event_engine/channel_args_endpoint_config.h"
47 #include "src/core/lib/event_engine/default_event_engine.h"
48 #include "src/core/lib/gprpp/host_port.h"
49 #include "src/core/lib/gprpp/memory.h"
50 #include "src/core/lib/gprpp/status_helper.h"
51 #include "src/core/lib/gprpp/thd.h"
52 #include "src/core/lib/gprpp/time.h"
53 #include "src/core/lib/http/parser.h"
54 #include "src/core/lib/iomgr/closure.h"
55 #include "src/core/lib/iomgr/combiner.h"
56 #include "src/core/lib/iomgr/endpoint.h"
57 #include "src/core/lib/iomgr/error.h"
58 #include "src/core/lib/iomgr/exec_ctx.h"
59 #include "src/core/lib/iomgr/iomgr_fwd.h"
60 #include "src/core/lib/iomgr/pollset.h"
61 #include "src/core/lib/iomgr/pollset_set.h"
62 #include "src/core/lib/iomgr/resolve_address.h"
63 #include "src/core/lib/iomgr/resolved_address.h"
64 #include "src/core/lib/iomgr/sockaddr.h"
65 #include "src/core/lib/iomgr/tcp_client.h"
66 #include "src/core/lib/iomgr/tcp_server.h"
67 #include "test/core/util/port.h"
68 
69 struct grpc_end2end_http_proxy {
grpc_end2end_http_proxygrpc_end2end_http_proxy70   grpc_end2end_http_proxy()
71       : combiner(grpc_combiner_create(
72             grpc_event_engine::experimental::GetDefaultEventEngine())) {}
73   std::string proxy_name;
74   std::atomic<bool> is_shutdown{false};
75   std::atomic<size_t> users{1};
76   grpc_core::Thread thd;
77   grpc_tcp_server* server = nullptr;
78   const grpc_channel_args* channel_args = nullptr;
79   gpr_mu* mu = nullptr;
80   std::vector<grpc_pollset*> pollset;
81   grpc_core::Combiner* combiner = nullptr;
82 };
83 
84 namespace {
85 
86 // Sometimes, on_accept may be called after thread_main has returned, and the
87 // proxy will have already been destroyed. This value is reset every time a
88 // proxy fixture is created, and it prevents a crash due to a repeated unref.
89 std::atomic<bool> proxy_destroyed{false};
90 
proxy_ref(grpc_end2end_http_proxy * proxy)91 void proxy_ref(grpc_end2end_http_proxy* proxy) { proxy->users.fetch_add(1); }
92 
93 // Returns the remaining number of outstanding refs
proxy_unref(grpc_end2end_http_proxy * proxy)94 size_t proxy_unref(grpc_end2end_http_proxy* proxy) {
95   if (proxy_destroyed.load()) return -1;
96   size_t ref_count = proxy->users.fetch_sub(1) - 1;
97   if (ref_count == 0) {
98     proxy_destroyed.store(true);
99     GRPC_COMBINER_UNREF(proxy->combiner, "test");
100     delete proxy;
101   }
102   return ref_count;
103 }
104 
105 }  // namespace
106 
107 //
108 // Connection handling
109 //
110 
111 // proxy_connection structure is only accessed in the closures which are all
112 // scheduled under the same combiner lock. So there is no need for a mutex to
113 // protect this structure.
114 typedef struct proxy_connection {
115   grpc_end2end_http_proxy* proxy;
116 
117   grpc_endpoint* client_endpoint;
118   grpc_endpoint* server_endpoint;
119 
120   gpr_refcount refcount;
121 
122   grpc_pollset_set* pollset_set;
123 
124   // NOTE: All the closures execute under proxy->combiner lock. Which means
125   // there will not be any data-races between the closures
126   grpc_closure on_read_request_done;
127   grpc_closure on_server_connect_done;
128   grpc_closure on_write_response_done;
129   grpc_closure on_client_read_done;
130   grpc_closure on_client_write_done;
131   grpc_closure on_server_read_done;
132   grpc_closure on_server_write_done;
133 
134   bool client_read_failed : 1;
135   bool client_write_failed : 1;
136   bool client_shutdown : 1;
137   bool server_read_failed : 1;
138   bool server_write_failed : 1;
139   bool server_shutdown : 1;
140 
141   grpc_slice_buffer client_read_buffer;
142   grpc_slice_buffer client_deferred_write_buffer;
143   bool client_is_writing;
144   grpc_slice_buffer client_write_buffer;
145   grpc_slice_buffer server_read_buffer;
146   grpc_slice_buffer server_deferred_write_buffer;
147   bool server_is_writing;
148   grpc_slice_buffer server_write_buffer;
149 
150   grpc_http_parser http_parser;
151   grpc_http_request http_request;
152 } proxy_connection;
153 
proxy_connection_ref(proxy_connection * conn,const char *)154 static void proxy_connection_ref(proxy_connection* conn,
155                                  const char* /*reason*/) {
156   gpr_ref(&conn->refcount);
157 }
158 
159 // Helper function to destroy the proxy connection.
proxy_connection_unref(proxy_connection * conn,const char *)160 static void proxy_connection_unref(proxy_connection* conn,
161                                    const char* /*reason*/) {
162   if (gpr_unref(&conn->refcount)) {
163     grpc_endpoint_destroy(conn->client_endpoint);
164     if (conn->server_endpoint != nullptr) {
165       grpc_endpoint_destroy(conn->server_endpoint);
166     }
167     grpc_pollset_set_destroy(conn->pollset_set);
168     grpc_slice_buffer_destroy(&conn->client_read_buffer);
169     grpc_slice_buffer_destroy(&conn->client_deferred_write_buffer);
170     grpc_slice_buffer_destroy(&conn->client_write_buffer);
171     grpc_slice_buffer_destroy(&conn->server_read_buffer);
172     grpc_slice_buffer_destroy(&conn->server_deferred_write_buffer);
173     grpc_slice_buffer_destroy(&conn->server_write_buffer);
174     grpc_http_parser_destroy(&conn->http_parser);
175     grpc_http_request_destroy(&conn->http_request);
176     proxy_unref(conn->proxy);
177     gpr_free(conn);
178   }
179 }
180 
181 enum failure_type {
182   SETUP_FAILED,  // To be used before we start proxying.
183   CLIENT_READ_FAILED,
184   CLIENT_WRITE_FAILED,
185   SERVER_READ_FAILED,
186   SERVER_WRITE_FAILED,
187 };
188 
189 // Forward declarations
190 static void on_client_write_done(void* arg, grpc_error_handle error);
191 static void on_server_write_done(void* arg, grpc_error_handle error);
192 static void on_client_read_done(void* arg, grpc_error_handle error);
193 static void on_server_read_done(void* arg, grpc_error_handle error);
194 static void on_server_connect_done(void* arg, grpc_error_handle error);
195 static void on_read_request_done(void* arg, grpc_error_handle error);
196 
197 static void on_client_write_done_locked(void* arg, grpc_error_handle error);
198 static void on_server_write_done_locked(void* arg, grpc_error_handle error);
199 static void on_client_read_done_locked(void* arg, grpc_error_handle error);
200 static void on_server_read_done_locked(void* arg, grpc_error_handle error);
201 static void on_server_connect_done_locked(void* arg, grpc_error_handle error);
202 static void on_read_request_done_locked(void* arg, grpc_error_handle error);
203 
204 // Helper function to shut down the proxy connection.
proxy_connection_failed(proxy_connection * conn,failure_type failure,const char * prefix,grpc_error_handle error)205 static void proxy_connection_failed(proxy_connection* conn,
206                                     failure_type failure, const char* prefix,
207                                     grpc_error_handle error) {
208   gpr_log(GPR_INFO, "%s: %s", prefix, grpc_core::StatusToString(error).c_str());
209   // Decide whether we should shut down the client and server.
210   bool shutdown_client = false;
211   bool shutdown_server = false;
212   if (failure == SETUP_FAILED) {
213     shutdown_client = true;
214     shutdown_server = true;
215   } else {
216     if ((failure == CLIENT_READ_FAILED && conn->client_write_failed) ||
217         (failure == CLIENT_WRITE_FAILED && conn->client_read_failed) ||
218         (failure == SERVER_READ_FAILED && !conn->client_is_writing)) {
219       shutdown_client = true;
220     }
221     if ((failure == SERVER_READ_FAILED && conn->server_write_failed) ||
222         (failure == SERVER_WRITE_FAILED && conn->server_read_failed) ||
223         (failure == CLIENT_READ_FAILED && !conn->server_is_writing)) {
224       shutdown_server = true;
225     }
226   }
227   // If we decided to shut down either one and have not yet done so, do so.
228   if (shutdown_client && !conn->client_shutdown) {
229     grpc_endpoint_shutdown(conn->client_endpoint, error);
230     conn->client_shutdown = true;
231   }
232   if (shutdown_server && !conn->server_shutdown &&
233       (conn->server_endpoint != nullptr)) {
234     grpc_endpoint_shutdown(conn->server_endpoint, error);
235     conn->server_shutdown = true;
236   }
237   // Unref the connection.
238   proxy_connection_unref(conn, "conn_failed");
239 }
240 
241 // Callback for writing proxy data to the client.
on_client_write_done_locked(void * arg,grpc_error_handle error)242 static void on_client_write_done_locked(void* arg, grpc_error_handle error) {
243   proxy_connection* conn = static_cast<proxy_connection*>(arg);
244   conn->client_is_writing = false;
245   if (!error.ok()) {
246     conn->client_write_failed = true;
247     proxy_connection_failed(conn, CLIENT_WRITE_FAILED,
248                             "HTTP proxy client write", error);
249     return;
250   }
251   if (conn->server_read_failed) {
252     grpc_endpoint_shutdown(conn->client_endpoint,
253                            absl::UnknownError("Client shutdown"));
254     // No more writes.  Unref the connection.
255     proxy_connection_unref(conn, "client_write");
256     return;
257   }
258   // Clear write buffer (the data we just wrote).
259   grpc_slice_buffer_reset_and_unref(&conn->client_write_buffer);
260   // If more data was read from the server since we started this write,
261   // write that data now.
262   if (conn->client_deferred_write_buffer.length > 0) {
263     grpc_slice_buffer_move_into(&conn->client_deferred_write_buffer,
264                                 &conn->client_write_buffer);
265     conn->client_is_writing = true;
266     GRPC_CLOSURE_INIT(&conn->on_client_write_done, on_client_write_done, conn,
267                       grpc_schedule_on_exec_ctx);
268     grpc_endpoint_write(conn->client_endpoint, &conn->client_write_buffer,
269                         &conn->on_client_write_done, nullptr,
270                         /*max_frame_size=*/INT_MAX);
271   } else {
272     // No more writes.  Unref the connection.
273     proxy_connection_unref(conn, "write_done");
274   }
275 }
276 
on_client_write_done(void * arg,grpc_error_handle error)277 static void on_client_write_done(void* arg, grpc_error_handle error) {
278   proxy_connection* conn = static_cast<proxy_connection*>(arg);
279   GRPC_CLOSURE_INIT(&conn->on_client_write_done, on_client_write_done_locked,
280                     conn, nullptr);
281   conn->proxy->combiner->Run(&conn->on_client_write_done, error);
282 }
283 
284 // Callback for writing proxy data to the backend server.
on_server_write_done_locked(void * arg,grpc_error_handle error)285 static void on_server_write_done_locked(void* arg, grpc_error_handle error) {
286   proxy_connection* conn = static_cast<proxy_connection*>(arg);
287   conn->server_is_writing = false;
288   if (!error.ok()) {
289     conn->server_write_failed = true;
290     proxy_connection_failed(conn, SERVER_WRITE_FAILED,
291                             "HTTP proxy server write", error);
292     return;
293   }
294 
295   if (conn->client_read_failed) {
296     grpc_endpoint_shutdown(conn->server_endpoint,
297                            absl::UnknownError("Server shutdown"));
298     // No more writes.  Unref the connection.
299     proxy_connection_unref(conn, "server_write");
300     return;
301   }
302   // Clear write buffer (the data we just wrote).
303   grpc_slice_buffer_reset_and_unref(&conn->server_write_buffer);
304   // If more data was read from the client since we started this write,
305   // write that data now.
306   if (conn->server_deferred_write_buffer.length > 0) {
307     grpc_slice_buffer_move_into(&conn->server_deferred_write_buffer,
308                                 &conn->server_write_buffer);
309     conn->server_is_writing = true;
310     GRPC_CLOSURE_INIT(&conn->on_server_write_done, on_server_write_done, conn,
311                       grpc_schedule_on_exec_ctx);
312     grpc_endpoint_write(conn->server_endpoint, &conn->server_write_buffer,
313                         &conn->on_server_write_done, nullptr,
314                         /*max_frame_size=*/INT_MAX);
315   } else {
316     // No more writes.  Unref the connection.
317     proxy_connection_unref(conn, "server_write");
318   }
319 }
320 
on_server_write_done(void * arg,grpc_error_handle error)321 static void on_server_write_done(void* arg, grpc_error_handle error) {
322   proxy_connection* conn = static_cast<proxy_connection*>(arg);
323   GRPC_CLOSURE_INIT(&conn->on_server_write_done, on_server_write_done_locked,
324                     conn, nullptr);
325   conn->proxy->combiner->Run(&conn->on_server_write_done, error);
326 }
327 
328 // Callback for reading data from the client, which will be proxied to
329 // the backend server.
on_client_read_done_locked(void * arg,grpc_error_handle error)330 static void on_client_read_done_locked(void* arg, grpc_error_handle error) {
331   proxy_connection* conn = static_cast<proxy_connection*>(arg);
332   if (!error.ok()) {
333     conn->client_read_failed = true;
334     proxy_connection_failed(conn, CLIENT_READ_FAILED, "HTTP proxy client read",
335                             error);
336     return;
337   }
338   // If there is already a pending write (i.e., server_write_buffer is
339   // not empty), then move the read data into server_deferred_write_buffer,
340   // and the next write will be requested in on_server_write_done(), when
341   // the current write is finished.
342   //
343   // Otherwise, move the read data into the write buffer and write it.
344   if (conn->server_is_writing) {
345     grpc_slice_buffer_move_into(&conn->client_read_buffer,
346                                 &conn->server_deferred_write_buffer);
347   } else {
348     grpc_slice_buffer_move_into(&conn->client_read_buffer,
349                                 &conn->server_write_buffer);
350     proxy_connection_ref(conn, "client_read");
351     conn->server_is_writing = true;
352     GRPC_CLOSURE_INIT(&conn->on_server_write_done, on_server_write_done, conn,
353                       grpc_schedule_on_exec_ctx);
354     grpc_endpoint_write(conn->server_endpoint, &conn->server_write_buffer,
355                         &conn->on_server_write_done, nullptr,
356                         /*max_frame_size=*/INT_MAX);
357   }
358   // Read more data.
359   GRPC_CLOSURE_INIT(&conn->on_client_read_done, on_client_read_done, conn,
360                     grpc_schedule_on_exec_ctx);
361   grpc_endpoint_read(conn->client_endpoint, &conn->client_read_buffer,
362                      &conn->on_client_read_done, /*urgent=*/false,
363                      /*min_progress_size=*/1);
364 }
365 
on_client_read_done(void * arg,grpc_error_handle error)366 static void on_client_read_done(void* arg, grpc_error_handle error) {
367   proxy_connection* conn = static_cast<proxy_connection*>(arg);
368   GRPC_CLOSURE_INIT(&conn->on_client_read_done, on_client_read_done_locked,
369                     conn, nullptr);
370   conn->proxy->combiner->Run(&conn->on_client_read_done, error);
371 }
372 
373 // Callback for reading data from the backend server, which will be
374 // proxied to the client.
on_server_read_done_locked(void * arg,grpc_error_handle error)375 static void on_server_read_done_locked(void* arg, grpc_error_handle error) {
376   proxy_connection* conn = static_cast<proxy_connection*>(arg);
377   if (!error.ok()) {
378     conn->server_read_failed = true;
379     proxy_connection_failed(conn, SERVER_READ_FAILED, "HTTP proxy server read",
380                             error);
381     return;
382   }
383   // If there is already a pending write (i.e., client_write_buffer is
384   // not empty), then move the read data into client_deferred_write_buffer,
385   // and the next write will be requested in on_client_write_done(), when
386   // the current write is finished.
387   //
388   // Otherwise, move the read data into the write buffer and write it.
389   if (conn->client_is_writing) {
390     grpc_slice_buffer_move_into(&conn->server_read_buffer,
391                                 &conn->client_deferred_write_buffer);
392   } else {
393     grpc_slice_buffer_move_into(&conn->server_read_buffer,
394                                 &conn->client_write_buffer);
395     proxy_connection_ref(conn, "server_read");
396     conn->client_is_writing = true;
397     GRPC_CLOSURE_INIT(&conn->on_client_write_done, on_client_write_done, conn,
398                       grpc_schedule_on_exec_ctx);
399     grpc_endpoint_write(conn->client_endpoint, &conn->client_write_buffer,
400                         &conn->on_client_write_done, nullptr,
401                         /*max_frame_size=*/INT_MAX);
402   }
403   // Read more data.
404   GRPC_CLOSURE_INIT(&conn->on_server_read_done, on_server_read_done, conn,
405                     grpc_schedule_on_exec_ctx);
406   grpc_endpoint_read(conn->server_endpoint, &conn->server_read_buffer,
407                      &conn->on_server_read_done, /*urgent=*/false,
408                      /*min_progress_size=*/1);
409 }
410 
on_server_read_done(void * arg,grpc_error_handle error)411 static void on_server_read_done(void* arg, grpc_error_handle error) {
412   proxy_connection* conn = static_cast<proxy_connection*>(arg);
413   GRPC_CLOSURE_INIT(&conn->on_server_read_done, on_server_read_done_locked,
414                     conn, nullptr);
415   conn->proxy->combiner->Run(&conn->on_server_read_done, error);
416 }
417 
418 // Callback to write the HTTP response for the CONNECT request.
on_write_response_done_locked(void * arg,grpc_error_handle error)419 static void on_write_response_done_locked(void* arg, grpc_error_handle error) {
420   proxy_connection* conn = static_cast<proxy_connection*>(arg);
421   conn->client_is_writing = false;
422   if (!error.ok()) {
423     proxy_connection_failed(conn, SETUP_FAILED, "HTTP proxy write response",
424                             error);
425     return;
426   }
427   // Clear write buffer.
428   grpc_slice_buffer_reset_and_unref(&conn->client_write_buffer);
429   // Start reading from both client and server.  One of the read
430   // requests inherits our ref to conn, but we need to take a new ref
431   // for the other one.
432   proxy_connection_ref(conn, "client_read");
433   proxy_connection_ref(conn, "server_read");
434   proxy_connection_unref(conn, "write_response");
435   GRPC_CLOSURE_INIT(&conn->on_client_read_done, on_client_read_done, conn,
436                     grpc_schedule_on_exec_ctx);
437   grpc_endpoint_read(conn->client_endpoint, &conn->client_read_buffer,
438                      &conn->on_client_read_done, /*urgent=*/false,
439                      /*min_progress_size=*/1);
440   GRPC_CLOSURE_INIT(&conn->on_server_read_done, on_server_read_done, conn,
441                     grpc_schedule_on_exec_ctx);
442   grpc_endpoint_read(conn->server_endpoint, &conn->server_read_buffer,
443                      &conn->on_server_read_done, /*urgent=*/false,
444                      /*min_progress_size=*/1);
445 }
446 
on_write_response_done(void * arg,grpc_error_handle error)447 static void on_write_response_done(void* arg, grpc_error_handle error) {
448   proxy_connection* conn = static_cast<proxy_connection*>(arg);
449   GRPC_CLOSURE_INIT(&conn->on_write_response_done,
450                     on_write_response_done_locked, conn, nullptr);
451   conn->proxy->combiner->Run(&conn->on_write_response_done, error);
452 }
453 
454 // Callback to connect to the backend server specified by the HTTP
455 // CONNECT request.
on_server_connect_done_locked(void * arg,grpc_error_handle error)456 static void on_server_connect_done_locked(void* arg, grpc_error_handle error) {
457   proxy_connection* conn = static_cast<proxy_connection*>(arg);
458   if (!error.ok()) {
459     // TODO(roth): Technically, in this case, we should handle the error
460     // by returning an HTTP response to the client indicating that the
461     // connection failed.  However, for the purposes of this test code,
462     // it's fine to pretend this is a client-side error, which will
463     // cause the client connection to be dropped.
464     proxy_connection_failed(conn, SETUP_FAILED, "HTTP proxy server connect",
465                             error);
466     return;
467   }
468   // We've established a connection, so send back a 200 response code to
469   // the client.
470   // The write callback inherits our reference to conn.
471   grpc_slice slice =
472       grpc_slice_from_copied_string("HTTP/1.0 200 connected\r\n\r\n");
473   grpc_slice_buffer_add(&conn->client_write_buffer, slice);
474   conn->client_is_writing = true;
475   GRPC_CLOSURE_INIT(&conn->on_write_response_done, on_write_response_done, conn,
476                     grpc_schedule_on_exec_ctx);
477   grpc_endpoint_write(conn->client_endpoint, &conn->client_write_buffer,
478                       &conn->on_write_response_done, nullptr,
479                       /*max_frame_size=*/INT_MAX);
480 }
481 
on_server_connect_done(void * arg,grpc_error_handle error)482 static void on_server_connect_done(void* arg, grpc_error_handle error) {
483   proxy_connection* conn = static_cast<proxy_connection*>(arg);
484   GRPC_CLOSURE_INIT(&conn->on_server_connect_done,
485                     on_server_connect_done_locked, conn, nullptr);
486   conn->proxy->combiner->Run(&conn->on_server_connect_done, error);
487 }
488 
489 ///
490 /// Parses the proxy auth header value to check if it matches :-
491 /// Basic <base64_encoded_expected_cred>
492 /// Returns true if it matches, false otherwise
493 ///
proxy_auth_header_matches(absl::string_view proxy_auth_header_val,absl::string_view expected_cred)494 static bool proxy_auth_header_matches(absl::string_view proxy_auth_header_val,
495                                       absl::string_view expected_cred) {
496   if (!absl::ConsumePrefix(&proxy_auth_header_val, "Basic ")) {
497     return false;
498   }
499   std::string decoded;
500   absl::Base64Unescape(proxy_auth_header_val, &decoded);
501   return expected_cred == decoded;
502 }
503 
504 // Callback to read the HTTP CONNECT request.
505 // TODO(roth): Technically, for any of the failure modes handled by this
506 // function, we should handle the error by returning an HTTP response to
507 // the client indicating that the request failed.  However, for the purposes
508 // of this test code, it's fine to pretend this is a client-side error,
509 // which will cause the client connection to be dropped.
on_read_request_done_locked(void * arg,grpc_error_handle error)510 static void on_read_request_done_locked(void* arg, grpc_error_handle error) {
511   proxy_connection* conn = static_cast<proxy_connection*>(arg);
512   gpr_log(GPR_DEBUG, "on_read_request_done: %p %s", conn,
513           grpc_core::StatusToString(error).c_str());
514   if (!error.ok()) {
515     proxy_connection_failed(conn, SETUP_FAILED, "HTTP proxy read request",
516                             error);
517     return;
518   }
519   // Read request and feed it to the parser.
520   for (size_t i = 0; i < conn->client_read_buffer.count; ++i) {
521     if (GRPC_SLICE_LENGTH(conn->client_read_buffer.slices[i]) > 0) {
522       error = grpc_http_parser_parse(
523           &conn->http_parser, conn->client_read_buffer.slices[i], nullptr);
524       if (!error.ok()) {
525         proxy_connection_failed(conn, SETUP_FAILED, "HTTP proxy request parse",
526                                 error);
527         return;
528       }
529     }
530   }
531   grpc_slice_buffer_reset_and_unref(&conn->client_read_buffer);
532   // If we're not done reading the request, read more data.
533   if (conn->http_parser.state != GRPC_HTTP_BODY) {
534     GRPC_CLOSURE_INIT(&conn->on_read_request_done, on_read_request_done, conn,
535                       grpc_schedule_on_exec_ctx);
536     grpc_endpoint_read(conn->client_endpoint, &conn->client_read_buffer,
537                        &conn->on_read_request_done, /*urgent=*/false,
538                        /*min_progress_size=*/1);
539     return;
540   }
541   // Make sure we got a CONNECT request.
542   if (strcmp(conn->http_request.method, "CONNECT") != 0) {
543     error = GRPC_ERROR_CREATE(absl::StrCat("HTTP proxy got request method ",
544                                            conn->http_request.method));
545     proxy_connection_failed(conn, SETUP_FAILED, "HTTP proxy read request",
546                             error);
547     return;
548   }
549   // If proxy auth is being used, check if the header is present and as expected
550   const grpc_arg* proxy_auth_arg = grpc_channel_args_find(
551       conn->proxy->channel_args, GRPC_ARG_HTTP_PROXY_AUTH_CREDS);
552   char* proxy_auth_str = grpc_channel_arg_get_string(proxy_auth_arg);
553   if (proxy_auth_str != nullptr) {
554     bool client_authenticated = false;
555     for (size_t i = 0; i < conn->http_request.hdr_count; i++) {
556       if (strcmp(conn->http_request.hdrs[i].key, "Proxy-Authorization") == 0) {
557         client_authenticated = proxy_auth_header_matches(
558             conn->http_request.hdrs[i].value, proxy_auth_str);
559         break;
560       }
561     }
562     if (!client_authenticated) {
563       const char* msg = "HTTP Connect could not verify authentication";
564       error = GRPC_ERROR_CREATE(msg);
565       proxy_connection_failed(conn, SETUP_FAILED, "HTTP proxy read request",
566                               error);
567       return;
568     }
569   }
570   // Resolve address.
571   absl::StatusOr<std::vector<grpc_resolved_address>> addresses_or =
572       grpc_core::GetDNSResolver()->LookupHostnameBlocking(
573           conn->http_request.path, "80");
574   if (!addresses_or.ok()) {
575     proxy_connection_failed(conn, SETUP_FAILED, "HTTP proxy DNS lookup", error);
576     return;
577   }
578   GPR_ASSERT(!addresses_or->empty());
579   // Connect to requested address.
580   // The connection callback inherits our reference to conn.
581   const grpc_core::Timestamp deadline =
582       grpc_core::Timestamp::Now() + grpc_core::Duration::Seconds(10);
583   GRPC_CLOSURE_INIT(&conn->on_server_connect_done, on_server_connect_done, conn,
584                     grpc_schedule_on_exec_ctx);
585   auto args = grpc_core::CoreConfiguration::Get()
586                   .channel_args_preconditioning()
587                   .PreconditionChannelArgs(nullptr);
588   grpc_tcp_client_connect(
589       &conn->on_server_connect_done, &conn->server_endpoint, conn->pollset_set,
590       grpc_event_engine::experimental::ChannelArgsEndpointConfig(args),
591       &(*addresses_or)[0], deadline);
592 }
593 
on_read_request_done(void * arg,grpc_error_handle error)594 static void on_read_request_done(void* arg, grpc_error_handle error) {
595   proxy_connection* conn = static_cast<proxy_connection*>(arg);
596   GRPC_CLOSURE_INIT(&conn->on_read_request_done, on_read_request_done_locked,
597                     conn, nullptr);
598   conn->proxy->combiner->Run(&conn->on_read_request_done, error);
599 }
600 
on_accept(void * arg,grpc_endpoint * endpoint,grpc_pollset *,grpc_tcp_server_acceptor * acceptor)601 static void on_accept(void* arg, grpc_endpoint* endpoint,
602                       grpc_pollset* /*accepting_pollset*/,
603                       grpc_tcp_server_acceptor* acceptor) {
604   gpr_free(acceptor);
605   if (proxy_destroyed.load()) {
606     grpc_endpoint_shutdown(endpoint, absl::UnknownError("proxy shutdown"));
607     grpc_endpoint_destroy(endpoint);
608     return;
609   }
610   grpc_end2end_http_proxy* proxy = static_cast<grpc_end2end_http_proxy*>(arg);
611   proxy_ref(proxy);
612   if (proxy->is_shutdown.load()) {
613     grpc_endpoint_shutdown(endpoint, absl::UnknownError("proxy shutdown"));
614     grpc_endpoint_destroy(endpoint);
615     proxy_unref(proxy);
616     return;
617   }
618   // Instantiate proxy_connection.
619   proxy_connection* conn = grpc_core::Zalloc<proxy_connection>();
620   conn->client_endpoint = endpoint;
621   conn->proxy = proxy;
622   gpr_ref_init(&conn->refcount, 1);
623   conn->pollset_set = grpc_pollset_set_create();
624   grpc_pollset_set_add_pollset(conn->pollset_set, proxy->pollset[0]);
625   grpc_endpoint_add_to_pollset_set(endpoint, conn->pollset_set);
626   grpc_slice_buffer_init(&conn->client_read_buffer);
627   grpc_slice_buffer_init(&conn->client_deferred_write_buffer);
628   conn->client_is_writing = false;
629   grpc_slice_buffer_init(&conn->client_write_buffer);
630   grpc_slice_buffer_init(&conn->server_read_buffer);
631   grpc_slice_buffer_init(&conn->server_deferred_write_buffer);
632   conn->server_is_writing = false;
633   grpc_slice_buffer_init(&conn->server_write_buffer);
634   grpc_http_parser_init(&conn->http_parser, GRPC_HTTP_REQUEST,
635                         &conn->http_request);
636   GRPC_CLOSURE_INIT(&conn->on_read_request_done, on_read_request_done, conn,
637                     grpc_schedule_on_exec_ctx);
638   grpc_endpoint_read(conn->client_endpoint, &conn->client_read_buffer,
639                      &conn->on_read_request_done, /*urgent=*/false,
640                      /*min_progress_size=*/1);
641 }
642 
643 //
644 // Proxy class
645 //
646 
thread_main(void * arg)647 static void thread_main(void* arg) {
648   grpc_end2end_http_proxy* proxy = static_cast<grpc_end2end_http_proxy*>(arg);
649   grpc_core::ExecCtx exec_ctx;
650   do {
651     proxy_ref(proxy);
652     grpc_pollset_worker* worker = nullptr;
653     gpr_mu_lock(proxy->mu);
654     GRPC_LOG_IF_ERROR(
655         "grpc_pollset_work",
656         grpc_pollset_work(proxy->pollset[0], &worker,
657                           grpc_core::Timestamp::Now() +
658                               grpc_core::Duration::Milliseconds(100)));
659     gpr_mu_unlock(proxy->mu);
660     grpc_core::ExecCtx::Get()->Flush();
661   } while (proxy_unref(proxy) > 1 || !proxy->is_shutdown.load());
662 }
663 
grpc_end2end_http_proxy_create(const grpc_channel_args * args)664 grpc_end2end_http_proxy* grpc_end2end_http_proxy_create(
665     const grpc_channel_args* args) {
666   grpc_core::ExecCtx exec_ctx;
667   grpc_end2end_http_proxy* proxy = new grpc_end2end_http_proxy();
668   proxy_destroyed.store(false);
669   // Construct proxy address.
670   const int proxy_port = grpc_pick_unused_port_or_die();
671   proxy->proxy_name = grpc_core::JoinHostPort("localhost", proxy_port);
672   gpr_log(GPR_INFO, "Proxy address: %s", proxy->proxy_name.c_str());
673   // Create TCP server.
674   auto channel_args = grpc_core::CoreConfiguration::Get()
675                           .channel_args_preconditioning()
676                           .PreconditionChannelArgs(args);
677   proxy->channel_args = channel_args.ToC().release();
678   grpc_error_handle error = grpc_tcp_server_create(
679       nullptr,
680       grpc_event_engine::experimental::ChannelArgsEndpointConfig(channel_args),
681       on_accept, proxy, &proxy->server);
682   GPR_ASSERT(error.ok());
683   // Bind to port.
684   grpc_resolved_address resolved_addr;
685   grpc_sockaddr_in* addr =
686       reinterpret_cast<grpc_sockaddr_in*>(resolved_addr.addr);
687   memset(&resolved_addr, 0, sizeof(resolved_addr));
688   resolved_addr.len = sizeof(grpc_sockaddr_in);
689   addr->sin_family = GRPC_AF_INET;
690   grpc_sockaddr_set_port(&resolved_addr, proxy_port);
691   int port;
692   error = grpc_tcp_server_add_port(proxy->server, &resolved_addr, &port);
693   GPR_ASSERT(error.ok());
694   GPR_ASSERT(port == proxy_port);
695   // Start server.
696   auto* pollset = static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
697   grpc_pollset_init(pollset, &proxy->mu);
698   proxy->pollset.push_back(pollset);
699   grpc_tcp_server_start(proxy->server, &proxy->pollset);
700 
701   // Start proxy thread.
702   proxy->thd = grpc_core::Thread("grpc_http_proxy", thread_main, proxy);
703   proxy->thd.Start();
704   return proxy;
705 }
706 
destroy_pollset(void * arg,grpc_error_handle)707 static void destroy_pollset(void* arg, grpc_error_handle /*error*/) {
708   grpc_pollset* pollset = static_cast<grpc_pollset*>(arg);
709   grpc_pollset_destroy(pollset);
710   gpr_free(pollset);
711 }
712 
grpc_end2end_http_proxy_destroy(grpc_end2end_http_proxy * proxy)713 void grpc_end2end_http_proxy_destroy(grpc_end2end_http_proxy* proxy) {
714   proxy->is_shutdown.store(true);
715   grpc_core::ExecCtx exec_ctx;
716   proxy->thd.Join();
717   grpc_tcp_server_shutdown_listeners(proxy->server);
718   grpc_tcp_server_unref(proxy->server);
719   grpc_channel_args_destroy(proxy->channel_args);
720   grpc_pollset_shutdown(proxy->pollset[0],
721                         GRPC_CLOSURE_CREATE(destroy_pollset, proxy->pollset[0],
722                                             grpc_schedule_on_exec_ctx));
723   proxy_unref(proxy);
724 }
725 
grpc_end2end_http_proxy_get_proxy_name(grpc_end2end_http_proxy * proxy)726 const char* grpc_end2end_http_proxy_get_proxy_name(
727     grpc_end2end_http_proxy* proxy) {
728   return proxy->proxy_name.c_str();
729 }
730