diff options
Diffstat (limited to 'src/core/lib/http')
-rw-r--r-- | src/core/lib/http/httpcli.c | 123 | ||||
-rw-r--r-- | src/core/lib/http/httpcli.h | 21 | ||||
-rw-r--r-- | src/core/lib/http/httpcli_security_connector.c | 6 | ||||
-rw-r--r-- | src/core/lib/http/parser.c | 246 | ||||
-rw-r--r-- | src/core/lib/http/parser.c.orig | 357 | ||||
-rw-r--r-- | src/core/lib/http/parser.h | 17 |
6 files changed, 579 insertions, 191 deletions
diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c index e8957bfe89..18135bcb58 100644 --- a/src/core/lib/http/httpcli.c +++ b/src/core/lib/http/httpcli.c @@ -39,12 +39,14 @@ #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include <grpc/support/string_util.h> +#include <grpc/support/useful.h> #include "src/core/lib/http/format_request.h" #include "src/core/lib/http/parser.h" #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/iomgr/iomgr_internal.h" #include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" #include "src/core/lib/iomgr/tcp_client.h" #include "src/core/lib/support/string.h" @@ -59,8 +61,7 @@ typedef struct { gpr_timespec deadline; int have_read_byte; const grpc_httpcli_handshaker *handshaker; - grpc_httpcli_response_cb on_response; - void *user_data; + grpc_closure *on_done; grpc_httpcli_context *context; grpc_polling_entity *pollent; grpc_iomgr_object iomgr_obj; @@ -69,6 +70,7 @@ typedef struct { grpc_closure on_read; grpc_closure done_write; grpc_closure connected; + grpc_error *overall_error; } internal_request; static grpc_httpcli_get_override g_get_override = NULL; @@ -76,6 +78,7 @@ static grpc_httpcli_post_override g_post_override = NULL; static void plaintext_handshake(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint, const char *host, + gpr_timespec deadline, void (*on_done)(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint)) { @@ -93,14 +96,14 @@ void grpc_httpcli_context_destroy(grpc_httpcli_context *context) { grpc_pollset_set_destroy(context->pollset_set); } -static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req); +static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req, + grpc_error *due_to_error); static void finish(grpc_exec_ctx *exec_ctx, internal_request *req, - int success) { + grpc_error *error) { grpc_polling_entity_del_from_pollset_set(exec_ctx, req->pollent, req->context->pollset_set); - req->on_response(exec_ctx, req->user_data, - success ? &req->parser.http.response : NULL); + grpc_exec_ctx_sched(exec_ctx, req->on_done, error, NULL); grpc_http_parser_destroy(&req->parser); if (req->addresses != NULL) { grpc_resolved_addresses_destroy(req->addresses); @@ -114,39 +117,49 @@ static void finish(grpc_exec_ctx *exec_ctx, internal_request *req, grpc_iomgr_unregister_object(&req->iomgr_obj); gpr_slice_buffer_destroy(&req->incoming); gpr_slice_buffer_destroy(&req->outgoing); + GRPC_ERROR_UNREF(req->overall_error); gpr_free(req); } -static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, bool success); +static void append_error(internal_request *req, grpc_error *error) { + if (req->overall_error == GRPC_ERROR_NONE) { + req->overall_error = GRPC_ERROR_CREATE("Failed HTTP/1 client request"); + } + grpc_resolved_address *addr = &req->addresses->addrs[req->next_address - 1]; + char *addr_text = grpc_sockaddr_to_uri((struct sockaddr *)addr->addr); + req->overall_error = grpc_error_add_child( + req->overall_error, + grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, addr_text)); + gpr_free(addr_text); +} static void do_read(grpc_exec_ctx *exec_ctx, internal_request *req) { grpc_endpoint_read(exec_ctx, req->ep, &req->incoming, &req->on_read); } -static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, bool success) { +static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, + grpc_error *error) { internal_request *req = user_data; size_t i; for (i = 0; i < req->incoming.count; i++) { if (GPR_SLICE_LENGTH(req->incoming.slices[i])) { req->have_read_byte = 1; - if (!grpc_http_parser_parse(&req->parser, req->incoming.slices[i])) { - finish(exec_ctx, req, 0); + grpc_error *err = + grpc_http_parser_parse(&req->parser, req->incoming.slices[i]); + if (err != GRPC_ERROR_NONE) { + finish(exec_ctx, req, err); return; } } } - if (success) { + if (error == GRPC_ERROR_NONE) { do_read(exec_ctx, req); } else if (!req->have_read_byte) { - next_address(exec_ctx, req); + next_address(exec_ctx, req, GRPC_ERROR_REF(error)); } else { - int parse_success = grpc_http_parser_eof(&req->parser); - if (parse_success && (req->parser.type != GRPC_HTTP_RESPONSE)) { - parse_success = 0; - } - finish(exec_ctx, req, parse_success); + finish(exec_ctx, req, grpc_http_parser_eof(&req->parser)); } } @@ -154,12 +167,12 @@ static void on_written(grpc_exec_ctx *exec_ctx, internal_request *req) { do_read(exec_ctx, req); } -static void done_write(grpc_exec_ctx *exec_ctx, void *arg, bool success) { +static void done_write(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { internal_request *req = arg; - if (success) { + if (error == GRPC_ERROR_NONE) { on_written(exec_ctx, req); } else { - next_address(exec_ctx, req); + next_address(exec_ctx, req, GRPC_ERROR_REF(error)); } } @@ -174,7 +187,8 @@ static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, internal_request *req = arg; if (!ep) { - next_address(exec_ctx, req); + next_address(exec_ctx, req, + GRPC_ERROR_CREATE("Unexplained handshake failure")); return; } @@ -182,23 +196,30 @@ static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, start_write(exec_ctx, req); } -static void on_connected(grpc_exec_ctx *exec_ctx, void *arg, bool success) { +static void on_connected(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { internal_request *req = arg; if (!req->ep) { - next_address(exec_ctx, req); + next_address(exec_ctx, req, GRPC_ERROR_REF(error)); return; } req->handshaker->handshake( exec_ctx, req, req->ep, req->ssl_host_override ? req->ssl_host_override : req->host, - on_handshake_done); + req->deadline, on_handshake_done); } -static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req) { +static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req, + grpc_error *error) { grpc_resolved_address *addr; + if (error != GRPC_ERROR_NONE) { + append_error(req, error); + } if (req->next_address == req->addresses->naddrs) { - finish(exec_ctx, req, 0); + finish(exec_ctx, req, + GRPC_ERROR_CREATE_REFERENCING("Failed HTTP requests to all targets", + &req->overall_error, 1)); return; } addr = &req->addresses->addrs[req->next_address++]; @@ -208,34 +229,34 @@ static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req) { (struct sockaddr *)&addr->addr, addr->len, req->deadline); } -static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg, - grpc_resolved_addresses *addresses) { +static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { internal_request *req = arg; - if (!addresses) { - finish(exec_ctx, req, 0); + if (error != GRPC_ERROR_NONE) { + finish(exec_ctx, req, error); return; } - req->addresses = addresses; req->next_address = 0; - next_address(exec_ctx, req); + next_address(exec_ctx, req, GRPC_ERROR_NONE); } -static void internal_request_begin( - grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, - grpc_polling_entity *pollent, const grpc_httpcli_request *request, - gpr_timespec deadline, grpc_httpcli_response_cb on_response, - void *user_data, const char *name, gpr_slice request_text) { +static void internal_request_begin(grpc_exec_ctx *exec_ctx, + grpc_httpcli_context *context, + grpc_polling_entity *pollent, + const grpc_httpcli_request *request, + gpr_timespec deadline, grpc_closure *on_done, + grpc_httpcli_response *response, + const char *name, gpr_slice request_text) { internal_request *req = gpr_malloc(sizeof(internal_request)); memset(req, 0, sizeof(*req)); req->request_text = request_text; - grpc_http_parser_init(&req->parser); - req->on_response = on_response; - req->user_data = user_data; + grpc_http_parser_init(&req->parser, GRPC_HTTP_RESPONSE, response); + req->on_done = on_done; req->deadline = deadline; req->handshaker = request->handshaker ? request->handshaker : &grpc_httpcli_plaintext; req->context = context; req->pollent = pollent; + req->overall_error = GRPC_ERROR_NONE; grpc_closure_init(&req->on_read, on_read, req); grpc_closure_init(&req->done_write, done_write, req); gpr_slice_buffer_init(&req->incoming); @@ -248,22 +269,22 @@ static void internal_request_begin( grpc_polling_entity_add_to_pollset_set(exec_ctx, req->pollent, req->context->pollset_set); grpc_resolve_address(exec_ctx, request->host, req->handshaker->default_port, - on_resolved, req); + grpc_closure_create(on_resolved, req), &req->addresses); } void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, grpc_polling_entity *pollent, const grpc_httpcli_request *request, - gpr_timespec deadline, - grpc_httpcli_response_cb on_response, void *user_data) { + gpr_timespec deadline, grpc_closure *on_done, + grpc_httpcli_response *response) { char *name; if (g_get_override && - g_get_override(exec_ctx, request, deadline, on_response, user_data)) { + g_get_override(exec_ctx, request, deadline, on_done, response)) { return; } gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->http.path); - internal_request_begin(exec_ctx, context, pollent, request, deadline, - on_response, user_data, name, + internal_request_begin(exec_ctx, context, pollent, request, deadline, on_done, + response, name, grpc_httpcli_format_get_request(request)); gpr_free(name); } @@ -272,18 +293,18 @@ void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, grpc_polling_entity *pollent, const grpc_httpcli_request *request, const char *body_bytes, size_t body_size, - gpr_timespec deadline, - grpc_httpcli_response_cb on_response, void *user_data) { + gpr_timespec deadline, grpc_closure *on_done, + grpc_httpcli_response *response) { char *name; if (g_post_override && g_post_override(exec_ctx, request, body_bytes, body_size, deadline, - on_response, user_data)) { + on_done, response)) { return; } gpr_asprintf(&name, "HTTP:POST:%s:%s", request->host, request->http.path); internal_request_begin( - exec_ctx, context, pollent, request, deadline, on_response, user_data, - name, grpc_httpcli_format_post_request(request, body_bytes, body_size)); + exec_ctx, context, pollent, request, deadline, on_done, response, name, + grpc_httpcli_format_post_request(request, body_bytes, body_size)); gpr_free(name); } diff --git a/src/core/lib/http/httpcli.h b/src/core/lib/http/httpcli.h index 7e7784f1ab..662e176f4c 100644 --- a/src/core/lib/http/httpcli.h +++ b/src/core/lib/http/httpcli.h @@ -57,7 +57,7 @@ typedef struct grpc_httpcli_context { typedef struct { const char *default_port; void (*handshake)(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint, - const char *host, + const char *host, gpr_timespec deadline, void (*on_done)(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint)); } grpc_httpcli_handshaker; @@ -82,11 +82,6 @@ typedef struct grpc_httpcli_request { /* Expose the parser response type as a httpcli response too */ typedef struct grpc_http_response grpc_httpcli_response; -/* Callback for grpc_httpcli_get and grpc_httpcli_post. */ -typedef void (*grpc_httpcli_response_cb)(grpc_exec_ctx *exec_ctx, - void *user_data, - const grpc_http_response *response); - void grpc_httpcli_context_init(grpc_httpcli_context *context); void grpc_httpcli_context_destroy(grpc_httpcli_context *context); @@ -103,8 +98,8 @@ void grpc_httpcli_context_destroy(grpc_httpcli_context *context); void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, grpc_polling_entity *pollent, const grpc_httpcli_request *request, - gpr_timespec deadline, - grpc_httpcli_response_cb on_response, void *user_data); + gpr_timespec deadline, grpc_closure *on_complete, + grpc_httpcli_response *response); /* Asynchronously perform a HTTP POST. 'context' specifies the http context under which to do the post @@ -125,19 +120,19 @@ void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, grpc_polling_entity *pollent, const grpc_httpcli_request *request, const char *body_bytes, size_t body_size, - gpr_timespec deadline, - grpc_httpcli_response_cb on_response, void *user_data); + gpr_timespec deadline, grpc_closure *on_complete, + grpc_httpcli_response *response); /* override functions return 1 if they handled the request, 0 otherwise */ typedef int (*grpc_httpcli_get_override)(grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request, gpr_timespec deadline, - grpc_httpcli_response_cb on_response, - void *user_data); + grpc_closure *on_complete, + grpc_httpcli_response *response); typedef int (*grpc_httpcli_post_override)( grpc_exec_ctx *exec_ctx, const grpc_httpcli_request *request, const char *body_bytes, size_t body_size, gpr_timespec deadline, - grpc_httpcli_response_cb on_response, void *user_data); + grpc_closure *on_complete, grpc_httpcli_response *response); void grpc_httpcli_set_override(grpc_httpcli_get_override get, grpc_httpcli_post_override post); diff --git a/src/core/lib/http/httpcli_security_connector.c b/src/core/lib/http/httpcli_security_connector.c index 5590928968..a57d93bb7b 100644 --- a/src/core/lib/http/httpcli_security_connector.c +++ b/src/core/lib/http/httpcli_security_connector.c @@ -61,6 +61,7 @@ static void httpcli_ssl_destroy(grpc_security_connector *sc) { static void httpcli_ssl_do_handshake(grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, grpc_endpoint *nonsecure_endpoint, + gpr_timespec deadline, grpc_security_handshake_done_cb cb, void *user_data) { grpc_httpcli_ssl_channel_security_connector *c = @@ -79,7 +80,7 @@ static void httpcli_ssl_do_handshake(grpc_exec_ctx *exec_ctx, cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL); } else { grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, true, - nonsecure_endpoint, cb, user_data); + nonsecure_endpoint, deadline, cb, user_data); } } @@ -163,6 +164,7 @@ static void on_secure_transport_setup_done(grpc_exec_ctx *exec_ctx, void *rp, static void ssl_handshake(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *tcp, const char *host, + gpr_timespec deadline, void (*on_done)(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint)) { grpc_channel_security_connector *sc = NULL; @@ -181,7 +183,7 @@ static void ssl_handshake(grpc_exec_ctx *exec_ctx, void *arg, pem_root_certs, pem_root_certs_size, host, &sc) == GRPC_SECURITY_OK); grpc_channel_security_connector_do_handshake( - exec_ctx, sc, tcp, on_secure_transport_setup_done, c); + exec_ctx, sc, tcp, deadline, on_secure_transport_setup_done, c); GRPC_SECURITY_CONNECTOR_UNREF(&sc->base, "httpcli"); } diff --git a/src/core/lib/http/parser.c b/src/core/lib/http/parser.c index 09b2ed40d1..92ed08ae51 100644 --- a/src/core/lib/http/parser.c +++ b/src/core/lib/http/parser.c @@ -48,37 +48,38 @@ static char *buf2str(void *buffer, size_t length) { return out; } -static int handle_response_line(grpc_http_parser *parser) { +static grpc_error *handle_response_line(grpc_http_parser *parser) { uint8_t *beg = parser->cur_line; uint8_t *cur = beg; uint8_t *end = beg + parser->cur_line_length; - if (cur == end || *cur++ != 'H') goto error; - if (cur == end || *cur++ != 'T') goto error; - if (cur == end || *cur++ != 'T') goto error; - if (cur == end || *cur++ != 'P') goto error; - if (cur == end || *cur++ != '/') goto error; - if (cur == end || *cur++ != '1') goto error; - if (cur == end || *cur++ != '.') goto error; - if (cur == end || *cur < '0' || *cur++ > '1') goto error; - if (cur == end || *cur++ != ' ') goto error; - if (cur == end || *cur < '1' || *cur++ > '9') goto error; - if (cur == end || *cur < '0' || *cur++ > '9') goto error; - if (cur == end || *cur < '0' || *cur++ > '9') goto error; - parser->http.response.status = + if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'"); + if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'"); + if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'"); + if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'"); + if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'"); + if (cur == end || *cur++ != '1') return GRPC_ERROR_CREATE("Expected '1'"); + if (cur == end || *cur++ != '.') return GRPC_ERROR_CREATE("Expected '.'"); + if (cur == end || *cur < '0' || *cur++ > '1') { + return GRPC_ERROR_CREATE("Expected HTTP/1.0 or HTTP/1.1"); + } + if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '"); + if (cur == end || *cur < '1' || *cur++ > '9') + return GRPC_ERROR_CREATE("Expected status code"); + if (cur == end || *cur < '0' || *cur++ > '9') + return GRPC_ERROR_CREATE("Expected status code"); + if (cur == end || *cur < '0' || *cur++ > '9') + return GRPC_ERROR_CREATE("Expected status code"); + parser->http.response->status = (cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0'); - if (cur == end || *cur++ != ' ') goto error; + if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '"); /* we don't really care about the status code message */ - return 1; - -error: - if (grpc_http1_trace) gpr_log(GPR_ERROR, "Failed parsing response line"); - return 0; + return GRPC_ERROR_NONE; } -static int handle_request_line(grpc_http_parser *parser) { +static grpc_error *handle_request_line(grpc_http_parser *parser) { uint8_t *beg = parser->cur_line; uint8_t *cur = beg; uint8_t *end = beg + parser->cur_line_length; @@ -87,84 +88,81 @@ static int handle_request_line(grpc_http_parser *parser) { while (cur != end && *cur++ != ' ') ; - if (cur == end) goto error; - parser->http.request.method = buf2str(beg, (size_t)(cur - beg - 1)); + if (cur == end) return GRPC_ERROR_CREATE("No method on HTTP request line"); + parser->http.request->method = buf2str(beg, (size_t)(cur - beg - 1)); beg = cur; while (cur != end && *cur++ != ' ') ; - if (cur == end) goto error; - parser->http.request.path = buf2str(beg, (size_t)(cur - beg - 1)); - - if (cur == end || *cur++ != 'H') goto error; - if (cur == end || *cur++ != 'T') goto error; - if (cur == end || *cur++ != 'T') goto error; - if (cur == end || *cur++ != 'P') goto error; - if (cur == end || *cur++ != '/') goto error; + if (cur == end) return GRPC_ERROR_CREATE("No path on HTTP request line"); + parser->http.request->path = buf2str(beg, (size_t)(cur - beg - 1)); + + if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'"); + if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'"); + if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'"); + if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'"); + if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'"); vers_major = (uint8_t)(*cur++ - '1' + 1); ++cur; - if (cur == end) goto error; + if (cur == end) + return GRPC_ERROR_CREATE("End of line in HTTP version string"); vers_minor = (uint8_t)(*cur++ - '1' + 1); if (vers_major == 1) { if (vers_minor == 0) { - parser->http.request.version = GRPC_HTTP_HTTP10; + parser->http.request->version = GRPC_HTTP_HTTP10; } else if (vers_minor == 1) { - parser->http.request.version = GRPC_HTTP_HTTP11; + parser->http.request->version = GRPC_HTTP_HTTP11; } else { - goto error; + return GRPC_ERROR_CREATE( + "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); } } else if (vers_major == 2) { if (vers_minor == 0) { - parser->http.request.version = GRPC_HTTP_HTTP20; + parser->http.request->version = GRPC_HTTP_HTTP20; } else { - goto error; + return GRPC_ERROR_CREATE( + "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); } } else { - goto error; + return GRPC_ERROR_CREATE("Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); } - return 1; - -error: - if (grpc_http1_trace) gpr_log(GPR_ERROR, "Failed parsing request line"); - return 0; + return GRPC_ERROR_NONE; } -static int handle_first_line(grpc_http_parser *parser) { - if (parser->cur_line[0] == 'H') { - parser->type = GRPC_HTTP_RESPONSE; - return handle_response_line(parser); - } else { - parser->type = GRPC_HTTP_REQUEST; - return handle_request_line(parser); +static grpc_error *handle_first_line(grpc_http_parser *parser) { + switch (parser->type) { + case GRPC_HTTP_REQUEST: + return handle_request_line(parser); + case GRPC_HTTP_RESPONSE: + return handle_response_line(parser); } + GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here")); } -static int add_header(grpc_http_parser *parser) { +static grpc_error *add_header(grpc_http_parser *parser) { uint8_t *beg = parser->cur_line; uint8_t *cur = beg; uint8_t *end = beg + parser->cur_line_length; size_t *hdr_count = NULL; grpc_http_header **hdrs = NULL; grpc_http_header hdr = {NULL, NULL}; + grpc_error *error = GRPC_ERROR_NONE; GPR_ASSERT(cur != end); if (*cur == ' ' || *cur == '\t') { - if (grpc_http1_trace) - gpr_log(GPR_ERROR, "Continued header lines not supported yet"); - goto error; + error = GRPC_ERROR_CREATE("Continued header lines not supported yet"); + goto done; } while (cur != end && *cur != ':') { cur++; } if (cur == end) { - if (grpc_http1_trace) { - gpr_log(GPR_ERROR, "Didn't find ':' in header string"); - } - goto error; + error = GRPC_ERROR_CREATE("Didn't find ':' in header string"); + goto done; } GPR_ASSERT(cur >= beg); hdr.key = buf2str(beg, (size_t)(cur - beg)); @@ -176,14 +174,15 @@ static int add_header(grpc_http_parser *parser) { GPR_ASSERT((size_t)(end - cur) >= parser->cur_line_end_length); hdr.value = buf2str(cur, (size_t)(end - cur) - parser->cur_line_end_length); - if (parser->type == GRPC_HTTP_RESPONSE) { - hdr_count = &parser->http.response.hdr_count; - hdrs = &parser->http.response.hdrs; - } else if (parser->type == GRPC_HTTP_REQUEST) { - hdr_count = &parser->http.request.hdr_count; - hdrs = &parser->http.request.hdrs; - } else { - return 0; + switch (parser->type) { + case GRPC_HTTP_RESPONSE: + hdr_count = &parser->http.response->hdr_count; + hdrs = &parser->http.response->hdrs; + break; + case GRPC_HTTP_REQUEST: + hdr_count = &parser->http.request->hdr_count; + hdrs = &parser->http.request->hdrs; + break; } if (*hdr_count == parser->hdr_capacity) { @@ -192,20 +191,21 @@ static int add_header(grpc_http_parser *parser) { *hdrs = gpr_realloc(*hdrs, parser->hdr_capacity * sizeof(**hdrs)); } (*hdrs)[(*hdr_count)++] = hdr; - return 1; -error: - gpr_free(hdr.key); - gpr_free(hdr.value); - return 0; +done: + if (error != GRPC_ERROR_NONE) { + gpr_free(hdr.key); + gpr_free(hdr.value); + } + return error; } -static int finish_line(grpc_http_parser *parser) { +static grpc_error *finish_line(grpc_http_parser *parser) { + grpc_error *err; switch (parser->state) { case GRPC_HTTP_FIRST_LINE: - if (!handle_first_line(parser)) { - return 0; - } + err = handle_first_line(parser); + if (err != GRPC_ERROR_NONE) return err; parser->state = GRPC_HTTP_HEADERS; break; case GRPC_HTTP_HEADERS: @@ -213,30 +213,31 @@ static int finish_line(grpc_http_parser *parser) { parser->state = GRPC_HTTP_BODY; break; } - if (!add_header(parser)) { - return 0; + err = add_header(parser); + if (err != GRPC_ERROR_NONE) { + return err; } break; case GRPC_HTTP_BODY: - GPR_UNREACHABLE_CODE(return 0); + GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here")); } parser->cur_line_length = 0; - return 1; + return GRPC_ERROR_NONE; } -static int addbyte_body(grpc_http_parser *parser, uint8_t byte) { +static grpc_error *addbyte_body(grpc_http_parser *parser, uint8_t byte) { size_t *body_length = NULL; char **body = NULL; if (parser->type == GRPC_HTTP_RESPONSE) { - body_length = &parser->http.response.body_length; - body = &parser->http.response.body; + body_length = &parser->http.response->body_length; + body = &parser->http.response->body; } else if (parser->type == GRPC_HTTP_REQUEST) { - body_length = &parser->http.request.body_length; - body = &parser->http.request.body; + body_length = &parser->http.request->body_length; + body = &parser->http.request->body; } else { - return 0; + GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here")); } if (*body_length == parser->body_capacity) { @@ -246,34 +247,34 @@ static int addbyte_body(grpc_http_parser *parser, uint8_t byte) { (*body)[*body_length] = (char)byte; (*body_length)++; - return 1; + return GRPC_ERROR_NONE; } -static int check_line(grpc_http_parser *parser) { +static bool check_line(grpc_http_parser *parser) { if (parser->cur_line_length >= 2 && parser->cur_line[parser->cur_line_length - 2] == '\r' && parser->cur_line[parser->cur_line_length - 1] == '\n') { - return 1; + return true; } // HTTP request with \n\r line termiantors. else if (parser->cur_line_length >= 2 && parser->cur_line[parser->cur_line_length - 2] == '\n' && parser->cur_line[parser->cur_line_length - 1] == '\r') { - return 1; + return true; } // HTTP request with only \n line terminators. else if (parser->cur_line_length >= 1 && parser->cur_line[parser->cur_line_length - 1] == '\n') { parser->cur_line_end_length = 1; - return 1; + return true; } - return 0; + return false; } -static int addbyte(grpc_http_parser *parser, uint8_t byte) { +static grpc_error *addbyte(grpc_http_parser *parser, uint8_t byte) { switch (parser->state) { case GRPC_HTTP_FIRST_LINE: case GRPC_HTTP_HEADERS: @@ -288,7 +289,7 @@ static int addbyte(grpc_http_parser *parser, uint8_t byte) { if (check_line(parser)) { return finish_line(parser); } else { - return 1; + return GRPC_ERROR_NONE; } GPR_UNREACHABLE_CODE(return 0); case GRPC_HTTP_BODY: @@ -297,46 +298,53 @@ static int addbyte(grpc_http_parser *parser, uint8_t byte) { GPR_UNREACHABLE_CODE(return 0); } -void grpc_http_parser_init(grpc_http_parser *parser) { +void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type, + void *request_or_response) { memset(parser, 0, sizeof(*parser)); parser->state = GRPC_HTTP_FIRST_LINE; - parser->type = GRPC_HTTP_UNKNOWN; + parser->type = type; + parser->http.request_or_response = request_or_response; parser->cur_line_end_length = 2; } -void grpc_http_parser_destroy(grpc_http_parser *parser) { +void grpc_http_parser_destroy(grpc_http_parser *parser) {} + +void grpc_http_request_destroy(grpc_http_request *request) { size_t i; - if (parser->type == GRPC_HTTP_RESPONSE) { - gpr_free(parser->http.response.body); - for (i = 0; i < parser->http.response.hdr_count; i++) { - gpr_free(parser->http.response.hdrs[i].key); - gpr_free(parser->http.response.hdrs[i].value); - } - gpr_free(parser->http.response.hdrs); - } else if (parser->type == GRPC_HTTP_REQUEST) { - gpr_free(parser->http.request.body); - for (i = 0; i < parser->http.request.hdr_count; i++) { - gpr_free(parser->http.request.hdrs[i].key); - gpr_free(parser->http.request.hdrs[i].value); - } - gpr_free(parser->http.request.hdrs); - gpr_free(parser->http.request.method); - gpr_free(parser->http.request.path); + gpr_free(request->body); + for (i = 0; i < request->hdr_count; i++) { + gpr_free(request->hdrs[i].key); + gpr_free(request->hdrs[i].value); } + gpr_free(request->hdrs); + gpr_free(request->method); + gpr_free(request->path); } -int grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice) { +void grpc_http_response_destroy(grpc_http_response *response) { + size_t i; + gpr_free(response->body); + for (i = 0; i < response->hdr_count; i++) { + gpr_free(response->hdrs[i].key); + gpr_free(response->hdrs[i].value); + } + gpr_free(response->hdrs); +} + +grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice) { size_t i; for (i = 0; i < GPR_SLICE_LENGTH(slice); i++) { - if (!addbyte(parser, GPR_SLICE_START_PTR(slice)[i])) { - return 0; - } + grpc_error *err = addbyte(parser, GPR_SLICE_START_PTR(slice)[i]); + if (err != GRPC_ERROR_NONE) return err; } - return 1; + return GRPC_ERROR_NONE; } -int grpc_http_parser_eof(grpc_http_parser *parser) { - return parser->state == GRPC_HTTP_BODY; +grpc_error *grpc_http_parser_eof(grpc_http_parser *parser) { + if (parser->state != GRPC_HTTP_BODY) { + return GRPC_ERROR_CREATE("Did not finish headers"); + } + return GRPC_ERROR_NONE; } diff --git a/src/core/lib/http/parser.c.orig b/src/core/lib/http/parser.c.orig new file mode 100644 index 0000000000..74d90fd8bf --- /dev/null +++ b/src/core/lib/http/parser.c.orig @@ -0,0 +1,357 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/lib/http/parser.h" + +#include <string.h> + +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/useful.h> + +int grpc_http1_trace = 0; + +static char *buf2str(void *buffer, size_t length) { + char *out = gpr_malloc(length + 1); + memcpy(out, buffer, length); + out[length] = 0; + return out; +} + +static grpc_error *handle_response_line(grpc_http_parser *parser) { + uint8_t *beg = parser->cur_line; + uint8_t *cur = beg; + uint8_t *end = beg + parser->cur_line_length; + + if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'"); + if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'"); + if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'"); + if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'"); + if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'"); + if (cur == end || *cur++ != '1') return GRPC_ERROR_CREATE("Expected '1'"); + if (cur == end || *cur++ != '.') return GRPC_ERROR_CREATE("Expected '.'"); + if (cur == end || *cur < '0' || *cur++ > '1') { + return GRPC_ERROR_CREATE("Expected HTTP/1.0 or HTTP/1.1"); + } + if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '"); + if (cur == end || *cur < '1' || *cur++ > '9') + return GRPC_ERROR_CREATE("Expected status code"); + if (cur == end || *cur < '0' || *cur++ > '9') + return GRPC_ERROR_CREATE("Expected status code"); + if (cur == end || *cur < '0' || *cur++ > '9') + return GRPC_ERROR_CREATE("Expected status code"); + parser->http.response->status = + (cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0'); + if (cur == end || *cur++ != ' ') return GRPC_ERROR_CREATE("Expected ' '"); + + /* we don't really care about the status code message */ + + return GRPC_ERROR_NONE; +} + +static grpc_error *handle_request_line(grpc_http_parser *parser) { + uint8_t *beg = parser->cur_line; + uint8_t *cur = beg; + uint8_t *end = beg + parser->cur_line_length; + uint8_t vers_major = 0; + uint8_t vers_minor = 0; + + while (cur != end && *cur++ != ' ') + ; + if (cur == end) return GRPC_ERROR_CREATE("No method on HTTP request line"); + parser->http.request->method = buf2str(beg, (size_t)(cur - beg - 1)); + + beg = cur; + while (cur != end && *cur++ != ' ') + ; + if (cur == end) return GRPC_ERROR_CREATE("No path on HTTP request line"); + parser->http.request->path = buf2str(beg, (size_t)(cur - beg - 1)); + + if (cur == end || *cur++ != 'H') return GRPC_ERROR_CREATE("Expected 'H'"); + if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'"); + if (cur == end || *cur++ != 'T') return GRPC_ERROR_CREATE("Expected 'T'"); + if (cur == end || *cur++ != 'P') return GRPC_ERROR_CREATE("Expected 'P'"); + if (cur == end || *cur++ != '/') return GRPC_ERROR_CREATE("Expected '/'"); + vers_major = (uint8_t)(*cur++ - '1' + 1); + ++cur; + if (cur == end) + return GRPC_ERROR_CREATE("End of line in HTTP version string"); + vers_minor = (uint8_t)(*cur++ - '1' + 1); + + if (vers_major == 1) { + if (vers_minor == 0) { + parser->http.request->version = GRPC_HTTP_HTTP10; + } else if (vers_minor == 1) { + parser->http.request->version = GRPC_HTTP_HTTP11; + } else { + return GRPC_ERROR_CREATE( + "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); + } + } else if (vers_major == 2) { + if (vers_minor == 0) { + parser->http.request->version = GRPC_HTTP_HTTP20; + } else { + return GRPC_ERROR_CREATE( + "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); + } + } else { + return GRPC_ERROR_CREATE("Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); + } + + return GRPC_ERROR_NONE; +} + +static grpc_error *handle_first_line(grpc_http_parser *parser) { + switch (parser->type) { + case GRPC_HTTP_REQUEST: + return handle_request_line(parser); + case GRPC_HTTP_RESPONSE: + return handle_response_line(parser); + } + GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here")); +} + +static grpc_error *add_header(grpc_http_parser *parser) { + uint8_t *beg = parser->cur_line; + uint8_t *cur = beg; + uint8_t *end = beg + parser->cur_line_length; + size_t *hdr_count = NULL; + grpc_http_header **hdrs = NULL; + grpc_http_header hdr = {NULL, NULL}; + grpc_error *error = GRPC_ERROR_NONE; + + GPR_ASSERT(cur != end); + + if (*cur == ' ' || *cur == '\t') { + error = GRPC_ERROR_CREATE("Continued header lines not supported yet"); + goto done; + } + + while (cur != end && *cur != ':') { + cur++; + } + if (cur == end) { +<<<<<<< HEAD + error = GRPC_ERROR_CREATE("Didn't find ':' in header string"); + goto done; +======= + if (grpc_http1_trace) { + gpr_log(GPR_ERROR, "Didn't find ':' in header string"); + } + goto error; +>>>>>>> a709afe241d8b264a1c326315f757b4a8d330207 + } + GPR_ASSERT(cur >= beg); + hdr.key = buf2str(beg, (size_t)(cur - beg)); + cur++; /* skip : */ + + while (cur != end && (*cur == ' ' || *cur == '\t')) { + cur++; + } + GPR_ASSERT((size_t)(end - cur) >= parser->cur_line_end_length); + hdr.value = buf2str(cur, (size_t)(end - cur) - parser->cur_line_end_length); + + switch (parser->type) { + case GRPC_HTTP_RESPONSE: + hdr_count = &parser->http.response->hdr_count; + hdrs = &parser->http.response->hdrs; + break; + case GRPC_HTTP_REQUEST: + hdr_count = &parser->http.request->hdr_count; + hdrs = &parser->http.request->hdrs; + break; + } + + if (*hdr_count == parser->hdr_capacity) { + parser->hdr_capacity = + GPR_MAX(parser->hdr_capacity + 1, parser->hdr_capacity * 3 / 2); + *hdrs = gpr_realloc(*hdrs, parser->hdr_capacity * sizeof(**hdrs)); + } + (*hdrs)[(*hdr_count)++] = hdr; + +done: + if (error != GRPC_ERROR_NONE) { + gpr_free(hdr.key); + gpr_free(hdr.value); + } + return error; +} + +static grpc_error *finish_line(grpc_http_parser *parser) { + grpc_error *err; + switch (parser->state) { + case GRPC_HTTP_FIRST_LINE: + err = handle_first_line(parser); + if (err != GRPC_ERROR_NONE) return err; + parser->state = GRPC_HTTP_HEADERS; + break; + case GRPC_HTTP_HEADERS: + if (parser->cur_line_length == parser->cur_line_end_length) { + parser->state = GRPC_HTTP_BODY; + break; + } + err = add_header(parser); + if (err != GRPC_ERROR_NONE) { + return err; + } + break; + case GRPC_HTTP_BODY: + GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here")); + } + + parser->cur_line_length = 0; + return GRPC_ERROR_NONE; +} + +static grpc_error *addbyte_body(grpc_http_parser *parser, uint8_t byte) { + size_t *body_length = NULL; + char **body = NULL; + + if (parser->type == GRPC_HTTP_RESPONSE) { + body_length = &parser->http.response->body_length; + body = &parser->http.response->body; + } else if (parser->type == GRPC_HTTP_REQUEST) { + body_length = &parser->http.request->body_length; + body = &parser->http.request->body; + } else { + GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE("Should never reach here")); + } + + if (*body_length == parser->body_capacity) { + parser->body_capacity = GPR_MAX(8, parser->body_capacity * 3 / 2); + *body = gpr_realloc((void *)*body, parser->body_capacity); + } + (*body)[*body_length] = (char)byte; + (*body_length)++; + + return GRPC_ERROR_NONE; +} + +static bool check_line(grpc_http_parser *parser) { + if (parser->cur_line_length >= 2 && + parser->cur_line[parser->cur_line_length - 2] == '\r' && + parser->cur_line[parser->cur_line_length - 1] == '\n') { + return true; + } + + // HTTP request with \n\r line termiantors. + else if (parser->cur_line_length >= 2 && + parser->cur_line[parser->cur_line_length - 2] == '\n' && + parser->cur_line[parser->cur_line_length - 1] == '\r') { + return true; + } + + // HTTP request with only \n line terminators. + else if (parser->cur_line_length >= 1 && + parser->cur_line[parser->cur_line_length - 1] == '\n') { + parser->cur_line_end_length = 1; + return true; + } + + return false; +} + +static grpc_error *addbyte(grpc_http_parser *parser, uint8_t byte) { + switch (parser->state) { + case GRPC_HTTP_FIRST_LINE: + case GRPC_HTTP_HEADERS: + if (parser->cur_line_length >= GRPC_HTTP_PARSER_MAX_HEADER_LENGTH) { + if (grpc_http1_trace) + gpr_log(GPR_ERROR, "HTTP client max line length (%d) exceeded", + GRPC_HTTP_PARSER_MAX_HEADER_LENGTH); + return 0; + } + 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); + case GRPC_HTTP_BODY: + return addbyte_body(parser, byte); + } + GPR_UNREACHABLE_CODE(return 0); +} + +void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type, + void *request_or_response) { + memset(parser, 0, sizeof(*parser)); + parser->state = GRPC_HTTP_FIRST_LINE; + parser->type = type; + parser->http.request_or_response = request_or_response; + parser->cur_line_end_length = 2; +} + +void grpc_http_parser_destroy(grpc_http_parser *parser) {} + +void grpc_http_request_destroy(grpc_http_request *request) { + size_t i; + gpr_free(request->body); + for (i = 0; i < request->hdr_count; i++) { + gpr_free(request->hdrs[i].key); + gpr_free(request->hdrs[i].value); + } + gpr_free(request->hdrs); + gpr_free(request->method); + gpr_free(request->path); +} + +void grpc_http_response_destroy(grpc_http_response *response) { + size_t i; + gpr_free(response->body); + for (i = 0; i < response->hdr_count; i++) { + gpr_free(response->hdrs[i].key); + gpr_free(response->hdrs[i].value); + } + gpr_free(response->hdrs); +} + +grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice) { + size_t i; + + for (i = 0; i < GPR_SLICE_LENGTH(slice); i++) { + grpc_error *err = addbyte(parser, GPR_SLICE_START_PTR(slice)[i]); + if (err != GRPC_ERROR_NONE) return err; + } + + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_http_parser_eof(grpc_http_parser *parser) { + if (parser->state != GRPC_HTTP_BODY) { + return GRPC_ERROR_CREATE("Did not finish headers"); + } + return GRPC_ERROR_NONE; +} diff --git a/src/core/lib/http/parser.h b/src/core/lib/http/parser.h index 536637e9a2..6df3cc8b13 100644 --- a/src/core/lib/http/parser.h +++ b/src/core/lib/http/parser.h @@ -36,6 +36,7 @@ #include <grpc/support/port_platform.h> #include <grpc/support/slice.h> +#include "src/core/lib/iomgr/error.h" /* Maximum length of a header string of the form 'Key: Value\r\n' */ #define GRPC_HTTP_PARSER_MAX_HEADER_LENGTH 4096 @@ -61,7 +62,6 @@ typedef enum { typedef enum { GRPC_HTTP_RESPONSE, GRPC_HTTP_REQUEST, - GRPC_HTTP_UNKNOWN } grpc_http_type; /* A request */ @@ -97,8 +97,9 @@ typedef struct { grpc_http_type type; union { - grpc_http_response response; - grpc_http_request request; + grpc_http_response *response; + grpc_http_request *request; + void *request_or_response; } http; size_t body_capacity; size_t hdr_capacity; @@ -108,11 +109,15 @@ typedef struct { size_t cur_line_end_length; } grpc_http_parser; -void grpc_http_parser_init(grpc_http_parser *parser); +void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type, + void *request_or_response); void grpc_http_parser_destroy(grpc_http_parser *parser); -int grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice); -int grpc_http_parser_eof(grpc_http_parser *parser); +grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice); +grpc_error *grpc_http_parser_eof(grpc_http_parser *parser); + +void grpc_http_request_destroy(grpc_http_request *request); +void grpc_http_response_destroy(grpc_http_response *response); extern int grpc_http1_trace; |