Add support for CONNECT to httpcli code and flesh out handshaker implementation.

pull/7611/head
Mark D. Roth 8 years ago
parent 9136bb1c1c
commit f7250197a7
  1. 78
      src/core/ext/client_config/http_connect_handshaker.c
  2. 22
      src/core/lib/http/format_request.c
  3. 2
      src/core/lib/http/format_request.h
  4. 6
      src/core/lib/http/httpcli.h
  5. 8
      src/core/lib/http/parser.c

@ -36,6 +36,8 @@
#include <grpc/impl/codegen/alloc.h>
#include <grpc/impl/codegen/log.h>
#include "src/core/lib/http/format_request.h"
#include "src/core/lib/http/parser.h"
#include "src/core/ext/client_config/http_connect_handshaker.h"
typedef struct http_connect_handshaker {
@ -53,35 +55,65 @@ typedef struct http_connect_handshaker {
grpc_closure request_done_closure;
grpc_slice_buffer response_buffer;
grpc_closure response_read_closure;
grpc_http_parser http_parser;
grpc_http_response http_response;
} http_connect_handshaker;
// Callback invoked for reading HTTP CONNECT response.
static void on_read_done(grpc_exec_ctx* exec_ctx, void* arg,
grpc_error* error) {
http_connect_handshaker* h = arg;
// FIXME: process response; on failure, figure out how to abort
// Invoke handshake-done callback.
h->cb(exec_ctx, h->endpoint, h->args, h->user_data);
}
// Callback invoked when finished writing HTTP CONNECT request.
static void on_write_done(grpc_exec_ctx* exec_ctx, void* arg,
grpc_error* error) {
http_connect_handshaker* h = arg;
// Read HTTP CONNECT response.
gpr_slice_buffer_init(&h->response_buffer);
grpc_closure_init(&h->response_read_closure, on_read_done, h);
grpc_endpoint_read(exec_ctx, h->endpoint, &h->response_buffer,
&h->response_read_closure);
}
// Callback invoked for reading HTTP CONNECT response.
static void on_read_done(grpc_exec_ctx* exec_ctx, void* arg,
grpc_error* error) {
http_connect_handshaker* h = arg;
if (error == GRPC_ERROR_NONE) {
for (size_t i = 0; i < h->response_buffer.count; ++i) {
if (GPR_SLICE_LENGTH(h->response_buffer.slices[i]) > 0) {
error = grpc_http_parser_parse(
&h->http_parser, h->response_buffer.slices[i]);
if (error != GRPC_ERROR_NONE)
goto done;
}
}
// If we're not done reading the response, read more data.
// TODO(roth): In practice, I suspect that the response to a CONNECT
// request will never include a body, in which case this check is
// sufficient. However, the language of RFC-2817 doesn't explicitly
// forbid the response from including a body. If there is a body,
// it's possible that we might have parsed part but not all of the
// body, in which case this check will cause us to fail to parse the
// remainder of the body. If that ever becomes an issue, we may
// need to fix the HTTP parser to understand when the body is
// complete (e.g., handling chunked transfer encoding or looking
// at the Content-Length: header).
if (h->http_parser->state != GRPC_HTTP_BODY) {
grpc_endpoint_read(exec_ctx, h->endpoint, &h->response_buffer,
&h->response_read_closure);
return;
}
}
done:
// Invoke handshake-done callback.
// FIXME: pass error to callback
h->cb(exec_ctx, h->endpoint, h->args, h->user_data);
}
//
// Public handshaker methods
//
static void http_connect_handshaker_destroy(grpc_exec_ctx* exec_ctx,
grpc_handshaker* handshaker) {
grpc_slice_buffer_destroy(&handshaker->request_buffer);
grpc_slice_buffer_destroy(&handshaker->response_buffer);
grpc_http_parser_destroy(&handshaker->http_parser);
grpc_http_response_destroy(&handshaker->http_response);
gpr_free(handshaker);
}
@ -100,14 +132,24 @@ static void http_connect_handshaker_do_handshake(
h->args = args;
h->cb = cb;
h->user_data = user_data;
// Send HTTP CONNECT request.
// Initialize fields.
gpr_slice_buffer_init(&h->request_buffer);
gpr_slice_buffer_add(&h->request_buffer, "HTTP CONNECT ");
// FIXME: get server name from somewhere...
gpr_slice_buffer_add(&h->request_buffer, WHEE);
// FIXME: add headers as needed?
gpr_slice_buffer_add(&h->request_buffer, "\n\n");
grpc_closure_init(&h->request_done_closure, on_write_done, h);
gpr_slice_buffer_init(&h->response_buffer);
grpc_closure_init(&h->response_read_closure, on_read_done, h);
grpc_http_parser_init(&h->http_parser, GRPC_HTTP_RESPONSE,
&h->http_response);
// Send HTTP CONNECT request.
grpc_httpcli_request request;
memset(&request, 0, sizeof(request));
// FIXME: get proxy name from somewhere...
request.host = gpr_strdup("");
request.http.method = gpr_strdup("CONNECT");
// FIXME: get server name from somewhere...
request.http.path = gpr_strdup("");
request.handshaker = grpc_httpcli_plaintext;
gpr_slice request_slice = grpc_httpcli_format_connect_request(request);
gpr_slice_buffer_add(&h->request_buffer, request_slice);
grpc_endpoint_write(exec_ctx, endpoint, &h->request_buffer,
&h->request_done_closure);
}

@ -44,7 +44,7 @@
#include "src/core/lib/support/string.h"
static void fill_common_header(const grpc_httpcli_request *request,
gpr_strvec *buf) {
gpr_strvec *buf, bool connection_close) {
size_t i;
gpr_strvec_add(buf, gpr_strdup(request->http.path));
gpr_strvec_add(buf, gpr_strdup(" HTTP/1.0\r\n"));
@ -52,7 +52,8 @@ static void fill_common_header(const grpc_httpcli_request *request,
gpr_strvec_add(buf, gpr_strdup("Host: "));
gpr_strvec_add(buf, gpr_strdup(request->host));
gpr_strvec_add(buf, gpr_strdup("\r\n"));
gpr_strvec_add(buf, gpr_strdup("Connection: close\r\n"));
if (connection_close)
gpr_strvec_add(buf, gpr_strdup("Connection: close\r\n"));
gpr_strvec_add(buf,
gpr_strdup("User-Agent: " GRPC_HTTPCLI_USER_AGENT "\r\n"));
/* user supplied headers */
@ -71,7 +72,7 @@ gpr_slice grpc_httpcli_format_get_request(const grpc_httpcli_request *request) {
gpr_strvec_init(&out);
gpr_strvec_add(&out, gpr_strdup("GET "));
fill_common_header(request, &out);
fill_common_header(request, &out, true);
gpr_strvec_add(&out, gpr_strdup("\r\n"));
flat = gpr_strvec_flatten(&out, &flat_len);
@ -91,7 +92,7 @@ gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request,
gpr_strvec_init(&out);
gpr_strvec_add(&out, gpr_strdup("POST "));
fill_common_header(request, &out);
fill_common_header(request, &out, true);
if (body_bytes) {
uint8_t has_content_type = 0;
for (i = 0; i < request->http.hdr_count; i++) {
@ -118,3 +119,16 @@ gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request,
return gpr_slice_new(tmp, out_len, gpr_free);
}
gpr_slice grpc_httpcli_format_connect_request(
const grpc_httpcli_request *request) {
gpr_strvec out;
gpr_strvec_init(&out);
gpr_strvec_add(&out, gpr_strdup("CONNECT "));
fill_common_header(request, &out, false);
gpr_strvec_add(&out, gpr_strdup("\r\n"));
size_t flat_len;
char *flat = gpr_strvec_flatten(&out, &flat_len);
gpr_strvec_destroy(&out);
return gpr_slice_new(flat, flat_len, gpr_free);
}

@ -41,5 +41,7 @@ gpr_slice grpc_httpcli_format_get_request(const grpc_httpcli_request *request);
gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request,
const char *body_bytes,
size_t body_size);
gpr_slice grpc_httpcli_format_connect_request(
const grpc_httpcli_request *request);
#endif /* GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H */

@ -93,8 +93,7 @@ void grpc_httpcli_context_destroy(grpc_httpcli_context *context);
'request' contains request parameters - these are caller owned and can be
destroyed once the call returns
'deadline' contains a deadline for the request (or gpr_inf_future)
'on_response' is a callback to report results to (and 'user_data' is a user
supplied pointer to pass to said call) */
'on_response' is a callback to report results to */
void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
grpc_polling_entity *pollent,
const grpc_httpcli_request *request,
@ -113,8 +112,7 @@ void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
'deadline' contains a deadline for the request (or gpr_inf_future)
'em' points to a caller owned event manager that must be alive for the
lifetime of the request
'on_response' is a callback to report results to (and 'user_data' is a user
supplied pointer to pass to said call)
'on_response' is a callback to report results to
Does not support ?var1=val1&var2=val2 in the path. */
void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
grpc_polling_entity *pollent,

@ -282,20 +282,18 @@ static grpc_error *addbyte(grpc_http_parser *parser, uint8_t byte) {
if (grpc_http1_trace)
gpr_log(GPR_ERROR, "HTTP client max line length (%d) exceeded",
GRPC_HTTP_PARSER_MAX_HEADER_LENGTH);
return 0;
return GRPC_ERROR_NONE;
}
parser->cur_line[parser->cur_line_length] = byte;
parser->cur_line_length++;
if (check_line(parser)) {
return finish_line(parser);
} else {
return GRPC_ERROR_NONE;
}
GPR_UNREACHABLE_CODE(return 0);
return GRPC_ERROR_NONE;
case GRPC_HTTP_BODY:
return addbyte_body(parser, byte);
}
GPR_UNREACHABLE_CODE(return 0);
GPR_UNREACHABLE_CODE(return GRPC_ERROR_NONE);
}
void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type,

Loading…
Cancel
Save