aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/httpcli/httpcli.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/httpcli/httpcli.c')
-rw-r--r--src/core/httpcli/httpcli.c259
1 files changed, 259 insertions, 0 deletions
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);
+}