diff --git a/src/core/ext/client_channel/http_connect_handshaker.c b/src/core/ext/client_channel/http_connect_handshaker.c index 617f3f977b0..a4ff3797844 100644 --- a/src/core/ext/client_channel/http_connect_handshaker.c +++ b/src/core/ext/client_channel/http_connect_handshaker.c @@ -49,14 +49,12 @@ #include "src/core/lib/http/parser.h" #include "src/core/lib/slice/slice_internal.h" #include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" typedef struct http_connect_handshaker { // Base class. Must be first. grpc_handshaker base; - grpc_http_header* headers; - size_t num_headers; - gpr_refcount refcount; gpr_mu mu; @@ -90,11 +88,6 @@ static void http_connect_handshaker_unref(grpc_exec_ctx* exec_ctx, handshaker->read_buffer_to_destroy); gpr_free(handshaker->read_buffer_to_destroy); } - for (size_t i = 0; i < handshaker->num_headers; ++i) { - gpr_free(handshaker->headers[i].key); - gpr_free(handshaker->headers[i].value); - } - gpr_free(handshaker->headers); grpc_slice_buffer_destroy_internal(exec_ctx, &handshaker->write_buffer); grpc_http_parser_destroy(&handshaker->http_parser); grpc_http_response_destroy(&handshaker->http_response); @@ -282,26 +275,58 @@ static void http_connect_handshaker_do_handshake( grpc_closure_sched(exec_ctx, on_handshake_done, GRPC_ERROR_NONE); return; } + GPR_ASSERT(arg->type == GRPC_ARG_STRING); char* server_name = arg->value.string; + // Get headers from channel args. + arg = grpc_channel_args_find(args->args, GRPC_ARG_HTTP_CONNECT_HEADERS); + grpc_http_header* headers = NULL; + size_t num_headers = 0; + char** header_strings = NULL; + size_t num_header_strings = 0; + if (arg != NULL) { + GPR_ASSERT(arg->type == GRPC_ARG_STRING); + gpr_string_split(arg->value.string, "\n", &header_strings, + &num_header_strings); + headers = gpr_malloc(sizeof(grpc_http_header) * num_header_strings); + for (size_t i = 0; i < num_header_strings; ++i) { + char* sep = strchr(header_strings[i], ':'); + if (sep == NULL) { + gpr_log(GPR_ERROR, "skipping unparseable HTTP CONNECT header: %s", + header_strings[i]); + continue; + } + *sep = '\0'; + headers[num_headers].key = header_strings[i]; + headers[num_headers].value = sep + 1; + ++num_headers; + } + } // Save state in the handshaker object. gpr_mu_lock(&handshaker->mu); handshaker->args = args; handshaker->on_handshake_done = on_handshake_done; - // Send HTTP CONNECT request. + // Log connection via proxy. char* proxy_name = grpc_endpoint_get_peer(args->endpoint); gpr_log(GPR_INFO, "Connecting to server %s via HTTP proxy %s", server_name, proxy_name); gpr_free(proxy_name); + // Construct HTTP CONNECT request. grpc_httpcli_request request; memset(&request, 0, sizeof(request)); request.host = server_name; request.http.method = "CONNECT"; request.http.path = server_name; - request.http.hdrs = handshaker->headers; - request.http.hdr_count = handshaker->num_headers; + request.http.hdrs = headers; + request.http.hdr_count = num_headers; request.handshaker = &grpc_httpcli_plaintext; grpc_slice request_slice = grpc_httpcli_format_connect_request(&request); grpc_slice_buffer_add(&handshaker->write_buffer, request_slice); + // Clean up. + gpr_free(headers); + for (size_t i = 0; i < num_header_strings; ++i) { + gpr_free(header_strings[i]); + } + gpr_free(header_strings); // Take a new ref to be held by the write callback. gpr_ref(&handshaker->refcount); grpc_endpoint_write(exec_ctx, args->endpoint, &handshaker->write_buffer, @@ -313,21 +338,12 @@ static const grpc_handshaker_vtable http_connect_handshaker_vtable = { http_connect_handshaker_destroy, http_connect_handshaker_shutdown, http_connect_handshaker_do_handshake}; -grpc_handshaker* grpc_http_connect_handshaker_create(grpc_http_header* headers, - size_t num_headers) { +static grpc_handshaker* grpc_http_connect_handshaker_create() { http_connect_handshaker* handshaker = gpr_malloc(sizeof(*handshaker)); memset(handshaker, 0, sizeof(*handshaker)); grpc_handshaker_init(&http_connect_handshaker_vtable, &handshaker->base); gpr_mu_init(&handshaker->mu); gpr_ref_init(&handshaker->refcount, 1); - if (num_headers > 0) { - handshaker->headers = gpr_malloc(sizeof(grpc_http_header) * num_headers); - for (size_t i = 0; i < num_headers; ++i) { - handshaker->headers[i].key = gpr_strdup(headers[i].key); - handshaker->headers[i].value = gpr_strdup(headers[i].value); - } - handshaker->num_headers = num_headers; - } grpc_slice_buffer_init(&handshaker->write_buffer); grpc_closure_init(&handshaker->request_done_closure, on_write_done, handshaker, grpc_schedule_on_exec_ctx); @@ -370,7 +386,7 @@ static void handshaker_factory_add_handshakers( grpc_exec_ctx* exec_ctx, grpc_handshaker_factory* factory, const grpc_channel_args* args, grpc_handshake_manager* handshake_mgr) { grpc_handshake_manager_add(handshake_mgr, - grpc_http_connect_handshaker_create(NULL, 0)); + grpc_http_connect_handshaker_create()); } static void handshaker_factory_destroy(grpc_exec_ctx* exec_ctx, diff --git a/src/core/ext/client_channel/http_connect_handshaker.h b/src/core/ext/client_channel/http_connect_handshaker.h index ab981f338f4..a665bfc7e53 100644 --- a/src/core/ext/client_channel/http_connect_handshaker.h +++ b/src/core/ext/client_channel/http_connect_handshaker.h @@ -40,9 +40,10 @@ /// Channel arg indicating HTTP CONNECT server (string). #define GRPC_ARG_HTTP_CONNECT_SERVER "grpc.http_connect_server" -/// Creates a new HTTP CONNECT handshaker. -grpc_handshaker* grpc_http_connect_handshaker_create(grpc_http_header* headers, - size_t num_headers); +/// Channel arg indicating HTTP CONNECT headers (string). +/// Multiple headers are separated by newlines. Key/value pairs are +/// seperated by colons. +#define GRPC_ARG_HTTP_CONNECT_HEADERS "grpc.http_connect_headers" /// Returns the name of the proxy to use, or NULL if no proxy is configured. /// Caller takes ownership of result.