aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/httpcli
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/httpcli')
-rw-r--r--src/core/httpcli/format_request.c121
-rw-r--r--src/core/httpcli/format_request.h45
-rw-r--r--src/core/httpcli/httpcli.c259
-rw-r--r--src/core/httpcli/httpcli.h104
-rw-r--r--src/core/httpcli/httpcli_security_context.c128
-rw-r--r--src/core/httpcli/httpcli_security_context.h43
-rw-r--r--src/core/httpcli/parser.c212
-rw-r--r--src/core/httpcli/parser.h64
8 files changed, 976 insertions, 0 deletions
diff --git a/src/core/httpcli/format_request.c b/src/core/httpcli/format_request.c
new file mode 100644
index 0000000000..7a44f1266f
--- /dev/null
+++ b/src/core/httpcli/format_request.c
@@ -0,0 +1,121 @@
+/*
+ *
+ * Copyright 2014, 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/httpcli/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/useful.h>
+
+typedef struct {
+ size_t length;
+ size_t capacity;
+ char *data;
+} sbuf;
+
+static void sbuf_append(sbuf *buf, const char *bytes, size_t len) {
+ if (buf->length + len > buf->capacity) {
+ buf->capacity = GPR_MAX(buf->length + len, buf->capacity * 3 / 2);
+ buf->data = gpr_realloc(buf->data, buf->capacity);
+ }
+ memcpy(buf->data + buf->length, bytes, len);
+ buf->length += len;
+}
+
+static void sbprintf(sbuf *buf, const char *fmt, ...) {
+ char temp[GRPC_HTTPCLI_MAX_HEADER_LENGTH];
+ size_t len;
+ va_list args;
+
+ va_start(args, fmt);
+ len = vsprintf(temp, fmt, args);
+ va_end(args);
+
+ sbuf_append(buf, temp, len);
+}
+
+static void fill_common_header(const grpc_httpcli_request *request, sbuf *buf) {
+ size_t i;
+ sbprintf(buf, "%s HTTP/1.0\r\n", request->path);
+ /* just in case some crazy server really expects HTTP/1.1 */
+ sbprintf(buf, "Host: %s\r\n", request->host);
+ sbprintf(buf, "Connection: close\r\n");
+ sbprintf(buf, "User-Agent: %s\r\n", GRPC_HTTPCLI_USER_AGENT);
+ /* user supplied headers */
+ for (i = 0; i < request->hdr_count; i++) {
+ sbprintf(buf, "%s: %s\r\n", request->hdrs[i].key, request->hdrs[i].value);
+ }
+}
+
+gpr_slice grpc_httpcli_format_get_request(const grpc_httpcli_request *request) {
+ sbuf out = {0, 0, NULL};
+
+ sbprintf(&out, "GET ");
+ fill_common_header(request, &out);
+ sbprintf(&out, "\r\n");
+
+ return gpr_slice_new(out.data, out.length, gpr_free);
+}
+
+gpr_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request,
+ const char *body_bytes,
+ size_t body_size) {
+ sbuf out = {0, 0, NULL};
+ size_t i;
+
+ sbprintf(&out, "POST ");
+ fill_common_header(request, &out);
+ if (body_bytes) {
+ gpr_uint8 has_content_type = 0;
+ for (i = 0; i < request->hdr_count; i++) {
+ if (strcmp(request->hdrs[i].key, "Content-Type") == 0) {
+ has_content_type = 1;
+ break;
+ }
+ }
+ if (!has_content_type) {
+ sbprintf(&out, "Content-Type: text/plain\r\n");
+ }
+ sbprintf(&out, "Content-Length: %lu\r\n", (unsigned long)body_size);
+ }
+ sbprintf(&out, "\r\n");
+ if (body_bytes) {
+ sbuf_append(&out, body_bytes, body_size);
+ }
+
+ return gpr_slice_new(out.data, out.length, gpr_free);
+}
diff --git a/src/core/httpcli/format_request.h b/src/core/httpcli/format_request.h
new file mode 100644
index 0000000000..988f872563
--- /dev/null
+++ b/src/core/httpcli/format_request.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright 2014, 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_INTERNAL_HTTPCLI_FORMAT_REQUEST_H__
+#define __GRPC_INTERNAL_HTTPCLI_FORMAT_REQUEST_H__
+
+#include "src/core/httpcli/httpcli.h"
+#include <grpc/support/slice.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_INTERNAL_HTTPCLI_FORMAT_REQUEST_H__ */
diff --git a/src/core/httpcli/httpcli.c b/src/core/httpcli/httpcli.c
new file mode 100644
index 0000000000..6c0a688390
--- /dev/null
+++ b/src/core/httpcli/httpcli.c
@@ -0,0 +1,259 @@
+/*
+ *
+ * Copyright 2014, 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/httpcli/httpcli.h"
+
+#include <string.h>
+
+#include "src/core/endpoint/endpoint.h"
+#include "src/core/endpoint/resolve_address.h"
+#include "src/core/endpoint/tcp_client.h"
+#include "src/core/httpcli/format_request.h"
+#include "src/core/httpcli/httpcli_security_context.h"
+#include "src/core/httpcli/parser.h"
+#include "src/core/security/security_context.h"
+#include "src/core/security/google_root_certs.h"
+#include "src/core/security/secure_transport_setup.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string.h>
+
+typedef struct {
+ gpr_slice request_text;
+ grpc_httpcli_parser parser;
+ grpc_resolved_addresses *addresses;
+ size_t next_address;
+ grpc_endpoint *ep;
+ grpc_em *em;
+ char *host;
+ gpr_timespec deadline;
+ int have_read_byte;
+ int use_ssl;
+ grpc_httpcli_response_cb on_response;
+ void *user_data;
+} internal_request;
+
+static void next_address(internal_request *req);
+
+static void finish(internal_request *req, int success) {
+ gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
+ req->on_response(req->user_data, success ? &req->parser.r : NULL);
+ grpc_httpcli_parser_destroy(&req->parser);
+ if (req->addresses != NULL) {
+ grpc_resolved_addresses_destroy(req->addresses);
+ }
+ if (req->ep != NULL) {
+ grpc_endpoint_destroy(req->ep);
+ }
+ gpr_slice_unref(req->request_text);
+ gpr_free(req->host);
+ gpr_free(req);
+}
+
+static void on_read(void *user_data, gpr_slice *slices, size_t nslices,
+ grpc_endpoint_cb_status status) {
+ internal_request *req = user_data;
+ size_t i;
+
+ gpr_log(GPR_DEBUG, "%s nslices=%d status=%d", __FUNCTION__, nslices, status);
+
+ for (i = 0; i < nslices; i++) {
+ if (GPR_SLICE_LENGTH(slices[i])) {
+ req->have_read_byte = 1;
+ if (!grpc_httpcli_parser_parse(&req->parser, slices[i])) {
+ finish(req, 0);
+ goto done;
+ }
+ }
+ }
+
+ switch (status) {
+ case GRPC_ENDPOINT_CB_OK:
+ grpc_endpoint_notify_on_read(req->ep, on_read, req, gpr_inf_future);
+ break;
+ case GRPC_ENDPOINT_CB_EOF:
+ case GRPC_ENDPOINT_CB_ERROR:
+ case GRPC_ENDPOINT_CB_SHUTDOWN:
+ case GRPC_ENDPOINT_CB_TIMED_OUT:
+ if (!req->have_read_byte) {
+ next_address(req);
+ } else {
+ finish(req, grpc_httpcli_parser_eof(&req->parser));
+ }
+ break;
+ }
+
+done:
+ for (i = 0; i < nslices; i++) {
+ gpr_slice_unref(slices[i]);
+ }
+}
+
+static void on_written(internal_request *req) {
+ gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
+ grpc_endpoint_notify_on_read(req->ep, on_read, req, gpr_inf_future);
+}
+
+static void done_write(void *arg, grpc_endpoint_cb_status status) {
+ internal_request *req = arg;
+ gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
+ switch (status) {
+ case GRPC_ENDPOINT_CB_OK:
+ on_written(req);
+ break;
+ case GRPC_ENDPOINT_CB_EOF:
+ case GRPC_ENDPOINT_CB_SHUTDOWN:
+ case GRPC_ENDPOINT_CB_ERROR:
+ case GRPC_ENDPOINT_CB_TIMED_OUT:
+ next_address(req);
+ break;
+ }
+}
+
+static void start_write(internal_request *req) {
+ gpr_slice_ref(req->request_text);
+ gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
+ switch (grpc_endpoint_write(req->ep, &req->request_text, 1, done_write, req,
+ gpr_inf_future)) {
+ case GRPC_ENDPOINT_WRITE_DONE:
+ on_written(req);
+ break;
+ case GRPC_ENDPOINT_WRITE_PENDING:
+ break;
+ case GRPC_ENDPOINT_WRITE_ERROR:
+ finish(req, 0);
+ break;
+ }
+}
+
+static void on_secure_transport_setup_done(void *rp,
+ grpc_security_status status,
+ grpc_endpoint *secure_endpoint) {
+ internal_request *req = rp;
+ gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
+ if (status != GRPC_SECURITY_OK) {
+ gpr_log(GPR_ERROR, "Secure transport setup failed with error %d.", status);
+ finish(req, 0);
+ } else {
+ req->ep = secure_endpoint;
+ start_write(req);
+ }
+}
+
+static void on_connected(void *arg, grpc_endpoint *tcp) {
+ internal_request *req = arg;
+
+ gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
+ if (!tcp) {
+ next_address(req);
+ return;
+ }
+ req->ep = tcp;
+ if (req->use_ssl) {
+ grpc_channel_security_context *ctx = NULL;
+ GPR_ASSERT(grpc_httpcli_ssl_channel_security_context_create(
+ grpc_google_root_certs, grpc_google_root_certs_size,
+ req->host, &ctx) == GRPC_SECURITY_OK);
+ grpc_setup_secure_transport(&ctx->base, tcp, on_secure_transport_setup_done,
+ req);
+ grpc_security_context_unref(&ctx->base);
+ } else {
+ start_write(req);
+ }
+}
+
+static void next_address(internal_request *req) {
+ grpc_resolved_address *addr;
+ gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
+ if (req->next_address == req->addresses->naddrs) {
+ finish(req, 0);
+ return;
+ }
+ addr = &req->addresses->addrs[req->next_address++];
+ grpc_tcp_client_connect(on_connected, req, req->em,
+ (struct sockaddr *)&addr->addr, addr->len,
+ req->deadline);
+}
+
+static void on_resolved(void *arg, grpc_resolved_addresses *addresses) {
+ internal_request *req = arg;
+ gpr_log(GPR_DEBUG, "%s", __FUNCTION__);
+ if (!addresses) {
+ finish(req, 0);
+ }
+ req->addresses = addresses;
+ req->next_address = 0;
+ next_address(req);
+}
+
+void grpc_httpcli_get(const grpc_httpcli_request *request,
+ gpr_timespec deadline, grpc_em *em,
+ grpc_httpcli_response_cb on_response, void *user_data) {
+ internal_request *req = gpr_malloc(sizeof(internal_request));
+ memset(req, 0, sizeof(*req));
+ req->request_text = grpc_httpcli_format_get_request(request);
+ grpc_httpcli_parser_init(&req->parser);
+ req->on_response = on_response;
+ req->user_data = user_data;
+ req->em = em;
+ req->deadline = deadline;
+ req->use_ssl = request->use_ssl;
+ if (req->use_ssl) {
+ req->host = gpr_strdup(request->host);
+ }
+
+ grpc_resolve_address(request->host, req->use_ssl ? "https" : "http",
+ on_resolved, req);
+}
+
+void grpc_httpcli_post(const grpc_httpcli_request *request,
+ const char *body_bytes, size_t body_size,
+ gpr_timespec deadline, grpc_em *em,
+ grpc_httpcli_response_cb on_response, void *user_data) {
+ internal_request *req = gpr_malloc(sizeof(internal_request));
+ memset(req, 0, sizeof(*req));
+ req->request_text =
+ grpc_httpcli_format_post_request(request, body_bytes, body_size);
+ grpc_httpcli_parser_init(&req->parser);
+ req->on_response = on_response;
+ req->user_data = user_data;
+ req->em = em;
+ req->deadline = deadline;
+ req->use_ssl = request->use_ssl;
+ if (req->use_ssl) {
+ req->host = gpr_strdup(request->host);
+ }
+
+ grpc_resolve_address(request->host, req->use_ssl ? "https" : "http",
+ on_resolved, req);
+}
diff --git a/src/core/httpcli/httpcli.h b/src/core/httpcli/httpcli.h
new file mode 100644
index 0000000000..aef0edfdd4
--- /dev/null
+++ b/src/core/httpcli/httpcli.h
@@ -0,0 +1,104 @@
+/*
+ *
+ * Copyright 2014, 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_INTERNAL_HTTPCLI_HTTPCLI_H__
+#define __GRPC_INTERNAL_HTTPCLI_HTTPCLI_H__
+
+#include <stddef.h>
+
+#include "src/core/eventmanager/em.h"
+#include <grpc/support/time.h>
+
+/* User agent this library reports */
+#define GRPC_HTTPCLI_USER_AGENT "grpc-httpcli/0.0"
+/* Maximum length of a header string of the form 'Key: Value\r\n' */
+#define GRPC_HTTPCLI_MAX_HEADER_LENGTH 4096
+
+/* A single header to be passed in a request */
+typedef struct grpc_httpcli_header {
+ char *key;
+ char *value;
+} grpc_httpcli_header;
+
+/* A request */
+typedef struct grpc_httpcli_request {
+ /* The host name to connect to */
+ char *host;
+ /* The path of the resource to fetch */
+ char *path;
+ /* Additional headers: count and key/values; the following are supplied
+ automatically and MUST NOT be set here:
+ Host, Connection, User-Agent */
+ size_t hdr_count;
+ grpc_httpcli_header *hdrs;
+ /* whether to use ssl for the request */
+ int use_ssl;
+} grpc_httpcli_request;
+
+/* A response */
+typedef struct grpc_httpcli_response {
+ /* HTTP status code */
+ int status;
+ /* Headers: count and key/values */
+ size_t hdr_count;
+ grpc_httpcli_header *hdrs;
+ /* Body: length and contents; contents are NOT null-terminated */
+ size_t body_length;
+ char *body;
+} grpc_httpcli_response;
+
+/* Callback for grpc_httpcli_get */
+typedef void (*grpc_httpcli_response_cb)(void *user_data,
+ const grpc_httpcli_response *response);
+
+/* Asynchronously perform a HTTP GET.
+ '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)
+ '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) */
+void grpc_httpcli_get(const grpc_httpcli_request *request,
+ gpr_timespec deadline, grpc_em *em,
+ grpc_httpcli_response_cb on_response, void *user_data);
+
+/* Asynchronously perform a HTTP POST.
+ When there is no body, pass in NULL as body_bytes.
+ Does not support ?var1=val1&var2=val2 in the path. */
+void grpc_httpcli_post(const grpc_httpcli_request *request,
+ const char *body_bytes, size_t body_size,
+ gpr_timespec deadline, grpc_em *em,
+ grpc_httpcli_response_cb on_response, void *user_data);
+
+#endif /* __GRPC_INTERNAL_HTTPCLI_HTTPCLI_H__ */
diff --git a/src/core/httpcli/httpcli_security_context.c b/src/core/httpcli/httpcli_security_context.c
new file mode 100644
index 0000000000..c7b9b330f0
--- /dev/null
+++ b/src/core/httpcli/httpcli_security_context.c
@@ -0,0 +1,128 @@
+/*
+ *
+ * Copyright 2014, 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/httpcli/httpcli_security_context.h"
+
+#include <string.h>
+
+#include "src/core/security/secure_transport_setup.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string.h>
+#include "src/core/tsi/ssl_transport_security.h"
+
+typedef struct {
+ grpc_channel_security_context base;
+ tsi_ssl_handshaker_factory *handshaker_factory;
+ char *secure_peer_name;
+} grpc_httpcli_ssl_channel_security_context;
+
+static void httpcli_ssl_destroy(grpc_security_context *ctx) {
+ grpc_httpcli_ssl_channel_security_context *c =
+ (grpc_httpcli_ssl_channel_security_context *)ctx;
+ 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(ctx);
+}
+
+static grpc_security_status httpcli_ssl_create_handshaker(
+ grpc_security_context *ctx, tsi_handshaker **handshaker) {
+ grpc_httpcli_ssl_channel_security_context *c =
+ (grpc_httpcli_ssl_channel_security_context *)ctx;
+ tsi_result result = TSI_OK;
+ if (c->handshaker_factory == NULL) return GRPC_SECURITY_ERROR;
+ 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));
+ return GRPC_SECURITY_ERROR;
+ }
+ return GRPC_SECURITY_OK;
+}
+
+static grpc_security_status httpcli_ssl_check_peer(
+ grpc_security_context *ctx, const tsi_peer *peer,
+ grpc_security_check_peer_cb cb, void *user_data) {
+ grpc_httpcli_ssl_channel_security_context *c =
+ (grpc_httpcli_ssl_channel_security_context *)ctx;
+
+ /* 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);
+ return GRPC_SECURITY_ERROR;
+ }
+ return GRPC_SECURITY_OK;
+}
+
+static grpc_security_context_vtable httpcli_ssl_vtable = {
+ httpcli_ssl_destroy, httpcli_ssl_create_handshaker, httpcli_ssl_check_peer};
+
+grpc_security_status grpc_httpcli_ssl_channel_security_context_create(
+ const unsigned char *pem_root_certs, size_t pem_root_certs_size,
+ const char *secure_peer_name, grpc_channel_security_context **ctx) {
+ tsi_result result = TSI_OK;
+ grpc_httpcli_ssl_channel_security_context *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_context));
+ memset(c, 0, sizeof(grpc_httpcli_ssl_channel_security_context));
+
+ gpr_ref_init(&c->base.base.refcount, 1);
+ c->base.base.is_client_side = 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);
+ *ctx = NULL;
+ return GRPC_SECURITY_ERROR;
+ }
+ *ctx = &c->base;
+ return GRPC_SECURITY_OK;
+}
diff --git a/src/core/httpcli/httpcli_security_context.h b/src/core/httpcli/httpcli_security_context.h
new file mode 100644
index 0000000000..c267622e60
--- /dev/null
+++ b/src/core/httpcli/httpcli_security_context.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * Copyright 2014, 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_INTERNAL_HTTPCLI_HTTPCLI_SECURITY_CONTEXT_H__
+#define __GRPC_INTERNAL_HTTPCLI_HTTPCLI_SECURITY_CONTEXT_H__
+
+#include "src/core/security/security_context.h"
+
+grpc_security_status grpc_httpcli_ssl_channel_security_context_create(
+ const unsigned char *pem_root_certs, size_t pem_root_certs_size,
+ const char *secure_peer_name, grpc_channel_security_context **ctx);
+
+#endif /* __GRPC_INTERNAL_HTTPCLI_HTTPCLI_SECURITY_CONTEXT_H__ */
diff --git a/src/core/httpcli/parser.c b/src/core/httpcli/parser.c
new file mode 100644
index 0000000000..1f0c5167de
--- /dev/null
+++ b/src/core/httpcli/parser.c
@@ -0,0 +1,212 @@
+/*
+ *
+ * Copyright 2014, 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/httpcli/parser.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+static int handle_response_line(grpc_httpcli_parser *parser) {
+ gpr_uint8 *beg = parser->cur_line;
+ gpr_uint8 *cur = beg;
+ gpr_uint8 *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->r.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 char *buf2str(void *buffer, size_t length) {
+ char *out = gpr_malloc(length + 1);
+ memcpy(out, buffer, length);
+ out[length] = 0;
+ return out;
+}
+
+static int add_header(grpc_httpcli_parser *parser) {
+ gpr_uint8 *beg = parser->cur_line;
+ gpr_uint8 *cur = beg;
+ gpr_uint8 *end = beg + parser->cur_line_length;
+ grpc_httpcli_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;
+ }
+ hdr.key = buf2str(beg, cur - beg);
+ cur++; /* skip : */
+
+ while (cur != end && (*cur == ' ' || *cur == '\t')) {
+ cur++;
+ }
+ hdr.value = buf2str(cur, end - cur - 2);
+
+ if (parser->r.hdr_count == parser->hdr_capacity) {
+ parser->hdr_capacity =
+ GPR_MAX(parser->hdr_capacity + 1, parser->hdr_capacity * 3 / 2);
+ parser->r.hdrs = gpr_realloc(
+ parser->r.hdrs, parser->hdr_capacity * sizeof(*parser->r.hdrs));
+ }
+ parser->r.hdrs[parser->r.hdr_count++] = hdr;
+ return 1;
+
+error:
+ gpr_free(hdr.key);
+ gpr_free(hdr.value);
+ return 0;
+}
+
+static int finish_line(grpc_httpcli_parser *parser) {
+ switch (parser->state) {
+ case GRPC_HTTPCLI_INITIAL_RESPONSE:
+ if (!handle_response_line(parser)) {
+ return 0;
+ }
+ parser->state = GRPC_HTTPCLI_HEADERS;
+ break;
+ case GRPC_HTTPCLI_HEADERS:
+ if (parser->cur_line_length == 2) {
+ parser->state = GRPC_HTTPCLI_BODY;
+ break;
+ }
+ if (!add_header(parser)) {
+ return 0;
+ }
+ break;
+ case GRPC_HTTPCLI_BODY:
+ gpr_log(GPR_ERROR, "should never reach here");
+ abort();
+ }
+
+ parser->cur_line_length = 0;
+ return 1;
+}
+
+static int addbyte(grpc_httpcli_parser *parser, gpr_uint8 byte) {
+ switch (parser->state) {
+ case GRPC_HTTPCLI_INITIAL_RESPONSE:
+ case GRPC_HTTPCLI_HEADERS:
+ if (parser->cur_line_length >= GRPC_HTTPCLI_MAX_HEADER_LENGTH) {
+ gpr_log(GPR_ERROR, "HTTP client max line length (%d) exceeded",
+ GRPC_HTTPCLI_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_log(GPR_ERROR, "should never reach here");
+ abort();
+ case GRPC_HTTPCLI_BODY:
+ if (parser->r.body_length == parser->body_capacity) {
+ parser->body_capacity = GPR_MAX(8, parser->body_capacity * 3 / 2);
+ parser->r.body =
+ gpr_realloc((void *)parser->r.body, parser->body_capacity);
+ }
+ ((char *)parser->r.body)[parser->r.body_length] = byte;
+ parser->r.body_length++;
+ return 1;
+ }
+ gpr_log(GPR_ERROR, "should never reach here");
+ abort();
+}
+
+void grpc_httpcli_parser_init(grpc_httpcli_parser *parser) {
+ memset(parser, 0, sizeof(*parser));
+ parser->state = GRPC_HTTPCLI_INITIAL_RESPONSE;
+ parser->r.status = 500;
+}
+
+void grpc_httpcli_parser_destroy(grpc_httpcli_parser *parser) {
+ size_t i;
+ gpr_free(parser->r.body);
+ for (i = 0; i < parser->r.hdr_count; i++) {
+ gpr_free(parser->r.hdrs[i].key);
+ gpr_free(parser->r.hdrs[i].value);
+ }
+ gpr_free(parser->r.hdrs);
+}
+
+int grpc_httpcli_parser_parse(grpc_httpcli_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_httpcli_parser_eof(grpc_httpcli_parser *parser) {
+ return parser->state == GRPC_HTTPCLI_BODY;
+}
diff --git a/src/core/httpcli/parser.h b/src/core/httpcli/parser.h
new file mode 100644
index 0000000000..e2c24a9993
--- /dev/null
+++ b/src/core/httpcli/parser.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * Copyright 2014, 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_INTERNAL_HTTPCLI_PARSER_H__
+#define __GRPC_INTERNAL_HTTPCLI_PARSER_H__
+
+#include "src/core/httpcli/httpcli.h"
+#include <grpc/support/port_platform.h>
+#include <grpc/support/slice.h>
+
+typedef enum {
+ GRPC_HTTPCLI_INITIAL_RESPONSE,
+ GRPC_HTTPCLI_HEADERS,
+ GRPC_HTTPCLI_BODY
+} grpc_httpcli_parser_state;
+
+typedef struct {
+ grpc_httpcli_parser_state state;
+
+ grpc_httpcli_response r;
+ size_t body_capacity;
+ size_t hdr_capacity;
+
+ gpr_uint8 cur_line[GRPC_HTTPCLI_MAX_HEADER_LENGTH];
+ size_t cur_line_length;
+} grpc_httpcli_parser;
+
+void grpc_httpcli_parser_init(grpc_httpcli_parser *parser);
+void grpc_httpcli_parser_destroy(grpc_httpcli_parser *parser);
+
+int grpc_httpcli_parser_parse(grpc_httpcli_parser *parser, gpr_slice slice);
+int grpc_httpcli_parser_eof(grpc_httpcli_parser *parser);
+
+#endif /* __GRPC_INTERNAL_HTTPCLI_PARSER_H__ */