aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--build.json2
-rw-r--r--include/grpc/grpc_http.h67
-rw-r--r--src/core/channel/call_op_string.c3
-rw-r--r--src/core/channel/channel_args.c8
-rw-r--r--src/core/channel/channel_stack.c22
-rw-r--r--src/core/channel/channel_stack.h5
-rw-r--r--src/core/channel/connected_channel.c2
-rw-r--r--src/core/channel/http_client_filter.c12
-rw-r--r--src/core/channel/http_server_filter.c158
-rw-r--r--src/core/security/auth.c2
-rw-r--r--src/core/surface/call.c5
-rw-r--r--src/core/surface/call.h2
-rwxr-xr-xsrc/ruby/bin/interop/interop_client.rb99
-rwxr-xr-xsrc/ruby/bin/interop/interop_server.rb19
-rwxr-xr-xsrc/ruby/grpc.gemspec1
-rw-r--r--test/core/echo/server.c69
-rw-r--r--tools/dockerfile/grpc_ruby/Dockerfile8
-rwxr-xr-xtools/gce_setup/grpc_docker.sh21
-rwxr-xr-xtools/gce_setup/shared_startup_funcs.sh38
-rwxr-xr-xtools/run_tests/run_lcov.sh2
-rw-r--r--vsprojects/vs2013/gpr.vcxproj2
-rw-r--r--vsprojects/vs2013/gpr.vcxproj.filters6
23 files changed, 458 insertions, 96 deletions
diff --git a/Makefile b/Makefile
index f4bb942b52..74372a6fb8 100644
--- a/Makefile
+++ b/Makefile
@@ -1233,7 +1233,6 @@ PUBLIC_HEADERS_C += \
include/grpc/support/port_platform.h \
include/grpc/support/slice.h \
include/grpc/support/slice_buffer.h \
- include/grpc/support/string.h \
include/grpc/support/sync.h \
include/grpc/support/sync_generic.h \
include/grpc/support/sync_posix.h \
diff --git a/build.json b/build.json
index 4416c3fdd4..b90b1ca92e 100644
--- a/build.json
+++ b/build.json
@@ -202,7 +202,6 @@
"include/grpc/support/port_platform.h",
"include/grpc/support/slice.h",
"include/grpc/support/slice_buffer.h",
- "include/grpc/support/string.h",
"include/grpc/support/sync.h",
"include/grpc/support/sync_generic.h",
"include/grpc/support/sync_posix.h",
@@ -218,6 +217,7 @@
"headers": [
"src/core/support/cpu.h",
"src/core/support/murmur_hash.h",
+ "src/core/support/string.h",
"src/core/support/thd_internal.h"
],
"src": [
diff --git a/include/grpc/grpc_http.h b/include/grpc/grpc_http.h
new file mode 100644
index 0000000000..b2ae5340a5
--- /dev/null
+++ b/include/grpc/grpc_http.h
@@ -0,0 +1,67 @@
+/*
+ *
+ * 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_GRPC_HTTP_H__
+#define __GRPC_GRPC_HTTP_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* HTTP GET support.
+
+ HTTP2 servers can publish statically generated text content served
+ via HTTP2 GET queries by publishing one or more grpc_http_server_page
+ elements via repeated GRPC_ARG_SERVE_OVER_HTTP elements in the servers
+ channel_args.
+
+ This is not:
+ - a general purpose web server
+ - particularly fast
+
+ It's useful for being able to serve up some static content (maybe some
+ javascript to be able to interact with your GRPC server?) */
+
+typedef struct {
+ const char *path;
+ const char *content_type;
+ const char *content;
+} grpc_http_server_page;
+
+#define GRPC_ARG_SERVE_OVER_HTTP "grpc.serve_over_http"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __GRPC_GRPC_HTTP_H__ */
diff --git a/src/core/channel/call_op_string.c b/src/core/channel/call_op_string.c
index d36d51e789..127ea707bf 100644
--- a/src/core/channel/call_op_string.c
+++ b/src/core/channel/call_op_string.c
@@ -83,6 +83,9 @@ char *grpc_call_op_string(grpc_call_op *op) {
case GRPC_SEND_MESSAGE:
gpr_strvec_add(&b, gpr_strdup("SEND_MESSAGE"));
break;
+ case GRPC_SEND_PREFORMATTED_MESSAGE:
+ gpr_strvec_add(&b, gpr_strdup("SEND_PREFORMATTED_MESSAGE"));
+ break;
case GRPC_SEND_FINISH:
gpr_strvec_add(&b, gpr_strdup("SEND_FINISH"));
break;
diff --git a/src/core/channel/channel_args.c b/src/core/channel/channel_args.c
index 04ce519ff3..5f16c7b7e9 100644
--- a/src/core/channel/channel_args.c
+++ b/src/core/channel/channel_args.c
@@ -52,7 +52,9 @@ static grpc_arg copy_arg(const grpc_arg *src) {
break;
case GRPC_ARG_POINTER:
dst.value.pointer = src->value.pointer;
- dst.value.pointer.p = src->value.pointer.copy(src->value.pointer.p);
+ dst.value.pointer.p = src->value.pointer.copy
+ ? src->value.pointer.copy(src->value.pointer.p)
+ : src->value.pointer.p;
break;
}
return dst;
@@ -91,7 +93,9 @@ void grpc_channel_args_destroy(grpc_channel_args *a) {
case GRPC_ARG_INTEGER:
break;
case GRPC_ARG_POINTER:
- a->args[i].value.pointer.destroy(a->args[i].value.pointer.p);
+ if (a->args[i].value.pointer.destroy) {
+ a->args[i].value.pointer.destroy(a->args[i].value.pointer.p);
+ }
break;
}
gpr_free(a->args[i].key);
diff --git a/src/core/channel/channel_stack.c b/src/core/channel/channel_stack.c
index 5ee412bf7d..2721ed8cfc 100644
--- a/src/core/channel/channel_stack.c
+++ b/src/core/channel/channel_stack.c
@@ -202,6 +202,17 @@ grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem) {
static void do_nothing(void *user_data, grpc_op_error error) {}
+void grpc_call_element_recv_metadata(grpc_call_element *cur_elem,
+ grpc_mdelem *mdelem) {
+ grpc_call_op metadata_op;
+ metadata_op.type = GRPC_RECV_METADATA;
+ metadata_op.dir = GRPC_CALL_UP;
+ metadata_op.done_cb = do_nothing;
+ metadata_op.user_data = NULL;
+ metadata_op.data.metadata = mdelem;
+ grpc_call_next_op(cur_elem, &metadata_op);
+}
+
void grpc_call_element_send_metadata(grpc_call_element *cur_elem,
grpc_mdelem *mdelem) {
grpc_call_op metadata_op;
@@ -209,7 +220,7 @@ void grpc_call_element_send_metadata(grpc_call_element *cur_elem,
metadata_op.dir = GRPC_CALL_DOWN;
metadata_op.done_cb = do_nothing;
metadata_op.user_data = NULL;
- metadata_op.data.metadata = grpc_mdelem_ref(mdelem);
+ metadata_op.data.metadata = mdelem;
grpc_call_next_op(cur_elem, &metadata_op);
}
@@ -221,3 +232,12 @@ void grpc_call_element_send_cancel(grpc_call_element *cur_elem) {
cancel_op.user_data = NULL;
grpc_call_next_op(cur_elem, &cancel_op);
}
+
+void grpc_call_element_send_finish(grpc_call_element *cur_elem) {
+ grpc_call_op cancel_op;
+ cancel_op.type = GRPC_SEND_FINISH;
+ cancel_op.dir = GRPC_CALL_DOWN;
+ cancel_op.done_cb = do_nothing;
+ cancel_op.user_data = NULL;
+ grpc_call_next_op(cur_elem, &cancel_op);
+}
diff --git a/src/core/channel/channel_stack.h b/src/core/channel/channel_stack.h
index eb68102b43..754e16f4ff 100644
--- a/src/core/channel/channel_stack.h
+++ b/src/core/channel/channel_stack.h
@@ -69,6 +69,8 @@ typedef enum {
GRPC_SEND_START,
/* send a message to the channels peer */
GRPC_SEND_MESSAGE,
+ /* send a pre-formatted message to the channels peer */
+ GRPC_SEND_PREFORMATTED_MESSAGE,
/* send half-close to the channels peer */
GRPC_SEND_FINISH,
/* request that more data be allowed through flow control */
@@ -292,7 +294,10 @@ void grpc_call_log_op(char *file, int line, gpr_log_severity severity,
void grpc_call_element_send_metadata(grpc_call_element *cur_elem,
grpc_mdelem *elem);
+void grpc_call_element_recv_metadata(grpc_call_element *cur_elem,
+ grpc_mdelem *elem);
void grpc_call_element_send_cancel(grpc_call_element *cur_elem);
+void grpc_call_element_send_finish(grpc_call_element *cur_elem);
#ifdef GRPC_CHANNEL_STACK_TRACE
#define GRPC_CALL_LOG_OP(sev, elem, op) grpc_call_log_op(sev, elem, op)
diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c
index 55486ed5c3..adbeec0fc6 100644
--- a/src/core/channel/connected_channel.c
+++ b/src/core/channel/connected_channel.c
@@ -140,6 +140,8 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
grpc_sopb_add_begin_message(&calld->outgoing_sopb,
grpc_byte_buffer_length(op->data.message),
op->flags);
+ /* fall-through */
+ case GRPC_SEND_PREFORMATTED_MESSAGE:
copy_byte_buffer_to_stream_ops(op->data.message, &calld->outgoing_sopb);
calld->outgoing_buffer_length_estimate +=
(5 + grpc_byte_buffer_length(op->data.message));
diff --git a/src/core/channel/http_client_filter.c b/src/core/channel/http_client_filter.c
index b139b72795..1f6f3e0ca3 100644
--- a/src/core/channel/http_client_filter.c
+++ b/src/core/channel/http_client_filter.c
@@ -67,8 +67,8 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
/* Send : prefixed headers, which have to be before any application
* layer headers. */
calld->sent_headers = 1;
- grpc_call_element_send_metadata(elem, channeld->method);
- grpc_call_element_send_metadata(elem, channeld->scheme);
+ grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->method));
+ grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->scheme));
}
grpc_call_next_op(elem, op);
break;
@@ -76,12 +76,12 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
if (!calld->sent_headers) {
/* Send : prefixed headers, if we haven't already */
calld->sent_headers = 1;
- grpc_call_element_send_metadata(elem, channeld->method);
- grpc_call_element_send_metadata(elem, channeld->scheme);
+ grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->method));
+ grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->scheme));
}
/* Send non : prefixed headers */
- grpc_call_element_send_metadata(elem, channeld->te_trailers);
- grpc_call_element_send_metadata(elem, channeld->content_type);
+ grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->te_trailers));
+ grpc_call_element_send_metadata(elem, grpc_mdelem_ref(channeld->content_type));
grpc_call_next_op(elem, op);
break;
default:
diff --git a/src/core/channel/http_server_filter.c b/src/core/channel/http_server_filter.c
index 19b9606b43..2658a6d42e 100644
--- a/src/core/channel/http_server_filter.c
+++ b/src/core/channel/http_server_filter.c
@@ -34,29 +34,80 @@
#include "src/core/channel/http_server_filter.h"
#include <string.h>
+#include <grpc/grpc_http.h>
+#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
+typedef enum { NOT_RECEIVED, POST, GET } known_method_type;
+
+typedef struct {
+ grpc_mdelem *path;
+ grpc_mdelem *content_type;
+ grpc_byte_buffer *content;
+} gettable;
+
typedef struct call_data {
- int sent_status;
- int seen_scheme;
- int seen_method;
- int seen_te_trailers;
+ known_method_type seen_method;
+ gpr_uint8 sent_status;
+ gpr_uint8 seen_scheme;
+ gpr_uint8 seen_te_trailers;
+ grpc_mdelem *path;
} call_data;
typedef struct channel_data {
grpc_mdelem *te_trailers;
- grpc_mdelem *method;
+ grpc_mdelem *method_get;
+ grpc_mdelem *method_post;
grpc_mdelem *http_scheme;
grpc_mdelem *https_scheme;
/* TODO(klempner): Remove this once we stop using it */
grpc_mdelem *grpc_scheme;
grpc_mdelem *content_type;
- grpc_mdelem *status;
+ grpc_mdelem *status_ok;
+ grpc_mdelem *status_not_found;
+ grpc_mdstr *path_key;
+
+ size_t gettable_count;
+ gettable *gettables;
} channel_data;
/* used to silence 'variable not used' warnings */
static void ignore_unused(void *ignored) {}
+/* Handle 'GET': not technically grpc, so probably a web browser hitting
+ us */
+static void payload_done(void *elem, grpc_op_error error) {
+ if (error == GRPC_OP_OK) {
+ grpc_call_element_send_finish(elem);
+ }
+}
+
+static void handle_get(grpc_call_element *elem) {
+ channel_data *channeld = elem->channel_data;
+ call_data *calld = elem->call_data;
+ grpc_call_op op;
+ size_t i;
+
+ for (i = 0; i < channeld->gettable_count; i++) {
+ if (channeld->gettables[i].path == calld->path) {
+ grpc_call_element_send_metadata(elem,
+ grpc_mdelem_ref(channeld->status_ok));
+ grpc_call_element_send_metadata(
+ elem, grpc_mdelem_ref(channeld->gettables[i].content_type));
+ op.type = GRPC_SEND_PREFORMATTED_MESSAGE;
+ op.dir = GRPC_CALL_DOWN;
+ op.flags = 0;
+ op.data.message = channeld->gettables[i].content;
+ op.done_cb = payload_done;
+ op.user_data = elem;
+ grpc_call_next_op(elem, &op);
+ }
+ }
+ grpc_call_element_send_metadata(elem,
+ grpc_mdelem_ref(channeld->status_not_found));
+ grpc_call_element_send_finish(elem);
+}
+
/* Called either:
- in response to an API call (or similar) from above, to send something
- a network event (or similar) from below, to receive something
@@ -73,14 +124,17 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
case GRPC_RECV_METADATA:
/* Check if it is one of the headers we care about. */
if (op->data.metadata == channeld->te_trailers ||
- op->data.metadata == channeld->method ||
+ op->data.metadata == channeld->method_get ||
+ op->data.metadata == channeld->method_post ||
op->data.metadata == channeld->http_scheme ||
op->data.metadata == channeld->https_scheme ||
op->data.metadata == channeld->grpc_scheme ||
op->data.metadata == channeld->content_type) {
/* swallow it */
- if (op->data.metadata == channeld->method) {
- calld->seen_method = 1;
+ if (op->data.metadata == channeld->method_get) {
+ calld->seen_method = GET;
+ } else if (op->data.metadata == channeld->method_post) {
+ calld->seen_method = POST;
} else if (op->data.metadata->key == channeld->http_scheme->key) {
calld->seen_scheme = 1;
} else if (op->data.metadata == channeld->te_trailers) {
@@ -108,7 +162,7 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
grpc_mdelem_unref(op->data.metadata);
op->done_cb(op->user_data, GRPC_OP_OK);
} else if (op->data.metadata->key == channeld->te_trailers->key ||
- op->data.metadata->key == channeld->method->key ||
+ op->data.metadata->key == channeld->method_post->key ||
op->data.metadata->key == channeld->http_scheme->key ||
op->data.metadata->key == channeld->content_type->key) {
gpr_log(GPR_ERROR, "Invalid %s: header: '%s'",
@@ -120,6 +174,13 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
grpc_mdelem_unref(op->data.metadata);
op->done_cb(op->user_data, GRPC_OP_OK);
grpc_call_element_send_cancel(elem);
+ } else if (op->data.metadata->key == channeld->path_key) {
+ if (calld->path != NULL) {
+ gpr_log(GPR_ERROR, "Received :path twice");
+ grpc_mdelem_unref(calld->path);
+ }
+ calld->path = op->data.metadata;
+ op->done_cb(op->user_data, GRPC_OP_OK);
} else {
/* pass the event up */
grpc_call_next_op(elem, op);
@@ -129,14 +190,21 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
/* Have we seen the required http2 transport headers?
(:method, :scheme, content-type, with :path and :authority covered
at the channel level right now) */
- if (calld->seen_method && calld->seen_scheme && calld->seen_te_trailers) {
+ if (calld->seen_method == POST && calld->seen_scheme &&
+ calld->seen_te_trailers && calld->path) {
+ grpc_call_element_recv_metadata(elem, calld->path);
+ calld->path = NULL;
grpc_call_next_op(elem, op);
+ } else if (calld->seen_method == GET) {
+ handle_get(elem);
} else {
- if (!calld->seen_method) {
+ if (calld->seen_method == NOT_RECEIVED) {
gpr_log(GPR_ERROR, "Missing :method header");
- } else if (!calld->seen_scheme) {
+ }
+ if (!calld->seen_scheme) {
gpr_log(GPR_ERROR, "Missing :scheme header");
- } else if (!calld->seen_te_trailers) {
+ }
+ if (!calld->seen_te_trailers) {
gpr_log(GPR_ERROR, "Missing te trailers header");
}
/* Error this call out */
@@ -151,7 +219,8 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem,
if (!calld->sent_status) {
calld->sent_status = 1;
/* status is reffed by grpc_call_element_send_metadata */
- grpc_call_element_send_metadata(elem, channeld->status);
+ grpc_call_element_send_metadata(elem,
+ grpc_mdelem_ref(channeld->status_ok));
}
grpc_call_next_op(elem, op);
break;
@@ -189,9 +258,10 @@ static void init_call_elem(grpc_call_element *elem,
ignore_unused(channeld);
/* initialize members */
+ calld->path = NULL;
calld->sent_status = 0;
calld->seen_scheme = 0;
- calld->seen_method = 0;
+ calld->seen_method = NOT_RECEIVED;
calld->seen_te_trailers = 0;
}
@@ -201,14 +271,20 @@ static void destroy_call_elem(grpc_call_element *elem) {
call_data *calld = elem->call_data;
channel_data *channeld = elem->channel_data;
- ignore_unused(calld);
ignore_unused(channeld);
+
+ if (calld->path) {
+ grpc_mdelem_unref(calld->path);
+ }
}
/* Constructor for channel_data */
static void init_channel_elem(grpc_channel_element *elem,
const grpc_channel_args *args, grpc_mdctx *mdctx,
int is_first, int is_last) {
+ size_t i;
+ size_t gettable_capacity = 0;
+
/* grab pointers to our data from the channel element */
channel_data *channeld = elem->channel_data;
@@ -220,13 +296,40 @@ static void init_channel_elem(grpc_channel_element *elem,
/* initialize members */
channeld->te_trailers = grpc_mdelem_from_strings(mdctx, "te", "trailers");
- channeld->status = grpc_mdelem_from_strings(mdctx, ":status", "200");
- channeld->method = grpc_mdelem_from_strings(mdctx, ":method", "POST");
+ channeld->status_ok = grpc_mdelem_from_strings(mdctx, ":status", "200");
+ channeld->status_not_found =
+ grpc_mdelem_from_strings(mdctx, ":status", "404");
+ channeld->method_post = grpc_mdelem_from_strings(mdctx, ":method", "POST");
+ channeld->method_get = grpc_mdelem_from_strings(mdctx, ":method", "GET");
channeld->http_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "http");
channeld->https_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "https");
channeld->grpc_scheme = grpc_mdelem_from_strings(mdctx, ":scheme", "grpc");
+ channeld->path_key = grpc_mdstr_from_string(mdctx, ":path");
channeld->content_type =
grpc_mdelem_from_strings(mdctx, "content-type", "application/grpc");
+
+ /* initialize http download support */
+ channeld->gettable_count = 0;
+ channeld->gettables = NULL;
+ for (i = 0; i < args->num_args; i++) {
+ if (0 == strcmp(args->args[i].key, GRPC_ARG_SERVE_OVER_HTTP)) {
+ gettable *g;
+ gpr_slice slice;
+ grpc_http_server_page *p = args->args[i].value.pointer.p;
+ if (channeld->gettable_count == gettable_capacity) {
+ gettable_capacity =
+ GPR_MAX(gettable_capacity * 3 / 2, gettable_capacity + 1);
+ channeld->gettables =
+ gpr_realloc(channeld->gettables, gettable_capacity * sizeof(gettable));
+ }
+ g = &channeld->gettables[channeld->gettable_count++];
+ g->path = grpc_mdelem_from_strings(mdctx, ":path", p->path);
+ g->content_type =
+ grpc_mdelem_from_strings(mdctx, "content-type", p->content_type);
+ slice = gpr_slice_from_copied_string(p->content);
+ g->content = grpc_byte_buffer_create(&slice, 1);
+ }
+ }
}
/* Destructor for channel data */
@@ -235,19 +338,18 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
channel_data *channeld = elem->channel_data;
grpc_mdelem_unref(channeld->te_trailers);
- grpc_mdelem_unref(channeld->status);
- grpc_mdelem_unref(channeld->method);
+ grpc_mdelem_unref(channeld->status_ok);
+ grpc_mdelem_unref(channeld->status_not_found);
+ grpc_mdelem_unref(channeld->method_post);
+ grpc_mdelem_unref(channeld->method_get);
grpc_mdelem_unref(channeld->http_scheme);
grpc_mdelem_unref(channeld->https_scheme);
grpc_mdelem_unref(channeld->grpc_scheme);
grpc_mdelem_unref(channeld->content_type);
+ grpc_mdstr_unref(channeld->path_key);
}
const grpc_channel_filter grpc_http_server_filter = {
- call_op, channel_op,
-
- sizeof(call_data), init_call_elem, destroy_call_elem,
-
- sizeof(channel_data), init_channel_elem, destroy_channel_elem,
-
- "http-server"};
+ call_op, channel_op, sizeof(call_data),
+ init_call_elem, destroy_call_elem, sizeof(channel_data),
+ init_channel_elem, destroy_channel_elem, "http-server"};
diff --git a/src/core/security/auth.c b/src/core/security/auth.c
index f743b25838..9d0c075bc3 100644
--- a/src/core/security/auth.c
+++ b/src/core/security/auth.c
@@ -57,7 +57,7 @@ static void on_credentials_metadata(void *user_data, grpc_mdelem **md_elems,
grpc_call_element *elem = (grpc_call_element *)user_data;
size_t i;
for (i = 0; i < num_md; i++) {
- grpc_call_element_send_metadata(elem, md_elems[i]);
+ grpc_call_element_send_metadata(elem, grpc_mdelem_ref(md_elems[i]));
}
grpc_call_next_op(elem, &((call_data *)elem->call_data)->op);
}
diff --git a/src/core/surface/call.c b/src/core/surface/call.c
index 14d990df6a..bbd705b0c9 100644
--- a/src/core/surface/call.c
+++ b/src/core/surface/call.c
@@ -981,3 +981,8 @@ void grpc_call_set_deadline(grpc_call_element *elem, gpr_timespec deadline) {
call->have_alarm = 1;
grpc_alarm_init(&call->alarm, deadline, call_alarm, call, gpr_now());
}
+
+grpc_call_stack *grpc_call_get_call_stack(grpc_call *call) {
+ return CALL_STACK_FROM_CALL(call);
+}
+
diff --git a/src/core/surface/call.h b/src/core/surface/call.h
index 01605bb38a..804b387cb1 100644
--- a/src/core/surface/call.h
+++ b/src/core/surface/call.h
@@ -64,6 +64,8 @@ void grpc_call_client_initial_metadata_complete(
void grpc_call_set_deadline(grpc_call_element *surface_element,
gpr_timespec deadline);
+grpc_call_stack *grpc_call_get_call_stack(grpc_call *call);
+
/* Given the top call_element, get the call object. */
grpc_call *grpc_call_from_top_element(grpc_call_element *surface_element);
diff --git a/src/ruby/bin/interop/interop_client.rb b/src/ruby/bin/interop/interop_client.rb
index 0ea7f376be..86739b7b67 100755
--- a/src/ruby/bin/interop/interop_client.rb
+++ b/src/ruby/bin/interop/interop_client.rb
@@ -54,6 +54,8 @@ require 'test/cpp/interop/test_services'
require 'test/cpp/interop/messages'
require 'test/cpp/interop/empty'
+require 'signet/ssl_config'
+
# loads the certificates used to access the test server securely.
def load_test_certs
this_dir = File.expand_path(File.dirname(__FILE__))
@@ -62,21 +64,49 @@ def load_test_certs
files.map { |f| File.open(File.join(data_dir, f)).read }
end
+# loads the certificates used to access the test server securely.
+def load_prod_cert
+ fail 'could not find a production cert' if ENV['SSL_CERT_FILE'].nil?
+ p "loading prod certs from #{ENV['SSL_CERT_FILE']}"
+ File.open(ENV['SSL_CERT_FILE']).read
+end
+
# creates a Credentials from the test certificates.
def test_creds
certs = load_test_certs
GRPC::Core::Credentials.new(certs[0])
end
+RX_CERT = /-----BEGIN CERTIFICATE-----\n.*?-----END CERTIFICATE-----\n/m
+
+
+# creates a Credentials from the production certificates.
+def prod_creds
+ cert_text = load_prod_cert
+ GRPC::Core::Credentials.new(cert_text)
+end
+
# creates a test stub that accesses host:port securely.
-def create_stub(host, port)
+def create_stub(host, port, is_secure, host_override, use_test_ca)
address = "#{host}:#{port}"
- stub_opts = {
- :creds => test_creds,
- GRPC::Core::Channel::SSL_TARGET => 'foo.test.google.com'
- }
- logger.info("... connecting securely to #{address}")
- Grpc::Testing::TestService::Stub.new(address, **stub_opts)
+ if is_secure
+ creds = nil
+ if use_test_ca
+ creds = test_creds
+ else
+ creds = prod_creds
+ end
+
+ stub_opts = {
+ :creds => creds,
+ GRPC::Core::Channel::SSL_TARGET => host_override
+ }
+ logger.info("... connecting securely to #{address}")
+ Grpc::Testing::TestService::Stub.new(address, **stub_opts)
+ else
+ logger.info("... connecting insecurely to #{address}")
+ Grpc::Testing::TestService::Stub.new(address)
+ end
end
# produces a string of null chars (\0) of length l.
@@ -133,20 +163,12 @@ class NamedTests
@stub = stub
end
- # TESTING
- # PASSED
- # FAIL
- # ruby server: fails protobuf-ruby can't pass an empty message
def empty_unary
resp = @stub.empty_call(Empty.new)
assert resp.is_a?(Empty), 'empty_unary: invalid response'
p 'OK: empty_unary'
end
- # TESTING
- # PASSED
- # ruby server
- # FAILED
def large_unary
req_size, wanted_response_size = 271_828, 314_159
payload = Payload.new(type: :COMPRESSABLE, body: nulls(req_size))
@@ -163,10 +185,6 @@ class NamedTests
p 'OK: large_unary'
end
- # TESTING:
- # PASSED
- # ruby server
- # FAILED
def client_streaming
msg_sizes = [27_182, 8, 1828, 45_904]
wanted_aggregate_size = 74_922
@@ -180,10 +198,6 @@ class NamedTests
p 'OK: client_streaming'
end
- # TESTING:
- # PASSED
- # ruby server
- # FAILED
def server_streaming
msg_sizes = [31_415, 9, 2653, 58_979]
response_spec = msg_sizes.map { |s| ResponseParameters.new(size: s) }
@@ -200,10 +214,6 @@ class NamedTests
p 'OK: server_streaming'
end
- # TESTING:
- # PASSED
- # ruby server
- # FAILED
def ping_pong
msg_sizes = [[27_182, 31_415], [8, 9], [1828, 2653], [45_904, 58_979]]
ppp = PingPongPlayer.new(msg_sizes)
@@ -211,12 +221,23 @@ class NamedTests
resps.each { |r| ppp.queue.push(r) }
p 'OK: ping_pong'
end
+
+ def all
+ all_methods = NamedTests.instance_methods(false).map(&:to_s)
+ all_methods.each do |m|
+ next if m == 'all' || m.start_with?('assert')
+ p "TESTCASE: #{m}"
+ method(m).call
+ end
+ end
end
# validates the the command line options, returning them as a Hash.
def parse_options
options = {
+ 'secure' => false,
'server_host' => nil,
+ 'server_host_override' => nil,
'server_port' => nil,
'test_case' => nil
}
@@ -225,6 +246,10 @@ def parse_options
opts.on('--server_host SERVER_HOST', 'server hostname') do |v|
options['server_host'] = v
end
+ opts.on('--server_host_override HOST_OVERRIDE',
+ 'override host via a HTTP header') do |v|
+ options['server_host_override'] = v
+ end
opts.on('--server_port SERVER_PORT', 'server port') do |v|
options['server_port'] = v
end
@@ -235,19 +260,33 @@ def parse_options
" (#{test_case_list})") do |v|
options['test_case'] = v
end
+ opts.on('-s', '--use_tls', 'require a secure connection?') do |v|
+ options['secure'] = v
+ end
+ opts.on('-t', '--use_test_ca',
+ 'if secure, use the test certificate?') do |v|
+ options['use_test_ca'] = v
+ end
end.parse!
+ _check_options(options)
+end
+def _check_options(opts)
%w(server_host server_port test_case).each do |arg|
- if options[arg].nil?
+ if opts[arg].nil?
fail(OptionParser::MissingArgument, "please specify --#{arg}")
end
end
- options
+ if opts['server_host_override'].nil?
+ opts['server_host_override'] = opts['server_host']
+ end
+ opts
end
def main
opts = parse_options
- stub = create_stub(opts['server_host'], opts['server_port'])
+ stub = create_stub(opts['server_host'], opts['server_port'], opts['secure'],
+ opts['server_host_override'], opts['use_test_ca'])
NamedTests.new(stub).method(opts['test_case']).call
end
diff --git a/src/ruby/bin/interop/interop_server.rb b/src/ruby/bin/interop/interop_server.rb
index 83212823f6..cc4d260879 100755
--- a/src/ruby/bin/interop/interop_server.rb
+++ b/src/ruby/bin/interop/interop_server.rb
@@ -154,13 +154,17 @@ end
# validates the the command line options, returning them as a Hash.
def parse_options
options = {
- 'port' => nil
+ 'port' => nil,
+ 'secure' => false
}
OptionParser.new do |opts|
opts.banner = 'Usage: --port port'
opts.on('--port PORT', 'server port') do |v|
options['port'] = v
end
+ opts.on('-s', '--use_tls', 'require a secure connection?') do |v|
+ options['secure'] = v
+ end
end.parse!
if options['port'].nil?
@@ -172,10 +176,15 @@ end
def main
opts = parse_options
host = "0.0.0.0:#{opts['port']}"
- s = GRPC::RpcServer.new(creds: test_server_creds)
- s.add_http2_port(host, true)
- logger.info("... running securely on #{host}")
-
+ if opts['secure']
+ s = GRPC::RpcServer.new(creds: test_server_creds)
+ s.add_http2_port(host, true)
+ logger.info("... running securely on #{host}")
+ else
+ s = GRPC::RpcServer.new
+ s.add_http2_port(host)
+ logger.info("... running insecurely on #{host}")
+ end
s.handle(TestTarget)
s.run
end
diff --git a/src/ruby/grpc.gemspec b/src/ruby/grpc.gemspec
index 450362f5a8..ffd084dc91 100755
--- a/src/ruby/grpc.gemspec
+++ b/src/ruby/grpc.gemspec
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
s.add_dependency 'xray'
s.add_dependency 'logging', '~> 1.8'
s.add_dependency 'google-protobuf', '~> 3.0.0alpha.1.1'
+ s.add_dependency 'signet', '~> 0.5.1'
s.add_dependency 'minitest', '~> 5.4' # reqd for interop tests
s.add_development_dependency 'bundler', '~> 1.7'
diff --git a/test/core/echo/server.c b/test/core/echo/server.c
index 57b083779c..6b67334248 100644
--- a/test/core/echo/server.c
+++ b/test/core/echo/server.c
@@ -32,6 +32,8 @@
*/
#include <grpc/grpc.h>
+#include <grpc/grpc_http.h>
+#include <grpc/grpc_security.h>
#include <signal.h>
#include <stdio.h>
@@ -42,10 +44,12 @@
#include "src/core/support/string.h"
#include "test/core/util/test_config.h"
#include <grpc/support/alloc.h>
+#include <grpc/support/cmdline.h>
#include <grpc/support/host_port.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include "test/core/util/port.h"
+#include "test/core/end2end/data/ssl_test_data.h"
static grpc_completion_queue *cq;
static grpc_server *server;
@@ -83,29 +87,74 @@ static void sigint_handler(int x) { got_sigint = 1; }
int main(int argc, char **argv) {
grpc_event *ev;
- char *addr;
call_state *s;
+ char *addr_buf = NULL;
+ gpr_cmdline *cl;
int shutdown_started = 0;
int shutdown_finished = 0;
- grpc_test_init(argc, argv);
+ int secure = 0;
+ char *addr = NULL;
+
+ char *fake_argv[1];
+
+#define MAX_ARGS 4
+ grpc_arg arge[MAX_ARGS];
+ grpc_arg *e;
+ grpc_channel_args args = {0, NULL};
+
+ grpc_http_server_page home_page = {"/", "text/html",
+ "<head>\n"
+ "<title>Echo Server</title>\n"
+ "</head>\n"
+ "<body>\n"
+ "Welcome to the world of the future!\n"
+ "</body>\n"};
+
+ GPR_ASSERT(argc >= 1);
+ fake_argv[0] = argv[0];
+ grpc_test_init(1, fake_argv);
grpc_init();
srand(clock());
-
- if (argc == 2) {
- addr = gpr_strdup(argv[1]);
- } else {
- gpr_join_host_port(&addr, "::", grpc_pick_unused_port_or_die());
+ memset(arge, 0, sizeof(arge));
+ args.args = arge;
+
+ cl = gpr_cmdline_create("echo server");
+ gpr_cmdline_add_string(cl, "bind", "Bind host:port", &addr);
+ gpr_cmdline_add_flag(cl, "secure", "Run with security?", &secure);
+ gpr_cmdline_parse(cl, argc, argv);
+ gpr_cmdline_destroy(cl);
+
+ e = &arge[args.num_args++];
+ e->type = GRPC_ARG_POINTER;
+ e->key = GRPC_ARG_SERVE_OVER_HTTP;
+ e->value.pointer.p = &home_page;
+
+ if (addr == NULL) {
+ gpr_join_host_port(&addr_buf, "::", grpc_pick_unused_port_or_die());
+ addr = addr_buf;
}
gpr_log(GPR_INFO, "creating server on: %s", addr);
cq = grpc_completion_queue_create();
- server = grpc_server_create(cq, NULL);
- GPR_ASSERT(grpc_server_add_http2_port(server, addr));
- gpr_free(addr);
+ if (secure) {
+ grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {test_server1_key,
+ test_server1_cert};
+ grpc_server_credentials *ssl_creds =
+ grpc_ssl_server_credentials_create(NULL, &pem_key_cert_pair, 1);
+ server = grpc_secure_server_create(ssl_creds, cq, &args);
+ GPR_ASSERT(grpc_server_add_secure_http2_port(server, addr));
+ grpc_server_credentials_release(ssl_creds);
+ } else {
+ server = grpc_server_create(cq, &args);
+ GPR_ASSERT(grpc_server_add_http2_port(server, addr));
+ }
grpc_server_start(server);
+ gpr_free(addr_buf);
+ addr = addr_buf = NULL;
+
request_call();
signal(SIGINT, sigint_handler);
diff --git a/tools/dockerfile/grpc_ruby/Dockerfile b/tools/dockerfile/grpc_ruby/Dockerfile
index c677ceffff..f01f81d539 100644
--- a/tools/dockerfile/grpc_ruby/Dockerfile
+++ b/tools/dockerfile/grpc_ruby/Dockerfile
@@ -6,6 +6,9 @@ RUN cd /var/local/git/grpc \
&& git pull --recurse-submodules \
&& git submodule update --init --recursive
+# TODO: remove this, once make install is fixed
+RUN touch /var/local/git/grpc/include/grpc/support/string.h
+
# Build the C core.
RUN make install_c -C /var/local/git/grpc
@@ -18,5 +21,8 @@ RUN /bin/bash -l -c 'cd /var/local/git/grpc/src/ruby && bundle && rake compile:g
# - however, the interop server and client run OK, so this bug can be investigated
# RUN /bin/bash -l -c 'cd /var/local/git/grpc/src/ruby && bundle && rake'
+# Add a cacerts directory containing the Google root pem file, allowing the ruby client to access the production test instance
+ADD cacerts cacerts
+
# Specify the default command such that the interop server runs on its known testing port
-CMD ["/bin/bash", "-l", "-c", "ruby /var/local/git/grpc/src/ruby/bin/interop/interop_server.rb --port 8060"]
+CMD ["/bin/bash", "-l", "-c", "ruby /var/local/git/grpc/src/ruby/bin/interop/interop_server.rb --use_tls --port 8060"]
diff --git a/tools/gce_setup/grpc_docker.sh b/tools/gce_setup/grpc_docker.sh
index 3c2c5ae67a..16d6ef40e3 100755
--- a/tools/gce_setup/grpc_docker.sh
+++ b/tools/gce_setup/grpc_docker.sh
@@ -723,10 +723,27 @@ grpc_cloud_prod_test() {
grpc_interop_gen_ruby_cmd() {
local cmd_prefix="sudo docker run grpc/ruby bin/bash -l -c"
local test_script="/var/local/git/grpc/src/ruby/bin/interop/interop_client.rb"
- local the_cmd="$cmd_prefix 'ruby $test_script $@'"
+ local the_cmd="$cmd_prefix 'ruby $test_script --use_test_ca --use_tls $@'"
echo $the_cmd
}
+
+# constructs the full dockerized java interop test cmd.
+#
+# call-seq:
+# flags= .... # generic flags to include the command
+# cmd=$($grpc_gen_test_cmd $flags)
+grpc_cloud_prod_gen_ruby_cmd() {
+ local cmd_prefix="sudo docker run grpc/ruby bin/bash -l -c"
+ local test_script="/var/local/git/grpc/src/ruby/bin/interop/interop_client.rb"
+ local test_script+=" --use_tls"
+ local gfe_flags=" --server_port=443 --server_host=grpc-test.sandbox.google.com --server_host_override=grpc-test.sandbox.google.com"
+ local env_prefix="SSL_CERT_FILE=/cacerts/roots.pem"
+ local the_cmd="$cmd_prefix '$env_prefix ruby $test_script $gfe_flags $@'"
+ echo $the_cmd
+}
+
+
# constructs the full dockerized Go interop test cmd.
#
# call-seq:
@@ -803,7 +820,7 @@ grpc_interop_gen_cxx_cmd() {
# flags= .... # generic flags to include the command
# cmd=$($grpc_gen_test_cmd $flags)
grpc_cloud_prod_gen_cxx_cmd() {
- local cmd_prefix="sudo docker run grpc/cxx";
+ local cmd_prefix="sudo docker run grpc/cxx";
local test_script="/var/local/git/grpc/bins/opt/interop_client --enable_ssl";
local gfe_flags=" --use_prod_roots --server_port=443 --server_host=grpc-test.sandbox.google.com --server_host_override=grpc-test.sandbox.google.com"
local the_cmd="$cmd_prefix $test_script $gfe_flags $@";
diff --git a/tools/gce_setup/shared_startup_funcs.sh b/tools/gce_setup/shared_startup_funcs.sh
index f1dbca9a2e..69f6ba8cc0 100755
--- a/tools/gce_setup/shared_startup_funcs.sh
+++ b/tools/gce_setup/shared_startup_funcs.sh
@@ -405,14 +405,18 @@ grpc_dockerfile_install() {
# For specific base images, sync the ssh key into the .ssh dir in the dockerfile context
[[ $image_label == "grpc/base" ]] && {
- grpc_docker_sync_github_key $dockerfile_dir/.ssh 'base_ssh_key'|| return 1;
+ grpc_docker_sync_github_key $dockerfile_dir/.ssh 'base_ssh_key' || return 1;
}
[[ $image_label == "grpc/go" ]] && {
- grpc_docker_sync_github_key $dockerfile_dir/.ssh 'go_ssh_key'|| return 1;
+ grpc_docker_sync_github_key $dockerfile_dir/.ssh 'go_ssh_key' || return 1;
}
[[ $image_label == "grpc/java_base" ]] && {
- grpc_docker_sync_github_key $dockerfile_dir/.ssh 'java_base_ssh_key'|| return 1;
+ grpc_docker_sync_github_key $dockerfile_dir/.ssh 'java_base_ssh_key' || return 1;
}
+ [[ $image_label == "grpc/ruby" ]] && {
+ grpc_docker_sync_roots_pem $dockerfile_dir/cacerts || return 1;
+ }
+
# TODO(temiola): maybe make cache/no-cache a func option?
sudo docker build $cache_opt -t $image_label $dockerfile_dir || {
@@ -471,3 +475,31 @@ grpc_docker_sync_github_key() {
}
gsutil cp $src $gcs_key_path $local_key_path
}
+
+# grpc_docker_sync_roots_pem.
+#
+# Copies the root pems from GCS to the target dir
+#
+# call-seq:
+# grpc_docker_sync_roots_pem <target_dir>
+grpc_docker_sync_roots_pem() {
+ local target_dir=$1
+ [[ -n $target_dir ]] || { echo "$FUNCNAME: missing arg: target_dir" >&2; return 1; }
+
+ # determine the admin root; the parent of the dockerfile root,
+ local gs_dockerfile_root=$(load_metadata "attributes/gs_dockerfile_root")
+ [[ -n $gs_dockerfile_root ]] || {
+ echo "$FUNCNAME: missing metadata: gs_dockerfile_root" >&2
+ return 1
+ }
+ local gcs_admin_root=$(dirname $gs_dockerfile_root)
+
+ # cp the file from gsutil to a known local area
+ local gcs_certs_path=$gcs_admin_root/cacerts/roots.pem
+ local local_certs_path=$target_dir/roots.pem
+ mkdir -p $target_dir || {
+ echo "$FUNCNAME: could not create dir: $target_dir" 1>&2
+ return 1
+ }
+ gsutil cp $src $gcs_certs_path $local_certs_path
+}
diff --git a/tools/run_tests/run_lcov.sh b/tools/run_tests/run_lcov.sh
index 6f22b0e8a4..068213a3d2 100755
--- a/tools/run_tests/run_lcov.sh
+++ b/tools/run_tests/run_lcov.sh
@@ -7,7 +7,7 @@ out=`realpath ${1:-coverage}`
root=`realpath $(dirname $0)/../..`
tmp=`mktemp`
cd $root
-tools/run_tests/run_tests.py -c gcov
+tools/run_tests/run_tests.py -c gcov -l c c++
lcov --capture --directory . --output-file $tmp
genhtml $tmp --output-directory $out
rm $tmp
diff --git a/vsprojects/vs2013/gpr.vcxproj b/vsprojects/vs2013/gpr.vcxproj
index 6075e25ca0..f71b586aff 100644
--- a/vsprojects/vs2013/gpr.vcxproj
+++ b/vsprojects/vs2013/gpr.vcxproj
@@ -86,7 +86,6 @@
<ClInclude Include="..\..\include\grpc\support\port_platform.h" />
<ClInclude Include="..\..\include\grpc\support\slice.h" />
<ClInclude Include="..\..\include\grpc\support\slice_buffer.h" />
- <ClInclude Include="..\..\include\grpc\support\string.h" />
<ClInclude Include="..\..\include\grpc\support\sync.h" />
<ClInclude Include="..\..\include\grpc\support\sync_generic.h" />
<ClInclude Include="..\..\include\grpc\support\sync_posix.h" />
@@ -102,6 +101,7 @@
<ItemGroup>
<ClInclude Include="..\..\src\core\support\cpu.h" />
<ClInclude Include="..\..\src\core\support\murmur_hash.h" />
+ <ClInclude Include="..\..\src\core\support\string.h" />
<ClInclude Include="..\..\src\core\support\thd_internal.h" />
</ItemGroup>
<ItemGroup>
diff --git a/vsprojects/vs2013/gpr.vcxproj.filters b/vsprojects/vs2013/gpr.vcxproj.filters
index c1fccfff3b..013ed4b3c9 100644
--- a/vsprojects/vs2013/gpr.vcxproj.filters
+++ b/vsprojects/vs2013/gpr.vcxproj.filters
@@ -120,9 +120,6 @@
<ClInclude Include="..\..\include\grpc\support\slice_buffer.h">
<Filter>include\grpc\support</Filter>
</ClInclude>
- <ClInclude Include="..\..\include\grpc\support\string.h">
- <Filter>include\grpc\support</Filter>
- </ClInclude>
<ClInclude Include="..\..\include\grpc\support\sync.h">
<Filter>include\grpc\support</Filter>
</ClInclude>
@@ -164,6 +161,9 @@
<ClInclude Include="..\..\src\core\support\murmur_hash.h">
<Filter>src\core\support</Filter>
</ClInclude>
+ <ClInclude Include="..\..\src\core\support\string.h">
+ <Filter>src\core\support</Filter>
+ </ClInclude>
<ClInclude Include="..\..\src\core\support\thd_internal.h">
<Filter>src\core\support</Filter>
</ClInclude>