aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/lib/http
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/lib/http')
-rw-r--r--src/core/lib/http/format_request.c120
-rw-r--r--src/core/lib/http/format_request.h45
-rw-r--r--src/core/lib/http/httpcli.c293
-rw-r--r--src/core/lib/http/httpcli.h144
-rw-r--r--src/core/lib/http/httpcli_security_connector.c188
-rw-r--r--src/core/lib/http/parser.c313
-rw-r--r--src/core/lib/http/parser.h116
7 files changed, 1219 insertions, 0 deletions
diff --git a/src/core/lib/http/format_request.c b/src/core/lib/http/format_request.c
new file mode 100644
index 0000000000..95b3918646
--- /dev/null
+++ b/src/core/lib/http/format_request.c
@@ -0,0 +1,120 @@
+/*
+ *
+ * Copyright 2015-2016, 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/format_request.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/slice.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/support/string.h"
+
+static void fill_common_header(const grpc_httpcli_request *request,
+ gpr_strvec *buf) {
+ size_t i;
+ gpr_strvec_add(buf, gpr_strdup(request->http.path));
+ gpr_strvec_add(buf, gpr_strdup(" HTTP/1.0\r\n"));
+ /* just in case some crazy server really expects HTTP/1.1 */
+ 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"));
+ gpr_strvec_add(buf,
+ gpr_strdup("User-Agent: " GRPC_HTTPCLI_USER_AGENT "\r\n"));
+ /* user supplied headers */
+ for (i = 0; i < request->http.hdr_count; i++) {
+ gpr_strvec_add(buf, gpr_strdup(request->http.hdrs[i].key));
+ gpr_strvec_add(buf, gpr_strdup(": "));
+ gpr_strvec_add(buf, gpr_strdup(request->http.hdrs[i].value));
+ gpr_strvec_add(buf, gpr_strdup("\r\n"));
+ }
+}
+
+gpr_slice grpc_httpcli_format_get_request(const grpc_httpcli_request *request) {
+ gpr_strvec out;
+ char *flat;
+ size_t flat_len;
+
+ gpr_strvec_init(&out);
+ gpr_strvec_add(&out, gpr_strdup("GET "));
+ fill_common_header(request, &out);
+ gpr_strvec_add(&out, gpr_strdup("\r\n"));
+
+ flat = gpr_strvec_flatten(&out, &flat_len);
+ gpr_strvec_destroy(&out);
+
+ return gpr_slice_new(flat, flat_len, gpr_free);
+}
+
+gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request,
+ const char *body_bytes,
+ size_t body_size) {
+ gpr_strvec out;
+ char *tmp;
+ size_t out_len;
+ size_t i;
+
+ gpr_strvec_init(&out);
+
+ gpr_strvec_add(&out, gpr_strdup("POST "));
+ fill_common_header(request, &out);
+ if (body_bytes) {
+ uint8_t has_content_type = 0;
+ for (i = 0; i < request->http.hdr_count; i++) {
+ if (strcmp(request->http.hdrs[i].key, "Content-Type") == 0) {
+ has_content_type = 1;
+ break;
+ }
+ }
+ if (!has_content_type) {
+ gpr_strvec_add(&out, gpr_strdup("Content-Type: text/plain\r\n"));
+ }
+ gpr_asprintf(&tmp, "Content-Length: %lu\r\n", (unsigned long)body_size);
+ gpr_strvec_add(&out, tmp);
+ }
+ gpr_strvec_add(&out, gpr_strdup("\r\n"));
+ tmp = gpr_strvec_flatten(&out, &out_len);
+ gpr_strvec_destroy(&out);
+
+ if (body_bytes) {
+ tmp = gpr_realloc(tmp, out_len + body_size);
+ memcpy(tmp + out_len, body_bytes, body_size);
+ out_len += body_size;
+ }
+
+ return gpr_slice_new(tmp, out_len, gpr_free);
+}
diff --git a/src/core/lib/http/format_request.h b/src/core/lib/http/format_request.h
new file mode 100644
index 0000000000..2e933d804b
--- /dev/null
+++ b/src/core/lib/http/format_request.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2015-2016, 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H
+#define GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H
+
+#include <grpc/support/slice.h>
+#include "src/core/lib/http/httpcli.h"
+
+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);
+
+#endif /* GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H */
diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c
new file mode 100644
index 0000000000..aab28ad8b6
--- /dev/null
+++ b/src/core/lib/http/httpcli.c
@@ -0,0 +1,293 @@
+/*
+ *
+ * Copyright 2015-2016, 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/httpcli.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.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/tcp_client.h"
+#include "src/core/lib/support/string.h"
+
+typedef struct {
+ gpr_slice request_text;
+ grpc_http_parser parser;
+ grpc_resolved_addresses *addresses;
+ size_t next_address;
+ grpc_endpoint *ep;
+ char *host;
+ char *ssl_host_override;
+ gpr_timespec deadline;
+ int have_read_byte;
+ const grpc_httpcli_handshaker *handshaker;
+ grpc_httpcli_response_cb on_response;
+ void *user_data;
+ grpc_httpcli_context *context;
+ grpc_pollset *pollset;
+ grpc_iomgr_object iomgr_obj;
+ gpr_slice_buffer incoming;
+ gpr_slice_buffer outgoing;
+ grpc_closure on_read;
+ grpc_closure done_write;
+ grpc_closure connected;
+} internal_request;
+
+static grpc_httpcli_get_override g_get_override = NULL;
+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,
+ void (*on_done)(grpc_exec_ctx *exec_ctx,
+ void *arg,
+ grpc_endpoint *endpoint)) {
+ on_done(exec_ctx, arg, endpoint);
+}
+
+const grpc_httpcli_handshaker grpc_httpcli_plaintext = {"http",
+ plaintext_handshake};
+
+void grpc_httpcli_context_init(grpc_httpcli_context *context) {
+ context->pollset_set = grpc_pollset_set_create();
+}
+
+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 finish(grpc_exec_ctx *exec_ctx, internal_request *req,
+ int success) {
+ grpc_pollset_set_del_pollset(exec_ctx, req->context->pollset_set,
+ req->pollset);
+ req->on_response(exec_ctx, req->user_data,
+ success ? &req->parser.http.response : NULL);
+ grpc_http_parser_destroy(&req->parser);
+ if (req->addresses != NULL) {
+ grpc_resolved_addresses_destroy(req->addresses);
+ }
+ if (req->ep != NULL) {
+ grpc_endpoint_destroy(exec_ctx, req->ep);
+ }
+ gpr_slice_unref(req->request_text);
+ gpr_free(req->host);
+ gpr_free(req->ssl_host_override);
+ grpc_iomgr_unregister_object(&req->iomgr_obj);
+ gpr_slice_buffer_destroy(&req->incoming);
+ gpr_slice_buffer_destroy(&req->outgoing);
+ gpr_free(req);
+}
+
+static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, bool success);
+
+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) {
+ 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);
+ return;
+ }
+ }
+ }
+
+ if (success) {
+ do_read(exec_ctx, req);
+ } else if (!req->have_read_byte) {
+ next_address(exec_ctx, req);
+ } 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);
+ }
+}
+
+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) {
+ internal_request *req = arg;
+ if (success) {
+ on_written(exec_ctx, req);
+ } else {
+ next_address(exec_ctx, req);
+ }
+}
+
+static void start_write(grpc_exec_ctx *exec_ctx, internal_request *req) {
+ gpr_slice_ref(req->request_text);
+ gpr_slice_buffer_add(&req->outgoing, req->request_text);
+ grpc_endpoint_write(exec_ctx, req->ep, &req->outgoing, &req->done_write);
+}
+
+static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg,
+ grpc_endpoint *ep) {
+ internal_request *req = arg;
+
+ if (!ep) {
+ next_address(exec_ctx, req);
+ return;
+ }
+
+ req->ep = ep;
+ start_write(exec_ctx, req);
+}
+
+static void on_connected(grpc_exec_ctx *exec_ctx, void *arg, bool success) {
+ internal_request *req = arg;
+
+ if (!req->ep) {
+ next_address(exec_ctx, req);
+ return;
+ }
+ req->handshaker->handshake(
+ exec_ctx, req, req->ep,
+ req->ssl_host_override ? req->ssl_host_override : req->host,
+ on_handshake_done);
+}
+
+static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req) {
+ grpc_resolved_address *addr;
+ if (req->next_address == req->addresses->naddrs) {
+ finish(exec_ctx, req, 0);
+ return;
+ }
+ addr = &req->addresses->addrs[req->next_address++];
+ grpc_closure_init(&req->connected, on_connected, req);
+ grpc_tcp_client_connect(
+ exec_ctx, &req->connected, &req->ep, req->context->pollset_set,
+ (struct sockaddr *)&addr->addr, addr->len, req->deadline);
+}
+
+static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg,
+ grpc_resolved_addresses *addresses) {
+ internal_request *req = arg;
+ if (!addresses) {
+ finish(exec_ctx, req, 0);
+ return;
+ }
+ req->addresses = addresses;
+ req->next_address = 0;
+ next_address(exec_ctx, req);
+}
+
+static void internal_request_begin(
+ grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
+ grpc_pollset *pollset, const grpc_httpcli_request *request,
+ gpr_timespec deadline, grpc_httpcli_response_cb on_response,
+ void *user_data, 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;
+ req->deadline = deadline;
+ req->handshaker =
+ request->handshaker ? request->handshaker : &grpc_httpcli_plaintext;
+ req->context = context;
+ req->pollset = pollset;
+ grpc_closure_init(&req->on_read, on_read, req);
+ grpc_closure_init(&req->done_write, done_write, req);
+ gpr_slice_buffer_init(&req->incoming);
+ gpr_slice_buffer_init(&req->outgoing);
+ grpc_iomgr_register_object(&req->iomgr_obj, name);
+ req->host = gpr_strdup(request->host);
+ req->ssl_host_override = gpr_strdup(request->ssl_host_override);
+
+ grpc_pollset_set_add_pollset(exec_ctx, req->context->pollset_set,
+ req->pollset);
+ grpc_resolve_address(request->host, req->handshaker->default_port,
+ on_resolved, req);
+}
+
+void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
+ grpc_pollset *pollset,
+ const grpc_httpcli_request *request,
+ gpr_timespec deadline,
+ grpc_httpcli_response_cb on_response, void *user_data) {
+ char *name;
+ if (g_get_override &&
+ g_get_override(exec_ctx, request, deadline, on_response, user_data)) {
+ return;
+ }
+ gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->http.path);
+ internal_request_begin(exec_ctx, context, pollset, request, deadline,
+ on_response, user_data, name,
+ grpc_httpcli_format_get_request(request));
+ gpr_free(name);
+}
+
+void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
+ grpc_pollset *pollset,
+ 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) {
+ char *name;
+ if (g_post_override &&
+ g_post_override(exec_ctx, request, body_bytes, body_size, deadline,
+ on_response, user_data)) {
+ return;
+ }
+ gpr_asprintf(&name, "HTTP:POST:%s:%s", request->host, request->http.path);
+ internal_request_begin(
+ exec_ctx, context, pollset, request, deadline, on_response, user_data,
+ name, grpc_httpcli_format_post_request(request, body_bytes, body_size));
+ gpr_free(name);
+}
+
+void grpc_httpcli_set_override(grpc_httpcli_get_override get,
+ grpc_httpcli_post_override post) {
+ g_get_override = get;
+ g_post_override = post;
+}
diff --git a/src/core/lib/http/httpcli.h b/src/core/lib/http/httpcli.h
new file mode 100644
index 0000000000..b8d54a8586
--- /dev/null
+++ b/src/core/lib/http/httpcli.h
@@ -0,0 +1,144 @@
+/*
+ *
+ * Copyright 2015-2016, 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_HTTP_HTTPCLI_H
+#define GRPC_CORE_LIB_HTTP_HTTPCLI_H
+
+#include <stddef.h>
+
+#include <grpc/support/time.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/pollset_set.h"
+
+/* User agent this library reports */
+#define GRPC_HTTPCLI_USER_AGENT "grpc-httpcli/0.0"
+
+/* Tracks in-progress http requests
+ TODO(ctiller): allow caching and capturing multiple requests for the
+ same content and combining them */
+typedef struct grpc_httpcli_context {
+ grpc_pollset_set *pollset_set;
+} grpc_httpcli_context;
+
+typedef struct {
+ const char *default_port;
+ void (*handshake)(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint,
+ const char *host,
+ void (*on_done)(grpc_exec_ctx *exec_ctx, void *arg,
+ grpc_endpoint *endpoint));
+} grpc_httpcli_handshaker;
+
+extern const grpc_httpcli_handshaker grpc_httpcli_plaintext;
+extern const grpc_httpcli_handshaker grpc_httpcli_ssl;
+
+/* A request */
+typedef struct grpc_httpcli_request {
+ /* The host name to connect to */
+ char *host;
+ /* The host to verify in the SSL handshake (or NULL) */
+ char *ssl_host_override;
+ /* The main part of the request
+ The following headers are supplied automatically and MUST NOT be set here:
+ Host, Connection, User-Agent */
+ grpc_http_request http;
+ /* handshaker to use ssl for the request */
+ const grpc_httpcli_handshaker *handshaker;
+} 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);
+
+/* Asynchronously perform a HTTP GET.
+ 'context' specifies the http context under which to do the get
+ 'pollset' indicates a grpc_pollset that is interested in the result
+ of the get - work on this pollset may be used to progress the get
+ operation
+ '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) */
+void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
+ grpc_pollset *pollset,
+ const grpc_httpcli_request *request,
+ gpr_timespec deadline,
+ grpc_httpcli_response_cb on_response, void *user_data);
+
+/* Asynchronously perform a HTTP POST.
+ 'context' specifies the http context under which to do the post
+ 'pollset' indicates a grpc_pollset that is interested in the result
+ of the post - work on this pollset may be used to progress the post
+ operation
+ 'request' contains request parameters - these are caller owned and can be
+ destroyed once the call returns
+ 'body_bytes' and 'body_size' specify the payload for the post.
+ When there is no body, pass in NULL as body_bytes.
+ '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)
+ Does not support ?var1=val1&var2=val2 in the path. */
+void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context,
+ grpc_pollset *pollset,
+ 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);
+
+/* 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);
+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);
+
+void grpc_httpcli_set_override(grpc_httpcli_get_override get,
+ grpc_httpcli_post_override post);
+
+#endif /* GRPC_CORE_LIB_HTTP_HTTPCLI_H */
diff --git a/src/core/lib/http/httpcli_security_connector.c b/src/core/lib/http/httpcli_security_connector.c
new file mode 100644
index 0000000000..6f1630ac1f
--- /dev/null
+++ b/src/core/lib/http/httpcli_security_connector.c
@@ -0,0 +1,188 @@
+/*
+ *
+ * Copyright 2015-2016, 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/httpcli.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include "src/core/lib/security/handshake.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/tsi/ssl_transport_security.h"
+
+typedef struct {
+ grpc_channel_security_connector base;
+ tsi_ssl_handshaker_factory *handshaker_factory;
+ char *secure_peer_name;
+} grpc_httpcli_ssl_channel_security_connector;
+
+static void httpcli_ssl_destroy(grpc_security_connector *sc) {
+ grpc_httpcli_ssl_channel_security_connector *c =
+ (grpc_httpcli_ssl_channel_security_connector *)sc;
+ if (c->handshaker_factory != NULL) {
+ tsi_ssl_handshaker_factory_destroy(c->handshaker_factory);
+ }
+ if (c->secure_peer_name != NULL) gpr_free(c->secure_peer_name);
+ gpr_free(sc);
+}
+
+static void httpcli_ssl_do_handshake(grpc_exec_ctx *exec_ctx,
+ grpc_channel_security_connector *sc,
+ grpc_endpoint *nonsecure_endpoint,
+ grpc_security_handshake_done_cb cb,
+ void *user_data) {
+ grpc_httpcli_ssl_channel_security_connector *c =
+ (grpc_httpcli_ssl_channel_security_connector *)sc;
+ tsi_result result = TSI_OK;
+ tsi_handshaker *handshaker;
+ if (c->handshaker_factory == NULL) {
+ cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL);
+ return;
+ }
+ result = tsi_ssl_handshaker_factory_create_handshaker(
+ c->handshaker_factory, c->secure_peer_name, &handshaker);
+ if (result != TSI_OK) {
+ gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.",
+ tsi_result_to_string(result));
+ 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);
+ }
+}
+
+static void httpcli_ssl_check_peer(grpc_exec_ctx *exec_ctx,
+ grpc_security_connector *sc, tsi_peer peer,
+ grpc_security_peer_check_cb cb,
+ void *user_data) {
+ grpc_httpcli_ssl_channel_security_connector *c =
+ (grpc_httpcli_ssl_channel_security_connector *)sc;
+ grpc_security_status status = GRPC_SECURITY_OK;
+
+ /* Check the peer name. */
+ if (c->secure_peer_name != NULL &&
+ !tsi_ssl_peer_matches_name(&peer, c->secure_peer_name)) {
+ gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate",
+ c->secure_peer_name);
+ status = GRPC_SECURITY_ERROR;
+ }
+ cb(exec_ctx, user_data, status, NULL);
+ tsi_peer_destruct(&peer);
+}
+
+static grpc_security_connector_vtable httpcli_ssl_vtable = {
+ httpcli_ssl_destroy, httpcli_ssl_check_peer};
+
+static grpc_security_status httpcli_ssl_channel_security_connector_create(
+ const unsigned char *pem_root_certs, size_t pem_root_certs_size,
+ const char *secure_peer_name, grpc_channel_security_connector **sc) {
+ tsi_result result = TSI_OK;
+ grpc_httpcli_ssl_channel_security_connector *c;
+
+ if (secure_peer_name != NULL && pem_root_certs == NULL) {
+ gpr_log(GPR_ERROR,
+ "Cannot assert a secure peer name without a trust root.");
+ return GRPC_SECURITY_ERROR;
+ }
+
+ c = gpr_malloc(sizeof(grpc_httpcli_ssl_channel_security_connector));
+ memset(c, 0, sizeof(grpc_httpcli_ssl_channel_security_connector));
+
+ gpr_ref_init(&c->base.base.refcount, 1);
+ c->base.base.vtable = &httpcli_ssl_vtable;
+ if (secure_peer_name != NULL) {
+ c->secure_peer_name = gpr_strdup(secure_peer_name);
+ }
+ result = tsi_create_ssl_client_handshaker_factory(
+ NULL, 0, NULL, 0, pem_root_certs, pem_root_certs_size, NULL, NULL, NULL,
+ 0, &c->handshaker_factory);
+ if (result != TSI_OK) {
+ gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.",
+ tsi_result_to_string(result));
+ httpcli_ssl_destroy(&c->base.base);
+ *sc = NULL;
+ return GRPC_SECURITY_ERROR;
+ }
+ c->base.do_handshake = httpcli_ssl_do_handshake;
+ *sc = &c->base;
+ return GRPC_SECURITY_OK;
+}
+
+/* handshaker */
+
+typedef struct {
+ void (*func)(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint);
+ void *arg;
+} on_done_closure;
+
+static void on_secure_transport_setup_done(grpc_exec_ctx *exec_ctx, void *rp,
+ grpc_security_status status,
+ grpc_endpoint *secure_endpoint,
+ grpc_auth_context *auth_context) {
+ on_done_closure *c = rp;
+ if (status != GRPC_SECURITY_OK) {
+ gpr_log(GPR_ERROR, "Secure transport setup failed with error %d.", status);
+ c->func(exec_ctx, c->arg, NULL);
+ } else {
+ c->func(exec_ctx, c->arg, secure_endpoint);
+ }
+ gpr_free(c);
+}
+
+static void ssl_handshake(grpc_exec_ctx *exec_ctx, void *arg,
+ grpc_endpoint *tcp, const char *host,
+ void (*on_done)(grpc_exec_ctx *exec_ctx, void *arg,
+ grpc_endpoint *endpoint)) {
+ grpc_channel_security_connector *sc = NULL;
+ const unsigned char *pem_root_certs = NULL;
+ on_done_closure *c = gpr_malloc(sizeof(*c));
+ size_t pem_root_certs_size = grpc_get_default_ssl_roots(&pem_root_certs);
+ if (pem_root_certs == NULL || pem_root_certs_size == 0) {
+ gpr_log(GPR_ERROR, "Could not get default pem root certs.");
+ on_done(exec_ctx, arg, NULL);
+ gpr_free(c);
+ return;
+ }
+ c->func = on_done;
+ c->arg = arg;
+ GPR_ASSERT(httpcli_ssl_channel_security_connector_create(
+ 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);
+ GRPC_SECURITY_CONNECTOR_UNREF(&sc->base, "httpcli");
+}
+
+const grpc_httpcli_handshaker grpc_httpcli_ssl = {"https", ssl_handshake};
diff --git a/src/core/lib/http/parser.c b/src/core/lib/http/parser.c
new file mode 100644
index 0000000000..5d4e304615
--- /dev/null
+++ b/src/core/lib/http/parser.c
@@ -0,0 +1,313 @@
+/*
+ *
+ * Copyright 2015-2016, 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>
+
+static char *buf2str(void *buffer, size_t length) {
+ char *out = gpr_malloc(length + 1);
+ memcpy(out, buffer, length);
+ out[length] = 0;
+ return out;
+}
+
+static int 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 =
+ (cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0');
+ if (cur == end || *cur++ != ' ') goto error;
+
+ /* we don't really care about the status code message */
+
+ return 1;
+
+error:
+ gpr_log(GPR_ERROR, "Failed parsing response line");
+ return 0;
+}
+
+static int 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) goto error;
+ 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;
+ vers_major = (uint8_t)(*cur++ - '1' + 1);
+ ++cur;
+ if (cur == end) goto error;
+ 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 {
+ goto error;
+ }
+ } else if (vers_major == 2) {
+ if (vers_minor == 0) {
+ parser->http.request.version = GRPC_HTTP_HTTP20;
+ } else {
+ goto error;
+ }
+ } else {
+ goto error;
+ }
+
+ return 1;
+
+error:
+ gpr_log(GPR_ERROR, "Failed parsing request line");
+ return 0;
+}
+
+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 int 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};
+
+ GPR_ASSERT(cur != end);
+
+ if (*cur == ' ' || *cur == '\t') {
+ gpr_log(GPR_ERROR, "Continued header lines not supported yet");
+ goto error;
+ }
+
+ while (cur != end && *cur != ':') {
+ cur++;
+ }
+ if (cur == end) {
+ gpr_log(GPR_ERROR, "Didn't find ':' in header string");
+ goto error;
+ }
+ GPR_ASSERT(cur >= beg);
+ hdr.key = buf2str(beg, (size_t)(cur - beg));
+ cur++; /* skip : */
+
+ while (cur != end && (*cur == ' ' || *cur == '\t')) {
+ cur++;
+ }
+ GPR_ASSERT(end - cur >= 2);
+ hdr.value = buf2str(cur, (size_t)(end - cur) - 2);
+
+ 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;
+ }
+
+ 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;
+ return 1;
+
+error:
+ gpr_free(hdr.key);
+ gpr_free(hdr.value);
+ return 0;
+}
+
+static int finish_line(grpc_http_parser *parser) {
+ switch (parser->state) {
+ case GRPC_HTTP_FIRST_LINE:
+ if (!handle_first_line(parser)) {
+ return 0;
+ }
+ parser->state = GRPC_HTTP_HEADERS;
+ break;
+ case GRPC_HTTP_HEADERS:
+ if (parser->cur_line_length == 2) {
+ parser->state = GRPC_HTTP_BODY;
+ break;
+ }
+ if (!add_header(parser)) {
+ return 0;
+ }
+ break;
+ case GRPC_HTTP_BODY:
+ GPR_UNREACHABLE_CODE(return 0);
+ }
+
+ parser->cur_line_length = 0;
+ return 1;
+}
+
+static int 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 {
+ return 0;
+ }
+
+ 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 1;
+}
+
+static int 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) {
+ 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 (parser->cur_line_length >= 2 &&
+ parser->cur_line[parser->cur_line_length - 2] == '\r' &&
+ parser->cur_line[parser->cur_line_length - 1] == '\n') {
+ return finish_line(parser);
+ } else {
+ return 1;
+ }
+ 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) {
+ memset(parser, 0, sizeof(*parser));
+ parser->state = GRPC_HTTP_FIRST_LINE;
+ parser->type = GRPC_HTTP_UNKNOWN;
+}
+
+void grpc_http_parser_destroy(grpc_http_parser *parser) {
+ 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);
+ }
+}
+
+int 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;
+ }
+ }
+
+ return 1;
+}
+
+int grpc_http_parser_eof(grpc_http_parser *parser) {
+ return parser->state == GRPC_HTTP_BODY;
+}
diff --git a/src/core/lib/http/parser.h b/src/core/lib/http/parser.h
new file mode 100644
index 0000000000..6a72174aa6
--- /dev/null
+++ b/src/core/lib/http/parser.h
@@ -0,0 +1,116 @@
+/*
+ *
+ * Copyright 2015-2016, 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.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_HTTP_PARSER_H
+#define GRPC_CORE_LIB_HTTP_PARSER_H
+
+#include <grpc/support/port_platform.h>
+#include <grpc/support/slice.h>
+
+/* Maximum length of a header string of the form 'Key: Value\r\n' */
+#define GRPC_HTTP_PARSER_MAX_HEADER_LENGTH 4096
+
+/* A single header to be passed in a request */
+typedef struct grpc_http_header {
+ char *key;
+ char *value;
+} grpc_http_header;
+
+typedef enum {
+ GRPC_HTTP_FIRST_LINE,
+ GRPC_HTTP_HEADERS,
+ GRPC_HTTP_BODY
+} grpc_http_parser_state;
+
+typedef enum {
+ GRPC_HTTP_HTTP10,
+ GRPC_HTTP_HTTP11,
+ GRPC_HTTP_HTTP20,
+} grpc_http_version;
+
+typedef enum {
+ GRPC_HTTP_RESPONSE,
+ GRPC_HTTP_REQUEST,
+ GRPC_HTTP_UNKNOWN
+} grpc_http_type;
+
+/* A request */
+typedef struct grpc_http_request {
+ /* Method of the request (e.g. GET, POST) */
+ char *method;
+ /* The path of the resource to fetch */
+ char *path;
+ /* HTTP version to use */
+ grpc_http_version version;
+ /* Headers attached to the request */
+ size_t hdr_count;
+ grpc_http_header *hdrs;
+ /* Body: length and contents; contents are NOT null-terminated */
+ size_t body_length;
+ char *body;
+} grpc_http_request;
+
+/* A response */
+typedef struct grpc_http_response {
+ /* HTTP status code */
+ int status;
+ /* Headers: count and key/values */
+ size_t hdr_count;
+ grpc_http_header *hdrs;
+ /* Body: length and contents; contents are NOT null-terminated */
+ size_t body_length;
+ char *body;
+} grpc_http_response;
+
+typedef struct {
+ grpc_http_parser_state state;
+ grpc_http_type type;
+
+ union {
+ grpc_http_response response;
+ grpc_http_request request;
+ } http;
+ size_t body_capacity;
+ size_t hdr_capacity;
+
+ uint8_t cur_line[GRPC_HTTP_PARSER_MAX_HEADER_LENGTH];
+ size_t cur_line_length;
+} grpc_http_parser;
+
+void grpc_http_parser_init(grpc_http_parser *parser);
+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);
+
+#endif /* GRPC_CORE_LIB_HTTP_PARSER_H */