Merge pull request #151 from ctiller/javascript

Allow serving GET's from GRPC HTTP2 servers.
pull/209/head^2
Yang Gao 10 years ago
commit 1e99756b58
  1. 67
      include/grpc/grpc_http.h
  2. 3
      src/core/channel/call_op_string.c
  3. 8
      src/core/channel/channel_args.c
  4. 22
      src/core/channel/channel_stack.c
  5. 5
      src/core/channel/channel_stack.h
  6. 2
      src/core/channel/connected_channel.c
  7. 12
      src/core/channel/http_client_filter.c
  8. 158
      src/core/channel/http_server_filter.c
  9. 2
      src/core/security/auth.c
  10. 69
      test/core/echo/server.c

@ -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__ */

@ -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;

@ -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);

@ -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);
}

@ -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)

@ -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));

@ -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:

@ -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"};

@ -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);
}

@ -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);

Loading…
Cancel
Save