aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/channel/channel_stack.h16
-rw-r--r--src/core/channel/client_channel.c6
-rw-r--r--src/core/channel/connected_channel.c33
-rw-r--r--src/core/endpoint/resolve_address.c19
-rw-r--r--src/core/endpoint/secure_endpoint.c21
-rw-r--r--src/core/endpoint/socket_utils.c160
-rw-r--r--src/core/endpoint/socket_utils.h80
-rw-r--r--src/core/endpoint/tcp.c35
-rw-r--r--src/core/endpoint/tcp.h4
-rw-r--r--src/core/endpoint/tcp_client.c56
-rw-r--r--src/core/endpoint/tcp_client.h5
-rw-r--r--src/core/endpoint/tcp_server.c86
-rw-r--r--src/core/endpoint/tcp_server.h22
-rw-r--r--src/core/eventmanager/em.c2
-rw-r--r--src/core/eventmanager/em.h4
-rw-r--r--src/core/security/auth.c2
-rw-r--r--src/core/security/credentials.c235
-rw-r--r--src/core/security/credentials.h14
-rw-r--r--src/core/security/security_context.c71
-rw-r--r--src/core/security/security_context.h11
-rw-r--r--src/core/security/server_secure_chttp2.c2
-rw-r--r--src/core/support/cpu_posix.c5
-rw-r--r--src/core/support/string_posix.c6
-rw-r--r--src/core/support/string_win32.c81
-rw-r--r--src/core/support/thd_posix.c6
-rw-r--r--src/core/support/thd_win32.c4
-rw-r--r--src/core/support/time_posix.c7
-rw-r--r--src/core/support/time_win32.c6
-rw-r--r--src/core/surface/call.c68
-rw-r--r--src/core/surface/channel.c11
-rw-r--r--src/core/surface/client.c3
-rw-r--r--src/core/surface/completion_queue.c8
-rw-r--r--src/core/surface/lame_client.c12
-rw-r--r--src/core/surface/lame_client.h2
-rw-r--r--src/core/surface/server.c41
-rw-r--r--src/core/surface/server_chttp2.c2
-rw-r--r--src/core/transport/chttp2/alpn.c45
-rw-r--r--src/core/transport/chttp2/alpn.h44
-rw-r--r--src/core/transport/chttp2/bin_encoder.c271
-rw-r--r--src/core/transport/chttp2/bin_encoder.h54
-rw-r--r--src/core/transport/chttp2/frame.h6
-rw-r--r--src/core/transport/chttp2/frame_goaway.c189
-rw-r--r--src/core/transport/chttp2/frame_goaway.h74
-rw-r--r--src/core/transport/chttp2/gen_hpack_tables.c300
-rw-r--r--src/core/transport/chttp2/huffsyms.c297
-rw-r--r--src/core/transport/chttp2/huffsyms.h48
-rw-r--r--src/core/transport/chttp2_transport.c107
-rw-r--r--src/core/transport/transport.c5
-rw-r--r--src/core/transport/transport.h8
-rw-r--r--src/core/transport/transport_impl.h4
-rw-r--r--src/core/tsi/ssl_transport_security.c22
-rw-r--r--src/cpp/client/channel.cc135
-rw-r--r--src/cpp/client/client_context.cc8
-rw-r--r--src/cpp/server/async_server_context.cc15
-rw-r--r--src/cpp/server/rpc_service_method.h90
-rw-r--r--src/cpp/server/server_credentials.cc62
-rw-r--r--src/cpp/server/server_rpc_handler.cc86
-rw-r--r--src/cpp/stream/stream_context.cc252
-rw-r--r--src/cpp/stream/stream_context.h50
-rwxr-xr-xsrc/ruby/README.md15
-rwxr-xr-xsrc/ruby/Rakefile16
-rwxr-xr-xsrc/ruby/bin/math.pb.rb2
-rw-r--r--src/ruby/bin/math_client.rb53
-rw-r--r--src/ruby/bin/math_server.rb40
-rw-r--r--src/ruby/ext/grpc/extconf.rb4
-rw-r--r--src/ruby/ext/grpc/rb_byte_buffer.c2
-rw-r--r--src/ruby/ext/grpc/rb_call.c6
-rw-r--r--src/ruby/ext/grpc/rb_channel.c36
-rw-r--r--src/ruby/ext/grpc/rb_completion_queue.c2
-rw-r--r--src/ruby/ext/grpc/rb_credentials.c301
-rw-r--r--src/ruby/ext/grpc/rb_credentials.h50
-rw-r--r--src/ruby/ext/grpc/rb_event.c6
-rw-r--r--src/ruby/ext/grpc/rb_grpc.c12
-rw-r--r--src/ruby/ext/grpc/rb_grpc.h4
-rw-r--r--src/ruby/ext/grpc/rb_metadata.c2
-rw-r--r--src/ruby/ext/grpc/rb_server.c78
-rw-r--r--src/ruby/ext/grpc/rb_server.h2
-rw-r--r--src/ruby/ext/grpc/rb_server_credentials.c215
-rw-r--r--src/ruby/ext/grpc/rb_server_credentials.h50
-rw-r--r--src/ruby/ext/grpc/rb_status.c5
-rw-r--r--src/ruby/lib/grpc.rb5
-rw-r--r--src/ruby/lib/grpc/beefcake.rb62
-rw-r--r--src/ruby/lib/grpc/core/event.rb (renamed from src/ruby/lib/grpc/event.rb)8
-rw-r--r--src/ruby/lib/grpc/core/time_consts.rb (renamed from src/ruby/lib/grpc/time_consts.rb)61
-rw-r--r--src/ruby/lib/grpc/generic/active_call.rb43
-rw-r--r--src/ruby/lib/grpc/generic/bidi_call.rb17
-rw-r--r--src/ruby/lib/grpc/generic/client_stub.rb20
-rw-r--r--src/ruby/lib/grpc/generic/rpc_desc.rb9
-rw-r--r--src/ruby/lib/grpc/generic/rpc_server.rb25
-rw-r--r--src/ruby/lib/grpc/generic/service.rb18
-rw-r--r--src/ruby/spec/alloc_spec.rb8
-rw-r--r--src/ruby/spec/byte_buffer_spec.rb14
-rw-r--r--src/ruby/spec/call_spec.rb42
-rw-r--r--src/ruby/spec/channel_spec.rb229
-rw-r--r--src/ruby/spec/client_server_spec.rb44
-rw-r--r--src/ruby/spec/completion_queue_spec.rb17
-rw-r--r--src/ruby/spec/credentials_spec.rb87
-rw-r--r--src/ruby/spec/event_spec.rb8
-rw-r--r--src/ruby/spec/generic/active_call_spec.rb494
-rw-r--r--src/ruby/spec/generic/client_stub_spec.rb62
-rw-r--r--src/ruby/spec/generic/rpc_desc_spec.rb22
-rw-r--r--src/ruby/spec/generic/rpc_server_spec.rb498
-rw-r--r--src/ruby/spec/generic/service_spec.rb11
-rw-r--r--src/ruby/spec/metadata_spec.rb13
-rw-r--r--src/ruby/spec/server_credentials_spec.rb74
-rw-r--r--src/ruby/spec/server_spec.rb234
-rw-r--r--src/ruby/spec/status_spec.rb199
-rwxr-xr-xsrc/ruby/spec/testdata/README4
-rwxr-xr-xsrc/ruby/spec/testdata/ca.pem15
-rwxr-xr-xsrc/ruby/spec/testdata/server1.key16
-rwxr-xr-xsrc/ruby/spec/testdata/server1.pem16
-rw-r--r--src/ruby/spec/time_consts_spec.rb88
112 files changed, 4872 insertions, 1810 deletions
diff --git a/src/core/channel/channel_stack.h b/src/core/channel/channel_stack.h
index 0ae1005e67..67cd356ef9 100644
--- a/src/core/channel/channel_stack.h
+++ b/src/core/channel/channel_stack.h
@@ -124,9 +124,17 @@ typedef struct {
char *grpc_call_op_string(grpc_call_op *op);
typedef enum {
- GRPC_CHANNEL_SHUTDOWN,
+ /* send a goaway message to remote channels indicating that we are going
+ to disconnect in the future */
+ GRPC_CHANNEL_GOAWAY,
+ /* disconnect any underlying transports */
+ GRPC_CHANNEL_DISCONNECT,
+ /* transport received a new call */
GRPC_ACCEPT_CALL,
- GRPC_TRANSPORT_CLOSED
+ /* an underlying transport was closed */
+ GRPC_TRANSPORT_CLOSED,
+ /* an underlying transport is about to be closed */
+ GRPC_TRANSPORT_GOAWAY
} grpc_channel_op_type;
/* A single filterable operation to be performed on a channel */
@@ -142,6 +150,10 @@ typedef struct {
grpc_transport *transport;
const void *transport_server_data;
} accept_call;
+ struct {
+ grpc_status_code status;
+ gpr_slice message;
+ } goaway;
} data;
} grpc_channel_op;
diff --git a/src/core/channel/client_channel.c b/src/core/channel/client_channel.c
index 90563683d5..0ceffba62a 100644
--- a/src/core/channel/client_channel.c
+++ b/src/core/channel/client_channel.c
@@ -392,8 +392,14 @@ static void broadcast_channel_op_down(grpc_channel_element *elem,
/* send the message down */
for (i = 0; i < child_count; i++) {
child_elem = grpc_channel_stack_element(children[i], 0);
+ if (op->type == GRPC_CHANNEL_GOAWAY) {
+ gpr_slice_ref(op->data.goaway.message);
+ }
child_elem->filter->channel_op(child_elem, op);
}
+ if (op->type == GRPC_CHANNEL_GOAWAY) {
+ gpr_slice_unref(op->data.goaway.message);
+ }
/* unmark the inflight requests */
gpr_mu_lock(&chand->mu);
diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c
index 336472e740..fb2d5ad328 100644
--- a/src/core/channel/connected_channel.c
+++ b/src/core/channel/connected_channel.c
@@ -169,7 +169,11 @@ static void channel_op(grpc_channel_element *elem, grpc_channel_op *op) {
GPR_ASSERT(elem->filter == &grpc_connected_channel_filter);
switch (op->type) {
- case GRPC_CHANNEL_SHUTDOWN:
+ case GRPC_CHANNEL_GOAWAY:
+ grpc_transport_goaway(chand->transport, op->data.goaway.status,
+ op->data.goaway.message);
+ break;
+ case GRPC_CHANNEL_DISCONNECT:
grpc_transport_close(chand->transport);
break;
default:
@@ -439,7 +443,6 @@ static void recv_batch(void *user_data, grpc_transport *transport,
"Last message truncated; read %d bytes, expected %d",
calld->incoming_message.length,
calld->incoming_message_length);
- return;
}
call_op.type = GRPC_RECV_HALF_CLOSE;
call_op.dir = GRPC_CALL_UP;
@@ -458,6 +461,29 @@ static void recv_batch(void *user_data, grpc_transport *transport,
}
}
+static void transport_goaway(void *user_data, grpc_transport *transport,
+ grpc_status_code status, gpr_slice debug) {
+ /* transport got goaway ==> call up and handle it */
+ grpc_channel_element *elem = user_data;
+ channel_data *chand = elem->channel_data;
+ char *msg;
+ grpc_channel_op op;
+
+ GPR_ASSERT(elem->filter == &grpc_connected_channel_filter);
+ GPR_ASSERT(chand->transport == transport);
+
+ msg = gpr_hexdump((const char *)GPR_SLICE_START_PTR(debug),
+ GPR_SLICE_LENGTH(debug), GPR_HEXDUMP_PLAINTEXT);
+ gpr_log(GPR_DEBUG, "got goaway: status=%d, message=%s", status, msg);
+ gpr_free(msg);
+
+ op.type = GRPC_TRANSPORT_GOAWAY;
+ op.dir = GRPC_CALL_UP;
+ op.data.goaway.status = status;
+ op.data.goaway.message = debug;
+ channel_op(elem, &op);
+}
+
static void transport_closed(void *user_data, grpc_transport *transport) {
/* transport was closed ==> call up and handle it */
grpc_channel_element *elem = user_data;
@@ -473,7 +499,8 @@ static void transport_closed(void *user_data, grpc_transport *transport) {
}
const grpc_transport_callbacks connected_channel_transport_callbacks = {
- alloc_recv_buffer, accept_stream, recv_batch, transport_closed,
+ alloc_recv_buffer, accept_stream, recv_batch,
+ transport_goaway, transport_closed,
};
grpc_transport_setup_result grpc_connected_channel_bind_transport(
diff --git a/src/core/endpoint/resolve_address.c b/src/core/endpoint/resolve_address.c
index aa21954c6d..1993b9bdc5 100644
--- a/src/core/endpoint/resolve_address.c
+++ b/src/core/endpoint/resolve_address.c
@@ -41,10 +41,12 @@
#include <unistd.h>
#include <string.h>
+#include "src/core/endpoint/socket_utils.h"
#include <grpc/support/alloc.h>
#include <grpc/support/string.h>
#include <grpc/support/log.h>
#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
typedef struct {
char *name;
@@ -119,6 +121,7 @@ grpc_resolved_addresses *grpc_blocking_resolve_address(
int s;
size_t i;
grpc_resolved_addresses *addrs = NULL;
+ const gpr_timespec start_time = gpr_now();
/* parse name, splitting it into host and port parts */
split_host_port(name, &host, &port);
@@ -160,6 +163,22 @@ grpc_resolved_addresses *grpc_blocking_resolve_address(
i++;
}
+ /* Temporary logging, to help identify flakiness in dualstack_socket_test. */
+ {
+ const gpr_timespec delay = gpr_time_sub(gpr_now(), start_time);
+ const int delay_ms =
+ delay.tv_sec * GPR_MS_PER_SEC + delay.tv_nsec / GPR_NS_PER_MS;
+ gpr_log(GPR_INFO, "logspam: getaddrinfo(%s, %s) resolved %d addrs in %dms:",
+ host, port, addrs->naddrs, delay_ms);
+ for (i = 0; i < addrs->naddrs; i++) {
+ char *buf;
+ grpc_sockaddr_to_string(&buf, (struct sockaddr *)&addrs->addrs[i].addr,
+ 0);
+ gpr_log(GPR_INFO, "logspam: [%d] %s", i, buf);
+ gpr_free(buf);
+ }
+ }
+
done:
gpr_free(host);
gpr_free(port);
diff --git a/src/core/endpoint/secure_endpoint.c b/src/core/endpoint/secure_endpoint.c
index 4fab0faa03..ecf41d71a8 100644
--- a/src/core/endpoint/secure_endpoint.c
+++ b/src/core/endpoint/secure_endpoint.c
@@ -50,6 +50,8 @@ typedef struct {
/* saved upper level callbacks and user_data. */
grpc_endpoint_read_cb read_cb;
void *read_user_data;
+ grpc_endpoint_write_cb write_cb;
+ void *write_user_data;
/* saved handshaker leftover data to unprotect. */
gpr_slice_buffer leftover_bytes;
/* buffers for read and write */
@@ -208,6 +210,12 @@ static void flush_write_staging_buffer(secure_endpoint *ep, gpr_uint8 **cur,
*end = GPR_SLICE_END_PTR(ep->write_staging_buffer);
}
+static void on_write(void *data, grpc_endpoint_cb_status error) {
+ secure_endpoint *ep = data;
+ ep->write_cb(ep->write_user_data, error);
+ secure_endpoint_unref(ep);
+}
+
static grpc_endpoint_write_status write(grpc_endpoint *secure_ep,
gpr_slice *slices, size_t nslices,
grpc_endpoint_write_cb cb,
@@ -219,6 +227,7 @@ static grpc_endpoint_write_status write(grpc_endpoint *secure_ep,
secure_endpoint *ep = (secure_endpoint *)secure_ep;
gpr_uint8 *cur = GPR_SLICE_START_PTR(ep->write_staging_buffer);
gpr_uint8 *end = GPR_SLICE_END_PTR(ep->write_staging_buffer);
+ grpc_endpoint_write_status status;
GPR_ASSERT(ep->output_buffer.count == 0);
#ifdef GRPC_TRACE_SECURE_TRANSPORT
@@ -295,8 +304,16 @@ static grpc_endpoint_write_status write(grpc_endpoint *secure_ep,
/* clear output_buffer and let the lower level handle its slices. */
output_buffer_count = ep->output_buffer.count;
ep->output_buffer.count = 0;
- return grpc_endpoint_write(ep->wrapped_ep, ep->output_buffer.slices,
- output_buffer_count, cb, user_data, deadline);
+ ep->write_cb = cb;
+ ep->write_user_data = user_data;
+ /* Need to keep the endpoint alive across a transport */
+ secure_endpoint_ref(ep);
+ status = grpc_endpoint_write(ep->wrapped_ep, ep->output_buffer.slices,
+ output_buffer_count, on_write, ep, deadline);
+ if (status != GRPC_ENDPOINT_WRITE_PENDING) {
+ secure_endpoint_unref(ep);
+ }
+ return status;
}
static void shutdown(grpc_endpoint *secure_ep) {
diff --git a/src/core/endpoint/socket_utils.c b/src/core/endpoint/socket_utils.c
index 9c2540bcbf..ef160d7ea4 100644
--- a/src/core/endpoint/socket_utils.c
+++ b/src/core/endpoint/socket_utils.c
@@ -33,6 +33,7 @@
#include "src/core/endpoint/socket_utils.h"
+#include <arpa/inet.h>
#include <limits.h>
#include <fcntl.h>
#include <netinet/in.h>
@@ -44,6 +45,11 @@
#include <string.h>
#include <errno.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/string.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+
/* set a socket to non blocking mode */
int grpc_set_socket_nonblocking(int fd, int non_blocking) {
int oldflags = fcntl(fd, F_GETFL, 0);
@@ -103,3 +109,157 @@ int grpc_set_socket_low_latency(int fd, int low_latency) {
0 == getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen) &&
newval == val;
}
+
+/* This should be 0 in production, but it may be enabled for testing or
+ debugging purposes, to simulate an environment where IPv6 sockets can't
+ also speak IPv4. */
+int grpc_forbid_dualstack_sockets_for_testing = 0;
+
+static int set_socket_dualstack(int fd) {
+ if (!grpc_forbid_dualstack_sockets_for_testing) {
+ const int off = 0;
+ return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off));
+ } else {
+ /* Force an IPv6-only socket, for testing purposes. */
+ const int on = 1;
+ setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+ return 0;
+ }
+}
+
+int grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
+ int protocol, grpc_dualstack_mode *dsmode) {
+ int family = addr->sa_family;
+ if (family == AF_INET6) {
+ int fd = socket(family, type, protocol);
+ /* Check if we've got a valid dualstack socket. */
+ if (fd >= 0 && set_socket_dualstack(fd)) {
+ *dsmode = GRPC_DSMODE_DUALSTACK;
+ return fd;
+ }
+ /* If this isn't an IPv4 address, then return whatever we've got. */
+ if (!grpc_sockaddr_is_v4mapped(addr, NULL)) {
+ *dsmode = GRPC_DSMODE_IPV6;
+ return fd;
+ }
+ /* Fall back to AF_INET. */
+ if (fd >= 0) {
+ close(fd);
+ }
+ family = AF_INET;
+ }
+ *dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE;
+ return socket(family, type, protocol);
+}
+
+static const gpr_uint8 kV4MappedPrefix[] = {0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0xff, 0xff};
+
+int grpc_sockaddr_is_v4mapped(const struct sockaddr *addr,
+ struct sockaddr_in *addr4_out) {
+ GPR_ASSERT(addr != (struct sockaddr *)addr4_out);
+ if (addr->sa_family == AF_INET6) {
+ const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
+ if (memcmp(addr6->sin6_addr.s6_addr, kV4MappedPrefix,
+ sizeof(kV4MappedPrefix)) == 0) {
+ if (addr4_out != NULL) {
+ /* Normalize ::ffff:0.0.0.0/96 to IPv4. */
+ memset(addr4_out, 0, sizeof(*addr4_out));
+ addr4_out->sin_family = AF_INET;
+ /* s6_addr32 would be nice, but it's non-standard. */
+ memcpy(&addr4_out->sin_addr, &addr6->sin6_addr.s6_addr[12], 4);
+ addr4_out->sin_port = addr6->sin6_port;
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int grpc_sockaddr_to_v4mapped(const struct sockaddr *addr,
+ struct sockaddr_in6 *addr6_out) {
+ GPR_ASSERT(addr != (struct sockaddr *)addr6_out);
+ if (addr->sa_family == AF_INET) {
+ const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr;
+ memset(addr6_out, 0, sizeof(*addr6_out));
+ addr6_out->sin6_family = AF_INET6;
+ memcpy(&addr6_out->sin6_addr.s6_addr[0], kV4MappedPrefix, 12);
+ memcpy(&addr6_out->sin6_addr.s6_addr[12], &addr4->sin_addr, 4);
+ addr6_out->sin6_port = addr4->sin_port;
+ return 1;
+ }
+ return 0;
+}
+
+int grpc_sockaddr_is_wildcard(const struct sockaddr *addr, int *port_out) {
+ struct sockaddr_in addr4_normalized;
+ if (grpc_sockaddr_is_v4mapped(addr, &addr4_normalized)) {
+ addr = (struct sockaddr *)&addr4_normalized;
+ }
+ if (addr->sa_family == AF_INET) {
+ /* Check for 0.0.0.0 */
+ const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr;
+ if (addr4->sin_addr.s_addr != 0) {
+ return 0;
+ }
+ *port_out = ntohs(addr4->sin_port);
+ return 1;
+ } else if (addr->sa_family == AF_INET6) {
+ /* Check for :: */
+ const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
+ int i;
+ for (i = 0; i < 16; i++) {
+ if (addr6->sin6_addr.s6_addr[i] != 0) {
+ return 0;
+ }
+ }
+ *port_out = ntohs(addr6->sin6_port);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void grpc_sockaddr_make_wildcards(int port, struct sockaddr_in *wild4_out,
+ struct sockaddr_in6 *wild6_out) {
+ memset(wild4_out, 0, sizeof(*wild4_out));
+ wild4_out->sin_family = AF_INET;
+ wild4_out->sin_port = htons(port);
+
+ memset(wild6_out, 0, sizeof(*wild6_out));
+ wild6_out->sin6_family = AF_INET6;
+ wild6_out->sin6_port = htons(port);
+}
+
+int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr,
+ int normalize) {
+ const int save_errno = errno;
+ struct sockaddr_in addr_normalized;
+ char ntop_buf[INET6_ADDRSTRLEN];
+ const void *ip = NULL;
+ int port;
+ int ret;
+
+ *out = NULL;
+ if (normalize && grpc_sockaddr_is_v4mapped(addr, &addr_normalized)) {
+ addr = (const struct sockaddr *)&addr_normalized;
+ }
+ if (addr->sa_family == AF_INET) {
+ const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr;
+ ip = &addr4->sin_addr;
+ port = ntohs(addr4->sin_port);
+ } else if (addr->sa_family == AF_INET6) {
+ const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
+ ip = &addr6->sin6_addr;
+ port = ntohs(addr6->sin6_port);
+ }
+ if (ip != NULL &&
+ inet_ntop(addr->sa_family, ip, ntop_buf, sizeof(ntop_buf)) != NULL) {
+ ret = gpr_join_host_port(out, ntop_buf, port);
+ } else {
+ ret = gpr_asprintf(out, "(sockaddr family=%d)", addr->sa_family);
+ }
+ /* This is probably redundant, but we wouldn't want to log the wrong error. */
+ errno = save_errno;
+ return ret;
+}
diff --git a/src/core/endpoint/socket_utils.h b/src/core/endpoint/socket_utils.h
index 545d678eab..23fa19284a 100644
--- a/src/core/endpoint/socket_utils.h
+++ b/src/core/endpoint/socket_utils.h
@@ -38,6 +38,8 @@
#include <sys/socket.h>
struct sockaddr;
+struct sockaddr_in;
+struct sockaddr_in6;
/* a wrapper for accept or accept4 */
int grpc_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
@@ -55,4 +57,82 @@ int grpc_set_socket_reuse_addr(int fd, int reuse);
/* disable nagle */
int grpc_set_socket_low_latency(int fd, int low_latency);
+/* An enum to keep track of IPv4/IPv6 socket modes.
+
+ Currently, this information is only used when a socket is first created, but
+ in the future we may wish to store it alongside the fd. This would let calls
+ like sendto() know which family to use without asking the kernel first. */
+typedef enum grpc_dualstack_mode {
+ /* Uninitialized, or a non-IP socket. */
+ GRPC_DSMODE_NONE,
+ /* AF_INET only. */
+ GRPC_DSMODE_IPV4,
+ /* AF_INET6 only, because IPV6_V6ONLY could not be cleared. */
+ GRPC_DSMODE_IPV6,
+ /* AF_INET6, which also supports ::ffff-mapped IPv4 addresses. */
+ GRPC_DSMODE_DUALSTACK
+} grpc_dualstack_mode;
+
+/* Only tests should use this flag. */
+extern int grpc_forbid_dualstack_sockets_for_testing;
+
+/* Creates a new socket for connecting to (or listening on) an address.
+
+ If addr is AF_INET6, this creates an IPv6 socket first. If that fails,
+ and addr is within ::ffff:0.0.0.0/96, then it automatically falls back to
+ an IPv4 socket.
+
+ If addr is AF_INET, AF_UNIX, or anything else, then this is similar to
+ calling socket() directly.
+
+ Returns an fd on success, otherwise returns -1 with errno set to the result
+ of a failed socket() call.
+
+ The *dsmode output indicates which address family was actually created.
+ The recommended way to use this is:
+ - First convert to IPv6 using grpc_sockaddr_to_v4mapped().
+ - Create the socket.
+ - If *dsmode is IPV4, use grpc_sockaddr_is_v4mapped() to convert back to
+ IPv4, so that bind() or connect() see the correct family.
+ Also, it's important to distinguish between DUALSTACK and IPV6 when
+ listening on the [::] wildcard address. */
+int grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
+ int protocol, grpc_dualstack_mode *dsmode);
+
+/* Returns true if addr is an IPv4-mapped IPv6 address within the
+ ::ffff:0.0.0.0/96 range, or false otherwise.
+
+ If addr4_out is non-NULL, the inner IPv4 address will be copied here when
+ returning true. */
+int grpc_sockaddr_is_v4mapped(const struct sockaddr *addr,
+ struct sockaddr_in *addr4_out);
+
+/* If addr is an AF_INET address, writes the corresponding ::ffff:0.0.0.0/96
+ address to addr6_out and returns true. Otherwise returns false. */
+int grpc_sockaddr_to_v4mapped(const struct sockaddr *addr,
+ struct sockaddr_in6 *addr6_out);
+
+/* If addr is ::, 0.0.0.0, or ::ffff:0.0.0.0, writes the port number to
+ *port_out (if not NULL) and returns true, otherwise returns false. */
+int grpc_sockaddr_is_wildcard(const struct sockaddr *addr, int *port_out);
+
+/* Writes 0.0.0.0:port and [::]:port to separate sockaddrs. */
+void grpc_sockaddr_make_wildcards(int port, struct sockaddr_in *wild4_out,
+ struct sockaddr_in6 *wild6_out);
+
+/* Converts a sockaddr into a newly-allocated human-readable string.
+
+ Currently, only the AF_INET and AF_INET6 families are recognized.
+ If the normalize flag is enabled, ::ffff:0.0.0.0/96 IPv6 addresses are
+ displayed as plain IPv4.
+
+ Usage is similar to gpr_asprintf: returns the number of bytes written
+ (excluding the final '\0'), and *out points to a string which must later be
+ destroyed using gpr_free().
+
+ In the unlikely event of an error, returns -1 and sets *out to NULL.
+ The existing value of errno is always preserved. */
+int grpc_sockaddr_to_string(char **out, const struct sockaddr *addr,
+ int normalize);
+
#endif /* __GRPC_INTERNAL_ENDPOINT_SOCKET_UTILS_H__ */
diff --git a/src/core/endpoint/tcp.c b/src/core/endpoint/tcp.c
index 39367e80f6..482344d265 100644
--- a/src/core/endpoint/tcp.c
+++ b/src/core/endpoint/tcp.c
@@ -250,7 +250,7 @@ static void slice_state_remove_last(grpc_tcp_slice_state *state, size_t bytes) {
typedef struct {
grpc_endpoint base;
grpc_em *em;
- grpc_em_fd em_fd;
+ grpc_em_fd *em_fd;
int fd;
size_t slice_size;
gpr_refcount refcount;
@@ -277,14 +277,14 @@ grpc_endpoint *grpc_tcp_create(int fd, grpc_em *em) {
static void grpc_tcp_shutdown(grpc_endpoint *ep) {
grpc_tcp *tcp = (grpc_tcp *)ep;
- grpc_em_fd_shutdown(&tcp->em_fd);
+ grpc_em_fd_shutdown(tcp->em_fd);
}
static void grpc_tcp_unref(grpc_tcp *tcp) {
int refcount_zero = gpr_unref(&tcp->refcount);
if (refcount_zero) {
- grpc_em_fd_destroy(&tcp->em_fd);
- close(tcp->fd);
+ grpc_em_fd_destroy(tcp->em_fd);
+ gpr_free(tcp->em_fd);
gpr_free(tcp);
}
}
@@ -385,7 +385,7 @@ static void grpc_tcp_handle_read(void *arg /* grpc_tcp */,
} else {
/* Spurious read event, consume it here */
slice_state_destroy(&read_state);
- grpc_em_fd_notify_on_read(&tcp->em_fd, grpc_tcp_handle_read, tcp,
+ grpc_em_fd_notify_on_read(tcp->em_fd, grpc_tcp_handle_read, tcp,
tcp->read_deadline);
}
} else {
@@ -422,7 +422,7 @@ static void grpc_tcp_notify_on_read(grpc_endpoint *ep, grpc_endpoint_read_cb cb,
tcp->read_user_data = user_data;
tcp->read_deadline = deadline;
gpr_ref(&tcp->refcount);
- grpc_em_fd_notify_on_read(&tcp->em_fd, grpc_tcp_handle_read, tcp, deadline);
+ grpc_em_fd_notify_on_read(tcp->em_fd, grpc_tcp_handle_read, tcp, deadline);
}
#define MAX_WRITE_IOVEC 16
@@ -494,7 +494,7 @@ static void grpc_tcp_handle_write(void *arg /* grpc_tcp */,
write_status = grpc_tcp_flush(tcp);
if (write_status == GRPC_ENDPOINT_WRITE_PENDING) {
- grpc_em_fd_notify_on_write(&tcp->em_fd, grpc_tcp_handle_write, tcp,
+ grpc_em_fd_notify_on_write(tcp->em_fd, grpc_tcp_handle_write, tcp,
tcp->write_deadline);
} else {
slice_state_destroy(&tcp->write_state);
@@ -539,7 +539,7 @@ static grpc_endpoint_write_status grpc_tcp_write(
tcp->write_cb = cb;
tcp->write_user_data = user_data;
tcp->write_deadline = deadline;
- grpc_em_fd_notify_on_write(&tcp->em_fd, grpc_tcp_handle_write, tcp,
+ grpc_em_fd_notify_on_write(tcp->em_fd, grpc_tcp_handle_write, tcp,
tcp->write_deadline);
}
@@ -550,11 +550,12 @@ static const grpc_endpoint_vtable vtable = {grpc_tcp_notify_on_read,
grpc_tcp_write, grpc_tcp_shutdown,
grpc_tcp_destroy};
-grpc_endpoint *grpc_tcp_create_dbg(int fd, grpc_em *em, size_t slice_size) {
+static grpc_endpoint *grpc_tcp_create_generic(grpc_em_fd *em_fd,
+ size_t slice_size) {
grpc_tcp *tcp = (grpc_tcp *)gpr_malloc(sizeof(grpc_tcp));
tcp->base.vtable = &vtable;
- tcp->fd = fd;
- tcp->em = em;
+ tcp->fd = grpc_em_fd_get(em_fd);
+ tcp->em = grpc_em_fd_get_em(em_fd);
tcp->read_cb = NULL;
tcp->write_cb = NULL;
tcp->read_user_data = NULL;
@@ -565,6 +566,16 @@ grpc_endpoint *grpc_tcp_create_dbg(int fd, grpc_em *em, size_t slice_size) {
slice_state_init(&tcp->write_state, NULL, 0, 0);
/* paired with unref in grpc_tcp_destroy */
gpr_ref_init(&tcp->refcount, 1);
- grpc_em_fd_init(&tcp->em_fd, tcp->em, fd);
+ tcp->em_fd = em_fd;
return &tcp->base;
}
+
+grpc_endpoint *grpc_tcp_create_dbg(int fd, grpc_em *em, size_t slice_size) {
+ grpc_em_fd *em_fd = gpr_malloc(sizeof(grpc_em_fd));
+ grpc_em_fd_init(em_fd, em, fd);
+ return grpc_tcp_create_generic(em_fd, slice_size);
+}
+
+grpc_endpoint *grpc_tcp_create_emfd(grpc_em_fd *em_fd) {
+ return grpc_tcp_create_generic(em_fd, DEFAULT_SLICE_SIZE);
+}
diff --git a/src/core/endpoint/tcp.h b/src/core/endpoint/tcp.h
index 6507b2f6ef..f6a2a19ec4 100644
--- a/src/core/endpoint/tcp.h
+++ b/src/core/endpoint/tcp.h
@@ -52,4 +52,8 @@ grpc_endpoint *grpc_tcp_create(int fd, grpc_em *em);
/* Special version for debugging slice changes */
grpc_endpoint *grpc_tcp_create_dbg(int fd, grpc_em *em, size_t slice_size);
+/* Special version for handing off ownership of an existing already created
+ eventmanager fd. Must not have any outstanding callbacks. */
+grpc_endpoint *grpc_tcp_create_emfd(grpc_em_fd *em_fd);
+
#endif /* __GRPC_INTERNAL_ENDPOINT_TCP_H__ */
diff --git a/src/core/endpoint/tcp_client.c b/src/core/endpoint/tcp_client.c
index 01a0c3f23d..c6f470ba88 100644
--- a/src/core/endpoint/tcp_client.c
+++ b/src/core/endpoint/tcp_client.c
@@ -34,6 +34,7 @@
#include "src/core/endpoint/tcp_client.h"
#include <errno.h>
+#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
@@ -45,14 +46,12 @@
typedef struct {
void (*cb)(void *arg, grpc_endpoint *tcp);
void *cb_arg;
- grpc_em_fd fd;
+ grpc_em_fd *fd;
gpr_timespec deadline;
} async_connect;
-static int create_fd(int address_family) {
- int fd = socket(address_family, SOCK_STREAM, 0);
+static int prepare_socket(int fd) {
if (fd < 0) {
- gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
goto error;
}
@@ -63,13 +62,13 @@ static int create_fd(int address_family) {
goto error;
}
- return fd;
+ return 1;
error:
if (fd >= 0) {
close(fd);
}
- return -1;
+ return 0;
}
static void on_writable(void *acp, grpc_em_cb_status status) {
@@ -77,8 +76,7 @@ static void on_writable(void *acp, grpc_em_cb_status status) {
int so_error = 0;
socklen_t so_error_size;
int err;
- int fd = grpc_em_fd_get(&ac->fd);
- grpc_em *em = grpc_em_fd_get_em(&ac->fd);
+ int fd = grpc_em_fd_get(ac->fd);
if (status == GRPC_CALLBACK_SUCCESS) {
do {
@@ -105,7 +103,7 @@ static void on_writable(void *acp, grpc_em_cb_status status) {
opened too many network connections. The "easy" fix:
don't do that! */
gpr_log(GPR_ERROR, "kernel out of buffers");
- grpc_em_fd_notify_on_write(&ac->fd, on_writable, ac, ac->deadline);
+ grpc_em_fd_notify_on_write(ac->fd, on_writable, ac, ac->deadline);
return;
} else {
goto error;
@@ -122,31 +120,50 @@ static void on_writable(void *acp, grpc_em_cb_status status) {
error:
ac->cb(ac->cb_arg, NULL);
- grpc_em_fd_destroy(&ac->fd);
+ grpc_em_fd_destroy(ac->fd);
+ gpr_free(ac->fd);
gpr_free(ac);
- close(fd);
return;
great_success:
- grpc_em_fd_destroy(&ac->fd);
- ac->cb(ac->cb_arg, grpc_tcp_create(fd, em));
+ ac->cb(ac->cb_arg, grpc_tcp_create_emfd(ac->fd));
gpr_free(ac);
}
void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep),
- void *arg, grpc_em *em, struct sockaddr *addr,
- int len, gpr_timespec deadline) {
- int fd = create_fd(addr->sa_family);
+ void *arg, grpc_em *em,
+ const struct sockaddr *addr, int addr_len,
+ gpr_timespec deadline) {
+ int fd;
+ grpc_dualstack_mode dsmode;
int err;
async_connect *ac;
+ struct sockaddr_in6 addr6_v4mapped;
+ struct sockaddr_in addr4_copy;
+
+ /* Use dualstack sockets where available. */
+ if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
+ addr = (const struct sockaddr *)&addr6_v4mapped;
+ addr_len = sizeof(addr6_v4mapped);
+ }
+ fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode);
if (fd < 0) {
+ gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
+ }
+ if (dsmode == GRPC_DSMODE_IPV4) {
+ /* If we got an AF_INET socket, map the address back to IPv4. */
+ GPR_ASSERT(grpc_sockaddr_is_v4mapped(addr, &addr4_copy));
+ addr = (struct sockaddr *)&addr4_copy;
+ addr_len = sizeof(addr4_copy);
+ }
+ if (!prepare_socket(fd)) {
cb(arg, NULL);
return;
}
do {
- err = connect(fd, addr, len);
+ err = connect(fd, addr, addr_len);
} while (err < 0 && errno == EINTR);
if (err >= 0) {
@@ -165,6 +182,7 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep),
ac->cb = cb;
ac->cb_arg = arg;
ac->deadline = deadline;
- grpc_em_fd_init(&ac->fd, em, fd);
- grpc_em_fd_notify_on_write(&ac->fd, on_writable, ac, deadline);
+ ac->fd = gpr_malloc(sizeof(grpc_em_fd));
+ grpc_em_fd_init(ac->fd, em, fd);
+ grpc_em_fd_notify_on_write(ac->fd, on_writable, ac, deadline);
}
diff --git a/src/core/endpoint/tcp_client.h b/src/core/endpoint/tcp_client.h
index 2a8b8ee217..69b1b62f37 100644
--- a/src/core/endpoint/tcp_client.h
+++ b/src/core/endpoint/tcp_client.h
@@ -44,7 +44,8 @@
cb with arg and the completed connection when done (or call cb with arg and
NULL on failure) */
void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *tcp),
- void *arg, grpc_em *em, struct sockaddr *addr,
- int len, gpr_timespec deadline);
+ void *arg, grpc_em *em,
+ const struct sockaddr *addr, int addr_len,
+ gpr_timespec deadline);
#endif /* __GRPC_INTERNAL_ENDPOINT_TCP_CLIENT_H__ */
diff --git a/src/core/endpoint/tcp_server.c b/src/core/endpoint/tcp_server.c
index 2f386ce045..efd3dede50 100644
--- a/src/core/endpoint/tcp_server.c
+++ b/src/core/endpoint/tcp_server.c
@@ -114,7 +114,6 @@ void grpc_tcp_server_destroy(grpc_tcp_server *s) {
server_port *sp = &s->ports[i];
grpc_em_fd_destroy(sp->emfd);
gpr_free(sp->emfd);
- close(sp->fd);
}
gpr_free(s->ports);
gpr_free(s);
@@ -153,11 +152,9 @@ static int get_max_accept_queue_size() {
return s_max_accept_queue_size;
}
-/* create a socket to listen with */
-static int create_listening_socket(struct sockaddr *port, int len) {
- int fd = socket(port->sa_family, SOCK_STREAM, 0);
+/* Prepare a recently-created socket for listening. */
+static int prepare_socket(int fd, const struct sockaddr *addr, int addr_len) {
if (fd < 0) {
- gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
goto error;
}
@@ -169,8 +166,11 @@ static int create_listening_socket(struct sockaddr *port, int len) {
goto error;
}
- if (bind(fd, port, len) < 0) {
- gpr_log(GPR_ERROR, "bind: %s", strerror(errno));
+ if (bind(fd, addr, addr_len) < 0) {
+ char *addr_str;
+ grpc_sockaddr_to_string(&addr_str, addr, 0);
+ gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, strerror(errno));
+ gpr_free(addr_str);
goto error;
}
@@ -179,13 +179,13 @@ static int create_listening_socket(struct sockaddr *port, int len) {
goto error;
}
- return fd;
+ return 1;
error:
if (fd >= 0) {
close(fd);
}
- return -1;
+ return 0;
}
/* event manager callback when reads are ready */
@@ -200,6 +200,8 @@ static void on_read(void *arg, grpc_em_cb_status status) {
for (;;) {
struct sockaddr_storage addr;
socklen_t addrlen = sizeof(addr);
+ /* Note: If we ever decide to return this address to the user, remember to
+ strip off the ::ffff:0.0.0.0/96 prefix first. */
int fd = grpc_accept4(sp->fd, (struct sockaddr *)&addr, &addrlen, 1, 1);
if (fd < 0) {
switch (errno) {
@@ -231,13 +233,12 @@ error:
gpr_mu_unlock(&sp->server->mu);
}
-int grpc_tcp_server_add_port(grpc_tcp_server *s, struct sockaddr *port,
- int len) {
+static int add_socket_to_server(grpc_tcp_server *s, int fd,
+ const struct sockaddr *addr, int addr_len) {
server_port *sp;
- /* create a socket */
- int fd = create_listening_socket(port, len);
- if (fd < 0) {
- return -1;
+
+ if (!prepare_socket(fd, addr, addr_len)) {
+ return 0;
}
gpr_mu_lock(&s->mu);
@@ -257,11 +258,62 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, struct sockaddr *port,
gpr_free(sp->emfd);
s->nports--;
gpr_mu_unlock(&s->mu);
- return -1;
+ return 0;
}
gpr_mu_unlock(&s->mu);
- return fd;
+ return 1;
+}
+
+int grpc_tcp_server_add_port(grpc_tcp_server *s, const struct sockaddr *addr,
+ int addr_len) {
+ int ok = 0;
+ int fd;
+ grpc_dualstack_mode dsmode;
+ struct sockaddr_in6 addr6_v4mapped;
+ struct sockaddr_in wild4;
+ struct sockaddr_in6 wild6;
+ struct sockaddr_in addr4_copy;
+ int port;
+
+ if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
+ addr = (const struct sockaddr *)&addr6_v4mapped;
+ addr_len = sizeof(addr6_v4mapped);
+ }
+
+ /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */
+ if (grpc_sockaddr_is_wildcard(addr, &port)) {
+ grpc_sockaddr_make_wildcards(port, &wild4, &wild6);
+
+ /* Try listening on IPv6 first. */
+ addr = (struct sockaddr *)&wild6;
+ addr_len = sizeof(wild6);
+ fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode);
+ ok |= add_socket_to_server(s, fd, addr, addr_len);
+ if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) {
+ return ok;
+ }
+
+ /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */
+ addr = (struct sockaddr *)&wild4;
+ addr_len = sizeof(wild4);
+ }
+
+ fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode);
+ if (fd < 0) {
+ gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
+ }
+ if (dsmode == GRPC_DSMODE_IPV4 &&
+ grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) {
+ addr = (struct sockaddr *)&addr4_copy;
+ addr_len = sizeof(addr4_copy);
+ }
+ ok |= add_socket_to_server(s, fd, addr, addr_len);
+ return ok;
+}
+
+int grpc_tcp_server_get_fd(grpc_tcp_server *s, int index) {
+ return (0 <= index && index < s->nports) ? s->ports[index].fd : -1;
}
void grpc_tcp_server_start(grpc_tcp_server *s, grpc_tcp_server_cb cb,
diff --git a/src/core/endpoint/tcp_server.h b/src/core/endpoint/tcp_server.h
index 99cb83e181..d81cdd000e 100644
--- a/src/core/endpoint/tcp_server.h
+++ b/src/core/endpoint/tcp_server.h
@@ -53,11 +53,23 @@ grpc_tcp_server *grpc_tcp_server_create(grpc_em *em);
void grpc_tcp_server_start(grpc_tcp_server *server, grpc_tcp_server_cb cb,
void *cb_arg);
-/* Add a port to the server, returns a file descriptor on success, or <0 on
- failure; the file descriptor remains owned by the server and will be cleaned
- up when grpc_tcp_server_destroy is called */
-int grpc_tcp_server_add_port(grpc_tcp_server *server, struct sockaddr *port,
- int len);
+/* Add a port to the server, returning true on success, or false otherwise.
+
+ The :: and 0.0.0.0 wildcard addresses are treated identically, accepting
+ both IPv4 and IPv6 connections, but :: is the preferred style. This usually
+ creates one socket, but possibly two on systems which support IPv6,
+ but not dualstack sockets.
+
+ For raw access to the underlying sockets, see grpc_tcp_server_get_fd(). */
+int grpc_tcp_server_add_port(grpc_tcp_server *s, const struct sockaddr *addr,
+ int addr_len);
+
+/* Returns the file descriptor of the Nth listening socket on this server,
+ or -1 if the index is out of bounds.
+
+ The file descriptor remains owned by the server, and will be cleaned
+ up when grpc_tcp_server_destroy is called. */
+int grpc_tcp_server_get_fd(grpc_tcp_server *s, int index);
void grpc_tcp_server_destroy(grpc_tcp_server *server);
diff --git a/src/core/eventmanager/em.c b/src/core/eventmanager/em.c
index f16473b395..36f3720e0d 100644
--- a/src/core/eventmanager/em.c
+++ b/src/core/eventmanager/em.c
@@ -537,6 +537,8 @@ void grpc_em_fd_destroy(grpc_em_fd *em_fd) {
em->num_fds--;
gpr_cv_broadcast(&em->cv);
gpr_mu_unlock(&em->mu);
+
+ close(em_fd->fd);
}
int grpc_em_fd_get(struct grpc_em_fd *em_fd) { return em_fd->fd; }
diff --git a/src/core/eventmanager/em.h b/src/core/eventmanager/em.h
index 6f8bdedce2..aa439d1112 100644
--- a/src/core/eventmanager/em.h
+++ b/src/core/eventmanager/em.h
@@ -146,10 +146,12 @@ grpc_em_error grpc_em_alarm_cancel(grpc_em_alarm *alarm);
initialized *em_fd.
fd is a non-blocking file descriptor.
+ This takes ownership of closing fd.
+
Requires: *em_fd uninitialized. fd is a non-blocking file descriptor. */
grpc_em_error grpc_em_fd_init(grpc_em_fd *em_fd, grpc_em *em, int fd);
-/* Cause *em_fd no longer to be initialized.
+/* Cause *em_fd no longer to be initialized and closes the underlying fd.
Requires: *em_fd initialized; no outstanding notify_on_read or
notify_on_write. */
void grpc_em_fd_destroy(grpc_em_fd *em_fd);
diff --git a/src/core/security/auth.c b/src/core/security/auth.c
index 6480dd2239..9ce0c69d96 100644
--- a/src/core/security/auth.c
+++ b/src/core/security/auth.c
@@ -75,7 +75,7 @@ static void call_op(grpc_call_element *elem, grpc_call_op *op) {
switch (op->type) {
case GRPC_SEND_START: {
grpc_credentials *channel_creds =
- channeld->security_context->request_metadata_only_creds;
+ channeld->security_context->request_metadata_creds;
/* TODO(jboeuf):
Decide on the policy in this case:
- populate both channel and call?
diff --git a/src/core/security/credentials.c b/src/core/security/credentials.c
index 969a97369c..7ff48f9123 100644
--- a/src/core/security/credentials.c
+++ b/src/core/security/credentials.c
@@ -47,12 +47,10 @@
#include <stdio.h>
/* -- Constants. -- */
-
#define GRPC_COMPUTE_ENGINE_TOKEN_REFRESH_THRESHOLD_SECS 60
#define GRPC_COMPUTE_ENGINE_METADATA_HOST "metadata"
#define GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH \
"computeMetadata/v1/instance/service-accounts/default/token"
-#define GRPC_AUTHORIZATION_METADATA_KEY "Authorization"
/* -- Common. -- */
@@ -108,7 +106,13 @@ int grpc_credentials_has_request_metadata_only(grpc_credentials *creds) {
void grpc_credentials_get_request_metadata(grpc_credentials *creds,
grpc_credentials_metadata_cb cb,
void *user_data) {
- if (creds == NULL || !grpc_credentials_has_request_metadata(creds)) return;
+ if (creds == NULL || !grpc_credentials_has_request_metadata(creds) ||
+ creds->vtable->get_request_metadata == NULL) {
+ if (cb != NULL) {
+ cb(user_data, NULL, 0, GRPC_CREDENTIALS_OK);
+ }
+ return;
+ }
creds->vtable->get_request_metadata(creds, cb, user_data);
}
@@ -521,14 +525,235 @@ grpc_fake_transport_security_server_credentials_create() {
return c;
}
+/* -- Composite credentials. -- */
+
+typedef struct {
+ grpc_credentials base;
+ grpc_credentials_array inner;
+} grpc_composite_credentials;
+
+typedef struct {
+ grpc_composite_credentials *composite_creds;
+ size_t creds_index;
+ grpc_mdelem **md_elems;
+ size_t num_md;
+ void *user_data;
+ grpc_credentials_metadata_cb cb;
+} grpc_composite_credentials_metadata_context;
+
+static void composite_destroy(grpc_credentials *creds) {
+ grpc_composite_credentials *c = (grpc_composite_credentials *)creds;
+ size_t i;
+ for (i = 0; i < c->inner.num_creds; i++) {
+ grpc_credentials_unref(c->inner.creds_array[i]);
+ }
+ gpr_free(c->inner.creds_array);
+ gpr_free(creds);
+}
+
+static int composite_has_request_metadata(const grpc_credentials *creds) {
+ const grpc_composite_credentials *c =
+ (const grpc_composite_credentials *)creds;
+ size_t i;
+ for (i = 0; i < c->inner.num_creds; i++) {
+ if (grpc_credentials_has_request_metadata(c->inner.creds_array[i])) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int composite_has_request_metadata_only(const grpc_credentials *creds) {
+ const grpc_composite_credentials *c =
+ (const grpc_composite_credentials *)creds;
+ size_t i;
+ for (i = 0; i < c->inner.num_creds; i++) {
+ if (!grpc_credentials_has_request_metadata_only(c->inner.creds_array[i])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void composite_md_context_destroy(
+ grpc_composite_credentials_metadata_context *ctx) {
+ size_t i;
+ for (i = 0; i < ctx->num_md; i++) {
+ grpc_mdelem_unref(ctx->md_elems[i]);
+ }
+ gpr_free(ctx->md_elems);
+ gpr_free(ctx);
+}
+
+static void composite_metadata_cb(void *user_data, grpc_mdelem **md_elems,
+ size_t num_md,
+ grpc_credentials_status status) {
+ grpc_composite_credentials_metadata_context *ctx =
+ (grpc_composite_credentials_metadata_context *)user_data;
+ size_t i;
+ if (status != GRPC_CREDENTIALS_OK) {
+ ctx->cb(ctx->user_data, NULL, 0, status);
+ return;
+ }
+
+ /* Copy the metadata in the context. */
+ if (num_md > 0) {
+ ctx->md_elems = gpr_realloc(ctx->md_elems,
+ (ctx->num_md + num_md) * sizeof(grpc_mdelem *));
+ for (i = 0; i < num_md; i++) {
+ ctx->md_elems[i + ctx->num_md] = grpc_mdelem_ref(md_elems[i]);
+ }
+ ctx->num_md += num_md;
+ }
+
+ /* See if we need to get some more metadata. */
+ while (ctx->creds_index < ctx->composite_creds->inner.num_creds) {
+ grpc_credentials *inner_creds =
+ ctx->composite_creds->inner.creds_array[ctx->creds_index++];
+ if (grpc_credentials_has_request_metadata(inner_creds)) {
+ grpc_credentials_get_request_metadata(inner_creds, composite_metadata_cb,
+ ctx);
+ return;
+ }
+ }
+
+ /* We're done!. */
+ ctx->cb(ctx->user_data, ctx->md_elems, ctx->num_md, GRPC_CREDENTIALS_OK);
+ composite_md_context_destroy(ctx);
+}
+
+static void composite_get_request_metadata(grpc_credentials *creds,
+ grpc_credentials_metadata_cb cb,
+ void *user_data) {
+ grpc_composite_credentials *c = (grpc_composite_credentials *)creds;
+ grpc_composite_credentials_metadata_context *ctx;
+ if (!grpc_credentials_has_request_metadata(creds)) {
+ cb(user_data, NULL, 0, GRPC_CREDENTIALS_OK);
+ return;
+ }
+ ctx = gpr_malloc(sizeof(grpc_composite_credentials_metadata_context));
+ memset(ctx, 0, sizeof(grpc_composite_credentials_metadata_context));
+ ctx->user_data = user_data;
+ ctx->cb = cb;
+ ctx->composite_creds = c;
+ while (ctx->creds_index < c->inner.num_creds) {
+ grpc_credentials *inner_creds = c->inner.creds_array[ctx->creds_index++];
+ if (grpc_credentials_has_request_metadata(inner_creds)) {
+ grpc_credentials_get_request_metadata(inner_creds, composite_metadata_cb,
+ ctx);
+ return;
+ }
+ }
+ GPR_ASSERT(0); /* Should have exited before. */
+}
-/* -- Composite credentials TODO(jboeuf). -- */
+static grpc_credentials_vtable composite_credentials_vtable = {
+ composite_destroy, composite_has_request_metadata,
+ composite_has_request_metadata_only, composite_get_request_metadata};
+
+static grpc_credentials_array get_creds_array(grpc_credentials **creds_addr) {
+ grpc_credentials_array result;
+ grpc_credentials *creds = *creds_addr;
+ result.creds_array = creds_addr;
+ result.num_creds = 1;
+ if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE)) {
+ result = *grpc_composite_credentials_get_credentials(creds);
+ }
+ return result;
+}
grpc_credentials *grpc_composite_credentials_create(grpc_credentials *creds1,
grpc_credentials *creds2) {
- return NULL;
+ size_t i;
+ grpc_credentials_array creds1_array;
+ grpc_credentials_array creds2_array;
+ grpc_composite_credentials *c;
+ GPR_ASSERT(creds1 != NULL);
+ GPR_ASSERT(creds2 != NULL);
+ c = gpr_malloc(sizeof(grpc_composite_credentials));
+ memset(c, 0, sizeof(grpc_composite_credentials));
+ c->base.type = GRPC_CREDENTIALS_TYPE_COMPOSITE;
+ c->base.vtable = &composite_credentials_vtable;
+ gpr_ref_init(&c->base.refcount, 1);
+ creds1_array = get_creds_array(&creds1);
+ creds2_array = get_creds_array(&creds2);
+ c->inner.num_creds = creds1_array.num_creds + creds2_array.num_creds;
+ c->inner.creds_array =
+ gpr_malloc(c->inner.num_creds * sizeof(grpc_credentials *));
+ for (i = 0; i < creds1_array.num_creds; i++) {
+ c->inner.creds_array[i] = grpc_credentials_ref(creds1_array.creds_array[i]);
+ }
+ for (i = 0; i < creds2_array.num_creds; i++) {
+ c->inner.creds_array[i + creds1_array.num_creds] =
+ grpc_credentials_ref(creds2_array.creds_array[i]);
+ }
+ return &c->base;
+}
+
+const grpc_credentials_array *grpc_composite_credentials_get_credentials(
+ grpc_credentials *creds) {
+ const grpc_composite_credentials *c =
+ (const grpc_composite_credentials *)creds;
+ GPR_ASSERT(!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE));
+ return &c->inner;
+}
+
+/* -- IAM credentials. -- */
+
+typedef struct {
+ grpc_credentials base;
+ grpc_mdctx *md_ctx;
+ grpc_mdelem *token_md;
+ grpc_mdelem *authority_selector_md;
+} grpc_iam_credentials;
+
+static void iam_destroy(grpc_credentials *creds) {
+ grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
+ grpc_mdelem_unref(c->token_md);
+ grpc_mdelem_unref(c->authority_selector_md);
+ grpc_mdctx_orphan(c->md_ctx);
+ gpr_free(c);
+}
+
+static int iam_has_request_metadata(const grpc_credentials *creds) { return 1; }
+
+static int iam_has_request_metadata_only(const grpc_credentials *creds) {
+ return 1;
+}
+
+static void iam_get_request_metadata(grpc_credentials *creds,
+ grpc_credentials_metadata_cb cb,
+ void *user_data) {
+ grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
+ grpc_mdelem *md_array[2];
+ md_array[0] = c->token_md;
+ md_array[1] = c->authority_selector_md;
+ cb(user_data, md_array, 2, GRPC_CREDENTIALS_OK);
+}
+
+static grpc_credentials_vtable iam_vtable = {
+ iam_destroy, iam_has_request_metadata, iam_has_request_metadata_only,
+ iam_get_request_metadata};
+
+grpc_credentials *grpc_iam_credentials_create(const char *token,
+ const char *authority_selector) {
+ grpc_iam_credentials *c;
+ GPR_ASSERT(token != NULL);
+ GPR_ASSERT(authority_selector != NULL);
+ c = gpr_malloc(sizeof(grpc_iam_credentials));
+ memset(c, 0, sizeof(grpc_iam_credentials));
+ c->base.type = GRPC_CREDENTIALS_TYPE_IAM;
+ c->base.vtable = &iam_vtable;
+ gpr_ref_init(&c->base.refcount, 1);
+ c->md_ctx = grpc_mdctx_create();
+ c->token_md = grpc_mdelem_from_strings(
+ c->md_ctx, GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, token);
+ c->authority_selector_md = grpc_mdelem_from_strings(
+ c->md_ctx, GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, authority_selector);
+ return &c->base;
}
/* -- Default credentials TODO(jboeuf). -- */
grpc_credentials *grpc_default_credentials_create(void) { return NULL; }
+
diff --git a/src/core/security/credentials.h b/src/core/security/credentials.h
index 1432611ec6..9fb82e1ecd 100644
--- a/src/core/security/credentials.h
+++ b/src/core/security/credentials.h
@@ -50,9 +50,15 @@ typedef enum {
#define GRPC_CREDENTIALS_TYPE_SSL "Ssl"
#define GRPC_CREDENTIALS_TYPE_OAUTH2 "Oauth2"
+#define GRPC_CREDENTIALS_TYPE_IAM "Iam"
#define GRPC_CREDENTIALS_TYPE_COMPOSITE "Composite"
#define GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY "FakeTransportSecurity"
+#define GRPC_AUTHORIZATION_METADATA_KEY "Authorization"
+#define GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY \
+ "x-goog-iam-authorization-token"
+#define GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY "x-goog-iam-authority-selector"
+
/* --- grpc_credentials. --- */
typedef void (*grpc_credentials_metadata_cb)(void *user_data,
@@ -94,6 +100,14 @@ typedef struct {
const grpc_ssl_config *grpc_ssl_credentials_get_config(
const grpc_credentials *ssl_creds);
+typedef struct {
+ grpc_credentials **creds_array;
+ size_t num_creds;
+} grpc_credentials_array;
+
+const grpc_credentials_array *grpc_composite_credentials_get_credentials(
+ grpc_credentials *composite_creds);
+
/* Exposed for testing only. */
grpc_credentials_status grpc_compute_engine_credentials_parse_server_response(
const struct grpc_httpcli_response *response, grpc_mdctx *ctx,
diff --git a/src/core/security/security_context.c b/src/core/security/security_context.c
index beda64cba2..c56692ae83 100644
--- a/src/core/security/security_context.c
+++ b/src/core/security/security_context.c
@@ -37,6 +37,8 @@
#include "src/core/endpoint/secure_endpoint.h"
#include "src/core/security/credentials.h"
+#include "src/core/surface/lame_client.h"
+#include "src/core/transport/chttp2/alpn.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/slice_buffer.h>
@@ -47,7 +49,6 @@
/* -- Constants. -- */
-#define GRPC_ALPN_PROTOCOL_STRING "h2-15"
/* Defines the cipher suites that we accept. All these cipher suites are
compliant with TLS 1.2 and use an RSA public key. We prefer GCM over CBC
and ECDHE-RSA over just RSA. */
@@ -122,11 +123,11 @@ grpc_security_context *grpc_find_security_context_in_args(
return NULL;
}
-static int check_request_metadata_only_creds(grpc_credentials *creds) {
- if (creds != NULL && !grpc_credentials_has_request_metadata_only(creds)) {
+static int check_request_metadata_creds(grpc_credentials *creds) {
+ if (creds != NULL && !grpc_credentials_has_request_metadata(creds)) {
gpr_log(GPR_ERROR,
"Incompatible credentials for channel security context: needs to "
- "only set request metadata.");
+ "set request metadata.");
return 0;
}
return 1;
@@ -136,7 +137,7 @@ static int check_request_metadata_only_creds(grpc_credentials *creds) {
static void fake_channel_destroy(grpc_security_context *ctx) {
grpc_channel_security_context *c = (grpc_channel_security_context *)ctx;
- grpc_credentials_unref(c->request_metadata_only_creds);
+ grpc_credentials_unref(c->request_metadata_creds);
gpr_free(ctx);
}
@@ -191,15 +192,14 @@ static grpc_security_context_vtable fake_server_vtable = {
fake_server_destroy, fake_server_create_handshaker, fake_check_peer};
grpc_channel_security_context *grpc_fake_channel_security_context_create(
- grpc_credentials *request_metadata_only_creds) {
+ grpc_credentials *request_metadata_creds) {
grpc_channel_security_context *c =
gpr_malloc(sizeof(grpc_channel_security_context));
gpr_ref_init(&c->base.refcount, 1);
c->base.is_client_side = 1;
c->base.vtable = &fake_channel_vtable;
- GPR_ASSERT(check_request_metadata_only_creds(request_metadata_only_creds));
- c->request_metadata_only_creds =
- grpc_credentials_ref(request_metadata_only_creds);
+ GPR_ASSERT(check_request_metadata_creds(request_metadata_creds));
+ c->request_metadata_creds = grpc_credentials_ref(request_metadata_creds);
return c;
}
@@ -226,7 +226,7 @@ typedef struct {
static void ssl_channel_destroy(grpc_security_context *ctx) {
grpc_ssl_channel_security_context *c =
(grpc_ssl_channel_security_context *)ctx;
- grpc_credentials_unref(c->base.request_metadata_only_creds);
+ grpc_credentials_unref(c->base.request_metadata_creds);
if (c->handshaker_factory != NULL) {
tsi_ssl_handshaker_factory_destroy(c->handshaker_factory);
}
@@ -282,8 +282,8 @@ static grpc_security_status ssl_check_peer(const char *secure_peer_name,
gpr_log(GPR_ERROR, "Invalid or missing selected ALPN property.");
return GRPC_SECURITY_ERROR;
}
- if (strncmp(GRPC_ALPN_PROTOCOL_STRING, p->value.string.data,
- p->value.string.length)) {
+ if (!grpc_chttp2_is_alpn_version_supported(p->value.string.data,
+ p->value.string.length)) {
gpr_log(GPR_ERROR, "Invalid ALPN value.");
return GRPC_SECURITY_ERROR;
}
@@ -320,10 +320,9 @@ static grpc_security_context_vtable ssl_server_vtable = {
ssl_server_destroy, ssl_server_create_handshaker, ssl_server_check_peer};
grpc_security_status grpc_ssl_channel_security_context_create(
- grpc_credentials *request_metadata_only_creds,
- const grpc_ssl_config *config, const char *secure_peer_name,
- grpc_channel_security_context **ctx) {
- const char *alpn_protocol_string = GRPC_ALPN_PROTOCOL_STRING;
+ grpc_credentials *request_metadata_creds, const grpc_ssl_config *config,
+ const char *secure_peer_name, grpc_channel_security_context **ctx) {
+ const char *alpn_protocol_string = GRPC_CHTTP2_ALPN_VERSION;
unsigned char alpn_protocol_string_len =
(unsigned char)strlen(alpn_protocol_string);
tsi_result result = TSI_OK;
@@ -334,7 +333,7 @@ grpc_security_status grpc_ssl_channel_security_context_create(
gpr_log(GPR_ERROR, "An ssl channel needs a secure name and root certs.");
return GRPC_SECURITY_ERROR;
}
- if (!check_request_metadata_only_creds(request_metadata_only_creds)) {
+ if (!check_request_metadata_creds(request_metadata_creds)) {
return GRPC_SECURITY_ERROR;
}
@@ -344,8 +343,7 @@ grpc_security_status grpc_ssl_channel_security_context_create(
gpr_ref_init(&c->base.base.refcount, 1);
c->base.base.vtable = &ssl_channel_vtable;
c->base.base.is_client_side = 1;
- c->base.request_metadata_only_creds =
- grpc_credentials_ref(request_metadata_only_creds);
+ c->base.request_metadata_creds = grpc_credentials_ref(request_metadata_creds);
if (secure_peer_name != NULL) {
c->secure_peer_name = gpr_strdup(secure_peer_name);
}
@@ -368,7 +366,7 @@ grpc_security_status grpc_ssl_channel_security_context_create(
grpc_security_status grpc_ssl_server_security_context_create(
const grpc_ssl_config *config, grpc_security_context **ctx) {
- const char *alpn_protocol_string = GRPC_ALPN_PROTOCOL_STRING;
+ const char *alpn_protocol_string = GRPC_CHTTP2_ALPN_VERSION;
unsigned char alpn_protocol_string_len =
(unsigned char)strlen(alpn_protocol_string);
tsi_result result = TSI_OK;
@@ -427,7 +425,7 @@ static grpc_channel *grpc_ssl_channel_create(grpc_credentials *creds,
status = grpc_ssl_channel_security_context_create(creds, config,
secure_peer_name, &ctx);
if (status != GRPC_SECURITY_OK) {
- return NULL; /* TODO(ctiller): return lame channel. */
+ return grpc_lame_client_channel_create();
}
channel = grpc_secure_channel_create_internal(target, args, ctx);
grpc_security_context_unref(&ctx->base);
@@ -435,13 +433,38 @@ static grpc_channel *grpc_ssl_channel_create(grpc_credentials *creds,
}
+static grpc_credentials *get_creds_from_composite(
+ grpc_credentials *composite_creds, const char *type) {
+ size_t i;
+ const grpc_credentials_array *inner_creds_array =
+ grpc_composite_credentials_get_credentials(composite_creds);
+ for (i = 0; i < inner_creds_array->num_creds; i++) {
+ if (!strcmp(type, inner_creds_array->creds_array[i]->type)) {
+ return inner_creds_array->creds_array[i];
+ }
+ }
+ return NULL;
+}
+
+static grpc_channel *grpc_channel_create_from_composite_creds(
+ grpc_credentials *composite_creds, const char *target,
+ const grpc_channel_args *args) {
+ grpc_credentials *creds =
+ get_creds_from_composite(composite_creds, GRPC_CREDENTIALS_TYPE_SSL);
+ if (creds != NULL) {
+ return grpc_ssl_channel_create(
+ composite_creds, grpc_ssl_credentials_get_config(creds), target, args);
+ }
+ return NULL; /* TODO(ctiller): return lame channel. */
+}
+
grpc_channel *grpc_secure_channel_create(grpc_credentials *creds,
const char *target,
const grpc_channel_args *args) {
if (grpc_credentials_has_request_metadata_only(creds)) {
gpr_log(GPR_ERROR,
"Credentials is insufficient to create a secure channel.");
- return NULL; /* TODO(ctiller): return lame channel. */
+ return grpc_lame_client_channel_create();
}
if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) {
return grpc_ssl_channel_create(NULL, grpc_ssl_credentials_get_config(creds),
@@ -455,11 +478,11 @@ grpc_channel *grpc_secure_channel_create(grpc_credentials *creds,
grpc_security_context_unref(&ctx->base);
return channel;
} else if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE)) {
- return NULL; /* TODO(jboeuf) Implement. */
+ return grpc_channel_create_from_composite_creds(creds, target, args);
} else {
gpr_log(GPR_ERROR,
"Unknown credentials type %s for creating a secure channel.");
- return NULL; /* TODO(ctiller): return lame channel. */
+ return grpc_lame_client_channel_create();
}
}
diff --git a/src/core/security/security_context.h b/src/core/security/security_context.h
index 59c9bbdf34..0c6025643a 100644
--- a/src/core/security/security_context.h
+++ b/src/core/security/security_context.h
@@ -119,7 +119,7 @@ typedef struct grpc_channel_security_context grpc_channel_security_context;
struct grpc_channel_security_context {
grpc_security_context base; /* requires is_client_side to be non 0. */
- grpc_credentials *request_metadata_only_creds;
+ grpc_credentials *request_metadata_creds;
};
/* --- Creation security contexts. --- */
@@ -127,14 +127,14 @@ struct grpc_channel_security_context {
/* For TESTING ONLY!
Creates a fake context that emulates real channel security. */
grpc_channel_security_context *grpc_fake_channel_security_context_create(
- grpc_credentials *request_metadata_only_creds);
+ grpc_credentials *request_metadata_creds);
/* For TESTING ONLY!
Creates a fake context that emulates real server security. */
grpc_security_context *grpc_fake_server_security_context_create(void);
/* Creates an SSL channel_security_context.
- - request_metadata_only_creds is the credentials object which metadata
+ - request_metadata_creds is the credentials object which metadata
will be sent with each request. This parameter can be NULL.
- config is the SSL config to be used for the SSL channel establishment.
- is_client should be 0 for a server or a non-0 value for a client.
@@ -147,9 +147,8 @@ grpc_security_context *grpc_fake_server_security_context_create(void);
specific error code otherwise.
*/
grpc_security_status grpc_ssl_channel_security_context_create(
- grpc_credentials *request_metadata_only_creds,
- const grpc_ssl_config *config, const char *secure_peer_name,
- grpc_channel_security_context **ctx);
+ grpc_credentials *request_metadata_creds, const grpc_ssl_config *config,
+ const char *secure_peer_name, grpc_channel_security_context **ctx);
/* Creates an SSL server_security_context.
- config is the SSL config to be used for the SSL channel establishment.
diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c
index bce27ec3ab..335d502217 100644
--- a/src/core/security/server_secure_chttp2.c
+++ b/src/core/security/server_secure_chttp2.c
@@ -109,7 +109,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr) {
for (i = 0; i < resolved->naddrs; i++) {
if (grpc_tcp_server_add_port(tcp,
(struct sockaddr *)&resolved->addrs[i].addr,
- resolved->addrs[i].len) >= 0) {
+ resolved->addrs[i].len)) {
count++;
}
}
diff --git a/src/core/support/cpu_posix.c b/src/core/support/cpu_posix.c
index 82d58de2b4..7f9cb8b4dd 100644
--- a/src/core/support/cpu_posix.c
+++ b/src/core/support/cpu_posix.c
@@ -34,8 +34,6 @@
#include "src/core/support/cpu.h"
#ifdef __linux__
-#include <errno.h>
-#include <unistd.h>
#define _GNU_SOURCE
#define __USE_GNU
#define __USE_MISC
@@ -43,6 +41,9 @@
#undef _GNU_SOURCE
#undef __USE_GNU
#undef __USE_MISC
+
+#include <errno.h>
+#include <unistd.h>
#include <string.h>
#include <grpc/support/log.h>
diff --git a/src/core/support/string_posix.c b/src/core/support/string_posix.c
index d1da37923f..7b7e82e1e4 100644
--- a/src/core/support/string_posix.c
+++ b/src/core/support/string_posix.c
@@ -37,6 +37,10 @@
#define _POSIX_C_SOURCE 200112L
#endif
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_STRING
+
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
@@ -84,3 +88,5 @@ int gpr_asprintf(char **strp, const char *format, ...) {
*strp = NULL;
return -1;
}
+
+#endif /* GPR_POSIX_STRING */
diff --git a/src/core/support/string_win32.c b/src/core/support/string_win32.c
new file mode 100644
index 0000000000..74b10280eb
--- /dev/null
+++ b/src/core/support/string_win32.c
@@ -0,0 +1,81 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/* Posix code for gpr snprintf support. */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+int gpr_asprintf(char **strp, const char *format, ...) {
+ va_list args;
+ int ret;
+ size_t strp_buflen;
+
+ /* Determine the length. */
+ va_start(args, format);
+ ret = vscprintf(format, args);
+ va_end(args);
+ if (!(0 <= ret && ret < ~(size_t)0)) {
+ *strp = NULL;
+ return -1;
+ }
+
+ /* Allocate a new buffer, with space for the NUL terminator. */
+ strp_buflen = (size_t)ret + 1;
+ if ((*strp = gpr_malloc(strp_buflen)) == NULL) {
+ /* This shouldn't happen, because gpr_malloc() calls abort(). */
+ return -1;
+ }
+
+ /* Print to the buffer. */
+ va_start(args, format);
+ ret = vsnprintf_s(*strp, strp_buflen, _TRUNCATE, format, args);
+ va_end(args);
+ if (ret == strp_buflen - 1) {
+ return ret;
+ }
+
+ /* This should never happen. */
+ gpr_free(*strp);
+ *strp = NULL;
+ return -1;
+}
+
+#endif /* GPR_WIN32 */
diff --git a/src/core/support/thd_posix.c b/src/core/support/thd_posix.c
index c86eea415d..1189e0c3f9 100644
--- a/src/core/support/thd_posix.c
+++ b/src/core/support/thd_posix.c
@@ -33,6 +33,10 @@
/* Posix implementation for gpr threads. */
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SYNC
+
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
@@ -76,3 +80,5 @@ gpr_thd_options gpr_thd_options_default(void) {
memset(&options, 0, sizeof(options));
return options;
}
+
+#endif /* GPR_POSIX_SYNC */
diff --git a/src/core/support/thd_win32.c b/src/core/support/thd_win32.c
index 8440479520..1762f87f3c 100644
--- a/src/core/support/thd_win32.c
+++ b/src/core/support/thd_win32.c
@@ -48,7 +48,7 @@ struct thd_arg {
};
/* Body of every thread started via gpr_thd_new. */
-static DWORD thread_body(void *v) {
+static DWORD WINAPI thread_body(void *v) {
struct thd_arg a = *(struct thd_arg *)v;
gpr_free(v);
(*a.body)(a.arg);
@@ -62,7 +62,7 @@ int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg,
a->body = thd_body;
a->arg = arg;
*t = 0;
- handle = CreateThread(NULL, 64 * 1024, &thread_body, a, 0, NULL);
+ handle = CreateThread(NULL, 64 * 1024, thread_body, a, 0, NULL);
if (handle == NULL) {
gpr_free(a);
} else {
diff --git a/src/core/support/time_posix.c b/src/core/support/time_posix.c
index e7b79d10b1..78d4c3b446 100644
--- a/src/core/support/time_posix.c
+++ b/src/core/support/time_posix.c
@@ -37,6 +37,11 @@
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L
#endif
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_TIME
+
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
@@ -79,3 +84,5 @@ void gpr_sleep_until(gpr_timespec until) {
}
}
}
+
+#endif /* GPR_POSIX_TIME */
diff --git a/src/core/support/time_win32.c b/src/core/support/time_win32.c
index 425809144c..d9abe2dde1 100644
--- a/src/core/support/time_win32.c
+++ b/src/core/support/time_win32.c
@@ -38,12 +38,12 @@
#ifdef GPR_WIN32
#include <grpc/support/time.h>
-#include <windows.h>
+#include <sys/timeb.h>
gpr_timespec gpr_now(void) {
gpr_timespec now_tv;
- struct _timeb64 now_tb;
- _ftime64(&now_tb);
+ struct __timeb64 now_tb;
+ _ftime64_s(&now_tb);
now_tv.tv_sec = now_tb.time;
now_tv.tv_nsec = now_tb.millitm * 1000000;
return now_tv;
diff --git a/src/core/surface/call.c b/src/core/surface/call.c
index 02c224dabd..a731c7cab7 100644
--- a/src/core/surface/call.c
+++ b/src/core/surface/call.c
@@ -154,7 +154,12 @@ static int prq_pop_to_cq(pending_read_queue *q, void *tag, grpc_call *call,
/* the state of a call, based upon which functions have been called against
said call */
-typedef enum { CALL_CREATED, CALL_STARTED, CALL_FINISHED } call_state;
+typedef enum {
+ CALL_CREATED,
+ CALL_BOUNDCQ,
+ CALL_STARTED,
+ CALL_FINISHED
+} call_state;
struct grpc_call {
grpc_completion_queue *cq;
@@ -404,24 +409,18 @@ grpc_call_error grpc_call_start_invoke(grpc_call *call,
return GRPC_CALL_OK;
}
-grpc_call_error grpc_call_accept(grpc_call *call, grpc_completion_queue *cq,
- void *finished_tag, gpr_uint32 flags) {
- grpc_call_element *elem;
- grpc_call_op op;
-
+grpc_call_error grpc_call_server_accept(grpc_call *call,
+ grpc_completion_queue *cq,
+ void *finished_tag) {
/* validate preconditions */
if (call->is_client) {
gpr_log(GPR_ERROR, "can only call %s on servers", __FUNCTION__);
return GRPC_CALL_ERROR_NOT_ON_CLIENT;
}
- if (call->state >= CALL_STARTED) {
- gpr_log(GPR_ERROR, "call is already invoked");
- return GRPC_CALL_ERROR_ALREADY_INVOKED;
- }
-
- if (flags & GRPC_WRITE_NO_COMPRESS) {
- return GRPC_CALL_ERROR_INVALID_FLAGS;
+ if (call->state >= CALL_BOUNDCQ) {
+ gpr_log(GPR_ERROR, "call is already accepted");
+ return GRPC_CALL_ERROR_ALREADY_ACCEPTED;
}
/* inform the completion queue of an incoming operation (corresponding to
@@ -430,7 +429,7 @@ grpc_call_error grpc_call_accept(grpc_call *call, grpc_completion_queue *cq,
/* update state */
gpr_mu_lock(&call->read_mu);
- call->state = CALL_STARTED;
+ call->state = CALL_BOUNDCQ;
call->cq = cq;
call->finished_tag = finished_tag;
if (prq_is_empty(&call->prq) && call->received_finish) {
@@ -442,6 +441,32 @@ grpc_call_error grpc_call_accept(grpc_call *call, grpc_completion_queue *cq,
}
gpr_mu_unlock(&call->read_mu);
+ return GRPC_CALL_OK;
+}
+
+grpc_call_error grpc_call_server_end_initial_metadata(grpc_call *call,
+ gpr_uint32 flags) {
+ grpc_call_element *elem;
+ grpc_call_op op;
+
+ /* validate preconditions */
+ if (call->is_client) {
+ gpr_log(GPR_ERROR, "can only call %s on servers", __FUNCTION__);
+ return GRPC_CALL_ERROR_NOT_ON_CLIENT;
+ }
+
+ if (call->state >= CALL_STARTED) {
+ gpr_log(GPR_ERROR, "call is already started");
+ return GRPC_CALL_ERROR_ALREADY_INVOKED;
+ }
+
+ if (flags & GRPC_WRITE_NO_COMPRESS) {
+ return GRPC_CALL_ERROR_INVALID_FLAGS;
+ }
+
+ /* update state */
+ call->state = CALL_STARTED;
+
/* call down */
op.type = GRPC_SEND_START;
op.dir = GRPC_CALL_DOWN;
@@ -455,6 +480,17 @@ grpc_call_error grpc_call_accept(grpc_call *call, grpc_completion_queue *cq,
return GRPC_CALL_OK;
}
+grpc_call_error grpc_call_accept(grpc_call *call, grpc_completion_queue *cq,
+ void *finished_tag, gpr_uint32 flags) {
+ grpc_call_error err;
+
+ err = grpc_call_server_accept(call, cq, finished_tag);
+ if (err != GRPC_CALL_OK) return err;
+ err = grpc_call_server_end_initial_metadata(call, flags);
+ if (err != GRPC_CALL_OK) return err;
+ return GRPC_CALL_OK;
+}
+
static void done_writes_done(void *user_data, grpc_op_error error) {
grpc_call *call = user_data;
void *tag = call->write_tag;
@@ -515,6 +551,7 @@ grpc_call_error grpc_call_start_read(grpc_call *call, void *tag) {
switch (call->state) {
case CALL_CREATED:
return GRPC_CALL_ERROR_NOT_INVOKED;
+ case CALL_BOUNDCQ:
case CALL_STARTED:
break;
case CALL_FINISHED:
@@ -559,6 +596,7 @@ grpc_call_error grpc_call_start_write(grpc_call *call,
switch (call->state) {
case CALL_CREATED:
+ case CALL_BOUNDCQ:
return GRPC_CALL_ERROR_NOT_INVOKED;
case CALL_STARTED:
break;
@@ -607,6 +645,7 @@ grpc_call_error grpc_call_writes_done(grpc_call *call, void *tag) {
switch (call->state) {
case CALL_CREATED:
+ case CALL_BOUNDCQ:
return GRPC_CALL_ERROR_NOT_INVOKED;
case CALL_FINISHED:
return GRPC_CALL_ERROR_ALREADY_FINISHED;
@@ -646,6 +685,7 @@ grpc_call_error grpc_call_start_write_status(grpc_call *call,
switch (call->state) {
case CALL_CREATED:
+ case CALL_BOUNDCQ:
return GRPC_CALL_ERROR_NOT_INVOKED;
case CALL_FINISHED:
return GRPC_CALL_ERROR_ALREADY_FINISHED;
diff --git a/src/core/surface/channel.c b/src/core/surface/channel.c
index ff994257f4..dfb47b3a53 100644
--- a/src/core/surface/channel.c
+++ b/src/core/surface/channel.c
@@ -127,9 +127,16 @@ void grpc_channel_destroy(grpc_channel *channel) {
grpc_channel_op op;
grpc_channel_element *elem;
- op.type = GRPC_CHANNEL_SHUTDOWN;
- op.dir = GRPC_CALL_DOWN;
elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0);
+
+ op.type = GRPC_CHANNEL_GOAWAY;
+ op.dir = GRPC_CALL_DOWN;
+ op.data.goaway.status = GRPC_STATUS_OK;
+ op.data.goaway.message = gpr_slice_from_copied_string("Client disconnect");
+ elem->filter->channel_op(elem, &op);
+
+ op.type = GRPC_CHANNEL_DISCONNECT;
+ op.dir = GRPC_CALL_DOWN;
elem->filter->channel_op(elem, &op);
grpc_channel_internal_unref(channel);
diff --git a/src/core/surface/client.c b/src/core/surface/client.c
index 26abffa817..f78a45f073 100644
--- a/src/core/surface/client.c
+++ b/src/core/surface/client.c
@@ -83,6 +83,9 @@ static void channel_op(grpc_channel_element *elem, grpc_channel_op *op) {
case GRPC_TRANSPORT_CLOSED:
gpr_log(GPR_ERROR, "Transport closed");
break;
+ case GRPC_TRANSPORT_GOAWAY:
+ gpr_slice_unref(op->data.goaway.message);
+ break;
default:
GPR_ASSERT(op->dir == GRPC_CALL_DOWN);
grpc_channel_next_op(elem, op);
diff --git a/src/core/surface/completion_queue.c b/src/core/surface/completion_queue.c
index a7d611579f..2002476777 100644
--- a/src/core/surface/completion_queue.c
+++ b/src/core/surface/completion_queue.c
@@ -105,7 +105,7 @@ static event *add_locked(grpc_completion_queue *cc, grpc_completion_type type,
void *tag, grpc_call *call,
grpc_event_finish_func on_finish, void *user_data) {
event *ev = gpr_malloc(sizeof(event));
- gpr_intptr bucket = ((gpr_intptr)tag) % NUM_TAG_BUCKETS;
+ gpr_uintptr bucket = ((gpr_uintptr)tag) % NUM_TAG_BUCKETS;
GPR_ASSERT(!cc->shutdown);
ev->base.type = type;
ev->base.tag = tag;
@@ -260,9 +260,9 @@ grpc_event *grpc_completion_queue_next(grpc_completion_queue *cc,
gpr_mu_lock(&cc->em->mu);
for (;;) {
if (cc->queue != NULL) {
- gpr_intptr bucket;
+ gpr_uintptr bucket;
ev = cc->queue;
- bucket = ((gpr_intptr)ev->base.tag) % NUM_TAG_BUCKETS;
+ bucket = ((gpr_uintptr)ev->base.tag) % NUM_TAG_BUCKETS;
cc->queue = ev->queue_next;
ev->queue_next->queue_prev = ev->queue_prev;
ev->queue_prev->queue_next = ev->queue_next;
@@ -297,7 +297,7 @@ grpc_event *grpc_completion_queue_next(grpc_completion_queue *cc,
}
static event *pluck_event(grpc_completion_queue *cc, void *tag) {
- gpr_intptr bucket = ((gpr_intptr)tag) % NUM_TAG_BUCKETS;
+ gpr_uintptr bucket = ((gpr_uintptr)tag) % NUM_TAG_BUCKETS;
event *ev = cc->buckets[bucket];
if (ev == NULL) return NULL;
do {
diff --git a/src/core/surface/lame_client.c b/src/core/surface/lame_client.c
index 18921c44dd..3744b5b6db 100644
--- a/src/core/surface/lame_client.c
+++ b/src/core/surface/lame_client.c
@@ -61,7 +61,15 @@ static void call_op(grpc_call_element *elem, grpc_call_op *op) {
op->done_cb(op->user_data, GRPC_OP_ERROR);
}
-static void channel_op(grpc_channel_element *elem, grpc_channel_op *op) {}
+static void channel_op(grpc_channel_element *elem, grpc_channel_op *op) {
+ switch (op->type) {
+ case GRPC_CHANNEL_GOAWAY:
+ gpr_slice_unref(op->data.goaway.message);
+ break;
+ default:
+ break;
+ }
+}
static void init_call_elem(grpc_call_element *elem,
const void *transport_server_data) {}
@@ -87,7 +95,7 @@ static const grpc_channel_filter lame_filter = {
"lame-client",
};
-grpc_channel *grpc_lame_client_channel_create() {
+grpc_channel *grpc_lame_client_channel_create(void) {
static const grpc_channel_filter *filters[] = {&lame_filter};
return grpc_channel_create_from_filters(filters, 1, NULL, grpc_mdctx_create(),
1);
diff --git a/src/core/surface/lame_client.h b/src/core/surface/lame_client.h
index 74b9707202..3cfbf7b954 100644
--- a/src/core/surface/lame_client.h
+++ b/src/core/surface/lame_client.h
@@ -37,6 +37,6 @@
#include <grpc/grpc.h>
/* Create a lame client: this client fails every operation attempted on it. */
-grpc_channel *grpc_lame_client_channel_create();
+grpc_channel *grpc_lame_client_channel_create(void);
#endif /* __GRPC_INTERNAL_SURFACE_LAME_CLIENT_H_ */
diff --git a/src/core/surface/server.c b/src/core/surface/server.c
index 99d66ffb2d..d8d5a7adf1 100644
--- a/src/core/surface/server.c
+++ b/src/core/surface/server.c
@@ -331,6 +331,9 @@ static void channel_op(grpc_channel_element *elem, grpc_channel_op *op) {
gpr_mu_unlock(&chand->server->mu);
server_unref(chand->server);
break;
+ case GRPC_TRANSPORT_GOAWAY:
+ gpr_slice_unref(op->data.goaway.message);
+ break;
default:
GPR_ASSERT(op->dir == GRPC_CALL_DOWN);
grpc_channel_next_op(elem, op);
@@ -341,7 +344,7 @@ static void channel_op(grpc_channel_element *elem, grpc_channel_op *op) {
static void finish_shutdown_channel(void *cd, grpc_em_cb_status status) {
channel_data *chand = cd;
grpc_channel_op op;
- op.type = GRPC_CHANNEL_SHUTDOWN;
+ op.type = GRPC_CHANNEL_DISCONNECT;
op.dir = GRPC_CALL_DOWN;
channel_op(grpc_channel_stack_element(
grpc_channel_get_channel_stack(chand->channel), 0),
@@ -515,10 +518,15 @@ grpc_transport_setup_result grpc_server_setup_transport(
}
void grpc_server_shutdown(grpc_server *server) {
- /* TODO(ctiller): send goaway, etc */
listener *l;
void **tags;
size_t ntags;
+ channel_data **channels;
+ channel_data *c;
+ size_t nchannels;
+ size_t i;
+ grpc_channel_op op;
+ grpc_channel_element *elem;
/* lock, and gather up some stuff to do */
gpr_mu_lock(&server->mu);
@@ -527,6 +535,20 @@ void grpc_server_shutdown(grpc_server *server) {
return;
}
+ nchannels = 0;
+ for (c = server->root_channel_data.next; c != &server->root_channel_data;
+ c = c->next) {
+ nchannels++;
+ }
+ channels = gpr_malloc(sizeof(channel_data *) * nchannels);
+ i = 0;
+ for (c = server->root_channel_data.next; c != &server->root_channel_data;
+ c = c->next) {
+ grpc_channel_internal_ref(c->channel);
+ channels[i] = c;
+ i++;
+ }
+
tags = server->tags;
ntags = server->ntags;
server->tags = NULL;
@@ -535,6 +557,21 @@ void grpc_server_shutdown(grpc_server *server) {
server->shutdown = 1;
gpr_mu_unlock(&server->mu);
+ for (i = 0; i < nchannels; i++) {
+ c = channels[i];
+ elem = grpc_channel_stack_element(
+ grpc_channel_get_channel_stack(c->channel), 0);
+
+ op.type = GRPC_CHANNEL_GOAWAY;
+ op.dir = GRPC_CALL_DOWN;
+ op.data.goaway.status = GRPC_STATUS_OK;
+ op.data.goaway.message = gpr_slice_from_copied_string("Server shutdown");
+ elem->filter->channel_op(elem, &op);
+
+ grpc_channel_internal_unref(c->channel);
+ }
+ gpr_free(channels);
+
/* terminate all the requested calls */
early_terminate_requested_calls(server->cq, tags, ntags);
gpr_free(tags);
diff --git a/src/core/surface/server_chttp2.c b/src/core/surface/server_chttp2.c
index 24c5757166..db8924efea 100644
--- a/src/core/surface/server_chttp2.c
+++ b/src/core/surface/server_chttp2.c
@@ -91,7 +91,7 @@ int grpc_server_add_http2_port(grpc_server *server, const char *addr) {
for (i = 0; i < resolved->naddrs; i++) {
if (grpc_tcp_server_add_port(tcp,
(struct sockaddr *)&resolved->addrs[i].addr,
- resolved->addrs[i].len) >= 0) {
+ resolved->addrs[i].len)) {
count++;
}
}
diff --git a/src/core/transport/chttp2/alpn.c b/src/core/transport/chttp2/alpn.c
new file mode 100644
index 0000000000..cd9cf67a90
--- /dev/null
+++ b/src/core/transport/chttp2/alpn.c
@@ -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.
+ *
+ */
+
+#include "src/core/transport/chttp2/alpn.h"
+
+static const char *const supported_versions[] = {GRPC_CHTTP2_ALPN_VERSION,
+ "h2-15", "h2-14"};
+
+int grpc_chttp2_is_alpn_version_supported(const char *version, size_t size) {
+ size_t i;
+ for (i = 0; i < sizeof(supported_versions) / sizeof(const char *); i++) {
+ if (!strncmp(version, supported_versions[i], size)) return 1;
+ }
+ return 0;
+}
diff --git a/src/core/transport/chttp2/alpn.h b/src/core/transport/chttp2/alpn.h
new file mode 100644
index 0000000000..1353a18b1b
--- /dev/null
+++ b/src/core/transport/chttp2/alpn.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * 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_TRANSPORT_CHTTP2_ALPN_H_
+#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_ALPN_H_
+
+#include <string.h>
+
+#define GRPC_CHTTP2_ALPN_VERSION "h2-15"
+
+/* Retuns 1 if the version is supported, 0 otherwise. */
+int grpc_chttp2_is_alpn_version_supported(const char *version, size_t size);
+
+#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_ALPN_H_ */
diff --git a/src/core/transport/chttp2/bin_encoder.c b/src/core/transport/chttp2/bin_encoder.c
new file mode 100644
index 0000000000..39a303bce9
--- /dev/null
+++ b/src/core/transport/chttp2/bin_encoder.c
@@ -0,0 +1,271 @@
+/*
+ *
+ * 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/transport/chttp2/bin_encoder.h"
+#include "src/core/transport/chttp2/huffsyms.h"
+#include <grpc/support/log.h>
+
+static const char alphabet[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+typedef struct {
+ gpr_uint16 bits;
+ gpr_uint8 length;
+} b64_huff_sym;
+
+static const b64_huff_sym huff_alphabet[64] = {{0x21, 6},
+ {0x5d, 7},
+ {0x5e, 7},
+ {0x5f, 7},
+ {0x60, 7},
+ {0x61, 7},
+ {0x62, 7},
+ {0x63, 7},
+ {0x64, 7},
+ {0x65, 7},
+ {0x66, 7},
+ {0x67, 7},
+ {0x68, 7},
+ {0x69, 7},
+ {0x6a, 7},
+ {0x6b, 7},
+ {0x6c, 7},
+ {0x6d, 7},
+ {0x6e, 7},
+ {0x6f, 7},
+ {0x70, 7},
+ {0x71, 7},
+ {0x72, 7},
+ {0xfc, 8},
+ {0x73, 7},
+ {0xfd, 8},
+ {0x3, 5},
+ {0x23, 6},
+ {0x4, 5},
+ {0x24, 6},
+ {0x5, 5},
+ {0x25, 6},
+ {0x26, 6},
+ {0x27, 6},
+ {0x6, 5},
+ {0x74, 7},
+ {0x75, 7},
+ {0x28, 6},
+ {0x29, 6},
+ {0x2a, 6},
+ {0x7, 5},
+ {0x2b, 6},
+ {0x76, 7},
+ {0x2c, 6},
+ {0x8, 5},
+ {0x9, 5},
+ {0x2d, 6},
+ {0x77, 7},
+ {0x78, 7},
+ {0x79, 7},
+ {0x7a, 7},
+ {0x7b, 7},
+ {0x0, 5},
+ {0x1, 5},
+ {0x2, 5},
+ {0x19, 6},
+ {0x1a, 6},
+ {0x1b, 6},
+ {0x1c, 6},
+ {0x1d, 6},
+ {0x1e, 6},
+ {0x1f, 6},
+ {0x7fb, 11},
+ {0x18, 6}};
+
+static const gpr_uint8 tail_xtra[3] = {0, 2, 3};
+
+gpr_slice grpc_chttp2_base64_encode(gpr_slice input) {
+ size_t input_length = GPR_SLICE_LENGTH(input);
+ size_t input_triplets = input_length / 3;
+ size_t tail_case = input_length % 3;
+ size_t output_length = input_triplets * 4 + tail_xtra[tail_case];
+ gpr_slice output = gpr_slice_malloc(output_length);
+ gpr_uint8 *in = GPR_SLICE_START_PTR(input);
+ gpr_uint8 *out = GPR_SLICE_START_PTR(output);
+ size_t i;
+
+ /* encode full triplets */
+ for (i = 0; i < input_triplets; i++) {
+ out[0] = alphabet[in[0] >> 2];
+ out[1] = alphabet[((in[0] & 0x2) << 4) | (in[1] >> 4)];
+ out[2] = alphabet[((in[1] & 0xf) << 2) | (in[2] >> 6)];
+ out[3] = alphabet[in[2] & 0x3f];
+ out += 4;
+ in += 3;
+ }
+
+ /* encode the remaining bytes */
+ switch (tail_case) {
+ case 0:
+ break;
+ case 1:
+ out[0] = alphabet[in[0] >> 2];
+ out[1] = alphabet[(in[0] & 0x2) << 4];
+ out += 2;
+ in += 1;
+ break;
+ case 2:
+ out[0] = alphabet[in[0] >> 2];
+ out[1] = alphabet[((in[0] & 0x2) << 4) | (in[1] >> 4)];
+ out[2] = alphabet[(in[1] & 0xf) << 2];
+ out += 3;
+ in += 2;
+ break;
+ }
+
+ GPR_ASSERT(out == GPR_SLICE_END_PTR(output));
+ GPR_ASSERT(in == GPR_SLICE_END_PTR(input));
+ return output;
+}
+
+gpr_slice grpc_chttp2_huffman_compress(gpr_slice input) {
+ size_t nbits;
+ gpr_uint8 *in;
+ gpr_uint8 *out;
+ gpr_slice output;
+ gpr_uint32 temp = 0;
+ gpr_uint32 temp_length = 0;
+
+ nbits = 0;
+ for (in = GPR_SLICE_START_PTR(input); in != GPR_SLICE_END_PTR(input); ++in) {
+ nbits += grpc_chttp2_huffsyms[*in].length;
+ }
+
+ output = gpr_slice_malloc(nbits / 8 + (nbits % 8 != 0));
+ out = GPR_SLICE_START_PTR(output);
+ for (in = GPR_SLICE_START_PTR(input); in != GPR_SLICE_END_PTR(input); ++in) {
+ int sym = *in;
+ temp <<= grpc_chttp2_huffsyms[sym].length;
+ temp |= grpc_chttp2_huffsyms[sym].bits;
+ temp_length += grpc_chttp2_huffsyms[sym].length;
+
+ while (temp_length > 8) {
+ temp_length -= 8;
+ *out++ = temp >> temp_length;
+ }
+ }
+
+ if (temp_length) {
+ *out++ = (temp << (8 - temp_length)) | (0xff >> temp_length);
+ }
+
+ GPR_ASSERT(out == GPR_SLICE_END_PTR(output));
+
+ return output;
+}
+
+typedef struct {
+ gpr_uint32 temp;
+ gpr_uint32 temp_length;
+ gpr_uint8 *out;
+} huff_out;
+
+static void enc_flush_some(huff_out *out) {
+ while (out->temp_length > 8) {
+ out->temp_length -= 8;
+ *out->out++ = out->temp >> out->temp_length;
+ }
+}
+
+static void enc_add2(huff_out *out, gpr_uint8 a, gpr_uint8 b) {
+ b64_huff_sym sa = huff_alphabet[a];
+ b64_huff_sym sb = huff_alphabet[b];
+ out->temp =
+ (out->temp << (sa.length + sb.length)) | (sa.bits << sb.length) | sb.bits;
+ out->temp_length += sa.length + sb.length;
+ enc_flush_some(out);
+}
+
+static void enc_add1(huff_out *out, gpr_uint8 a) {
+ b64_huff_sym sa = huff_alphabet[a];
+ out->temp = (out->temp << sa.length) | sa.bits;
+ out->temp_length += sa.length;
+ enc_flush_some(out);
+}
+
+gpr_slice grpc_chttp2_base64_encode_and_huffman_compress(gpr_slice input) {
+ size_t input_length = GPR_SLICE_LENGTH(input);
+ size_t input_triplets = input_length / 3;
+ size_t tail_case = input_length % 3;
+ size_t output_syms = input_triplets * 4 + tail_xtra[tail_case];
+ size_t max_output_bits = 11 * output_syms;
+ size_t max_output_length = max_output_bits / 8 + (max_output_bits % 8 != 0);
+ gpr_slice output = gpr_slice_malloc(max_output_length);
+ gpr_uint8 *in = GPR_SLICE_START_PTR(input);
+ gpr_uint8 *start_out = GPR_SLICE_START_PTR(output);
+ huff_out out;
+ size_t i;
+
+ out.temp = 0;
+ out.temp_length = 0;
+ out.out = start_out;
+
+ /* encode full triplets */
+ for (i = 0; i < input_triplets; i++) {
+ enc_add2(&out, in[0] >> 2, ((in[0] & 0x2) << 4) | (in[1] >> 4));
+ enc_add2(&out, ((in[1] & 0xf) << 2) | (in[2] >> 6), in[2] & 0x3f);
+ in += 3;
+ }
+
+ /* encode the remaining bytes */
+ switch (tail_case) {
+ case 0:
+ break;
+ case 1:
+ enc_add2(&out, in[0] >> 2, (in[0] & 0x2) << 4);
+ in += 1;
+ break;
+ case 2:
+ enc_add2(&out, in[0] >> 2, ((in[0] & 0x2) << 4) | (in[1] >> 4));
+ enc_add1(&out, (in[1] & 0xf) << 2);
+ in += 2;
+ break;
+ }
+
+ if (out.temp_length) {
+ *out.out++ =
+ (out.temp << (8 - out.temp_length)) | (0xff >> out.temp_length);
+ }
+
+ GPR_ASSERT(out.out <= GPR_SLICE_END_PTR(output));
+ GPR_SLICE_SET_LENGTH(output, out.out - start_out);
+
+ GPR_ASSERT(in == GPR_SLICE_END_PTR(input));
+ return output;
+}
diff --git a/src/core/transport/chttp2/bin_encoder.h b/src/core/transport/chttp2/bin_encoder.h
new file mode 100644
index 0000000000..86031137dc
--- /dev/null
+++ b/src/core/transport/chttp2/bin_encoder.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * 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_TRANSPORT_CHTTP2_BIN_ENCODER_H_
+#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_BIN_ENCODER_H_
+
+#include <grpc/support/slice.h>
+
+/* base64 encode a slice. Returns a new slice, does not take ownership of the
+ input */
+gpr_slice grpc_chttp2_base64_encode(gpr_slice input);
+
+/* Compress a slice with the static huffman encoder detailed in the hpack
+ standard. Returns a new slice, does not take ownership of the input */
+gpr_slice grpc_chttp2_huffman_compress(gpr_slice input);
+
+/* equivalent to:
+ gpr_slice x = grpc_chttp2_base64_encode(input);
+ gpr_slice y = grpc_chttp2_huffman_compress(x);
+ gpr_slice_unref(x);
+ return y; */
+gpr_slice grpc_chttp2_base64_encode_and_huffman_compress(gpr_slice input);
+
+#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_BIN_ENCODER_H_ */
diff --git a/src/core/transport/chttp2/frame.h b/src/core/transport/chttp2/frame.h
index 7c0bbe026b..a04e442ed6 100644
--- a/src/core/transport/chttp2/frame.h
+++ b/src/core/transport/chttp2/frame.h
@@ -35,6 +35,7 @@
#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_H__
#include <grpc/support/port_platform.h>
+#include <grpc/support/slice.h>
/* Common definitions for frame handling in the chttp2 transport */
@@ -51,8 +52,12 @@ typedef struct {
gpr_uint8 ack_settings;
gpr_uint8 send_ping_ack;
gpr_uint8 process_ping_reply;
+ gpr_uint8 goaway;
gpr_uint32 window_update;
+ gpr_uint32 goaway_last_stream_index;
+ gpr_uint32 goaway_error;
+ gpr_slice goaway_text;
} grpc_chttp2_parse_state;
#define GRPC_CHTTP2_FRAME_DATA 0
@@ -61,6 +66,7 @@ typedef struct {
#define GRPC_CHTTP2_FRAME_RST_STREAM 3
#define GRPC_CHTTP2_FRAME_SETTINGS 4
#define GRPC_CHTTP2_FRAME_PING 6
+#define GRPC_CHTTP2_FRAME_GOAWAY 7
#define GRPC_CHTTP2_FRAME_WINDOW_UPDATE 8
#define GRPC_CHTTP2_MAX_PAYLOAD_LENGTH ((1 << 14) - 1)
diff --git a/src/core/transport/chttp2/frame_goaway.c b/src/core/transport/chttp2/frame_goaway.c
new file mode 100644
index 0000000000..de7c0b010b
--- /dev/null
+++ b/src/core/transport/chttp2/frame_goaway.c
@@ -0,0 +1,189 @@
+/*
+ *
+ * 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/transport/chttp2/frame_goaway.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+void grpc_chttp2_goaway_parser_init(grpc_chttp2_goaway_parser *p) {
+ p->debug_data = NULL;
+}
+
+void grpc_chttp2_goaway_parser_destroy(grpc_chttp2_goaway_parser *p) {
+ gpr_free(p->debug_data);
+}
+
+grpc_chttp2_parse_error grpc_chttp2_goaway_parser_begin_frame(
+ grpc_chttp2_goaway_parser *p, gpr_uint32 length, gpr_uint8 flags) {
+ if (length < 8) {
+ gpr_log(GPR_ERROR, "goaway frame too short (%d bytes)", length);
+ return GRPC_CHTTP2_CONNECTION_ERROR;
+ }
+
+ gpr_free(p->debug_data);
+ p->debug_length = length - 8;
+ p->debug_data = gpr_malloc(p->debug_length);
+ p->debug_pos = 0;
+ p->state = GRPC_CHTTP2_GOAWAY_LSI0;
+ return GRPC_CHTTP2_PARSE_OK;
+}
+
+grpc_chttp2_parse_error grpc_chttp2_goaway_parser_parse(
+ void *parser, grpc_chttp2_parse_state *state, gpr_slice slice,
+ int is_last) {
+ gpr_uint8 *const beg = GPR_SLICE_START_PTR(slice);
+ gpr_uint8 *const end = GPR_SLICE_END_PTR(slice);
+ gpr_uint8 *cur = beg;
+ grpc_chttp2_goaway_parser *p = parser;
+
+ switch (p->state) {
+ case GRPC_CHTTP2_GOAWAY_LSI0:
+ if (cur == end) {
+ p->state = GRPC_CHTTP2_GOAWAY_LSI0;
+ return GRPC_CHTTP2_PARSE_OK;
+ }
+ p->last_stream_id = ((gpr_uint32)*cur) << 24;
+ ++cur;
+ /* fallthrough */
+ case GRPC_CHTTP2_GOAWAY_LSI1:
+ if (cur == end) {
+ p->state = GRPC_CHTTP2_GOAWAY_LSI1;
+ return GRPC_CHTTP2_PARSE_OK;
+ }
+ p->last_stream_id |= ((gpr_uint32)*cur) << 16;
+ ++cur;
+ /* fallthrough */
+ case GRPC_CHTTP2_GOAWAY_LSI2:
+ if (cur == end) {
+ p->state = GRPC_CHTTP2_GOAWAY_LSI2;
+ return GRPC_CHTTP2_PARSE_OK;
+ }
+ p->last_stream_id |= ((gpr_uint32)*cur) << 8;
+ ++cur;
+ /* fallthrough */
+ case GRPC_CHTTP2_GOAWAY_LSI3:
+ if (cur == end) {
+ p->state = GRPC_CHTTP2_GOAWAY_LSI3;
+ return GRPC_CHTTP2_PARSE_OK;
+ }
+ p->last_stream_id |= ((gpr_uint32)*cur);
+ ++cur;
+ /* fallthrough */
+ case GRPC_CHTTP2_GOAWAY_ERR0:
+ if (cur == end) {
+ p->state = GRPC_CHTTP2_GOAWAY_ERR0;
+ return GRPC_CHTTP2_PARSE_OK;
+ }
+ p->error_code = ((gpr_uint32)*cur) << 24;
+ ++cur;
+ /* fallthrough */
+ case GRPC_CHTTP2_GOAWAY_ERR1:
+ if (cur == end) {
+ p->state = GRPC_CHTTP2_GOAWAY_ERR1;
+ return GRPC_CHTTP2_PARSE_OK;
+ }
+ p->error_code |= ((gpr_uint32)*cur) << 16;
+ ++cur;
+ /* fallthrough */
+ case GRPC_CHTTP2_GOAWAY_ERR2:
+ if (cur == end) {
+ p->state = GRPC_CHTTP2_GOAWAY_ERR2;
+ return GRPC_CHTTP2_PARSE_OK;
+ }
+ p->error_code |= ((gpr_uint32)*cur) << 8;
+ ++cur;
+ /* fallthrough */
+ case GRPC_CHTTP2_GOAWAY_ERR3:
+ if (cur == end) {
+ p->state = GRPC_CHTTP2_GOAWAY_ERR3;
+ return GRPC_CHTTP2_PARSE_OK;
+ }
+ p->error_code |= ((gpr_uint32)*cur);
+ ++cur;
+ /* fallthrough */
+ case GRPC_CHTTP2_GOAWAY_DEBUG:
+ memcpy(p->debug_data + p->debug_pos, cur, end - cur);
+ p->debug_pos += end - cur;
+ p->state = GRPC_CHTTP2_GOAWAY_DEBUG;
+ if (is_last) {
+ state->goaway = 1;
+ state->goaway_last_stream_index = p->last_stream_id;
+ state->goaway_error = p->error_code;
+ state->goaway_text =
+ gpr_slice_new(p->debug_data, p->debug_length, gpr_free);
+ p->debug_data = NULL;
+ }
+ return GRPC_CHTTP2_PARSE_OK;
+ }
+ gpr_log(GPR_ERROR, "Should never end up here");
+ abort();
+ return GRPC_CHTTP2_CONNECTION_ERROR;
+}
+
+void grpc_chttp2_goaway_append(gpr_uint32 last_stream_id, gpr_uint32 error_code,
+ gpr_slice debug_data,
+ gpr_slice_buffer *slice_buffer) {
+ gpr_slice header = gpr_slice_malloc(9 + 4 + 4);
+ gpr_uint8 *p = GPR_SLICE_START_PTR(header);
+ gpr_uint32 frame_length = 4 + 4 + GPR_SLICE_LENGTH(debug_data);
+
+ /* frame header: length */
+ *p++ = frame_length >> 16;
+ *p++ = frame_length >> 8;
+ *p++ = frame_length;
+ /* frame header: type */
+ *p++ = GRPC_CHTTP2_FRAME_GOAWAY;
+ /* frame header: flags */
+ *p++ = 0;
+ /* frame header: stream id */
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ /* payload: last stream id */
+ *p++ = last_stream_id >> 24;
+ *p++ = last_stream_id >> 16;
+ *p++ = last_stream_id >> 8;
+ *p++ = last_stream_id;
+ /* payload: error code */
+ *p++ = error_code >> 24;
+ *p++ = error_code >> 16;
+ *p++ = error_code >> 8;
+ *p++ = error_code;
+ GPR_ASSERT(p == GPR_SLICE_END_PTR(header));
+ gpr_slice_buffer_add(slice_buffer, header);
+ gpr_slice_buffer_add(slice_buffer, debug_data);
+}
diff --git a/src/core/transport/chttp2/frame_goaway.h b/src/core/transport/chttp2/frame_goaway.h
new file mode 100644
index 0000000000..9a3f8e6a7a
--- /dev/null
+++ b/src/core/transport/chttp2/frame_goaway.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * 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_TRANSPORT_CHTTP2_FRAME_GOAWAY_H_
+#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_GOAWAY_H_
+
+#include "src/core/transport/chttp2/frame.h"
+#include <grpc/support/port_platform.h>
+#include <grpc/support/slice.h>
+#include <grpc/support/slice_buffer.h>
+
+typedef enum {
+ GRPC_CHTTP2_GOAWAY_LSI0,
+ GRPC_CHTTP2_GOAWAY_LSI1,
+ GRPC_CHTTP2_GOAWAY_LSI2,
+ GRPC_CHTTP2_GOAWAY_LSI3,
+ GRPC_CHTTP2_GOAWAY_ERR0,
+ GRPC_CHTTP2_GOAWAY_ERR1,
+ GRPC_CHTTP2_GOAWAY_ERR2,
+ GRPC_CHTTP2_GOAWAY_ERR3,
+ GRPC_CHTTP2_GOAWAY_DEBUG
+} grpc_chttp2_goaway_parse_state;
+
+typedef struct {
+ grpc_chttp2_goaway_parse_state state;
+ gpr_uint32 last_stream_id;
+ gpr_uint32 error_code;
+ char *debug_data;
+ gpr_uint32 debug_length;
+ gpr_uint32 debug_pos;
+} grpc_chttp2_goaway_parser;
+
+void grpc_chttp2_goaway_parser_init(grpc_chttp2_goaway_parser *p);
+void grpc_chttp2_goaway_parser_destroy(grpc_chttp2_goaway_parser *p);
+grpc_chttp2_parse_error grpc_chttp2_goaway_parser_begin_frame(
+ grpc_chttp2_goaway_parser *parser, gpr_uint32 length, gpr_uint8 flags);
+grpc_chttp2_parse_error grpc_chttp2_goaway_parser_parse(
+ void *parser, grpc_chttp2_parse_state *state, gpr_slice slice, int is_last);
+
+void grpc_chttp2_goaway_append(gpr_uint32 last_stream_id, gpr_uint32 error_code,
+ gpr_slice debug_data,
+ gpr_slice_buffer *slice_buffer);
+
+#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_FRAME_GOAWAY_H_ */
diff --git a/src/core/transport/chttp2/gen_hpack_tables.c b/src/core/transport/chttp2/gen_hpack_tables.c
index cc94a737ca..301b79ef2c 100644
--- a/src/core/transport/chttp2/gen_hpack_tables.c
+++ b/src/core/transport/chttp2/gen_hpack_tables.c
@@ -39,6 +39,7 @@
#include <assert.h>
#include <grpc/support/log.h>
+#include "src/core/transport/chttp2/huffsyms.h"
/*
* first byte LUT generation
@@ -127,277 +128,10 @@ static void generate_first_byte_lut() {
* Huffman decoder table generation
*/
-#define NSYMS 257
#define MAXHUFFSTATES 1024
-/* Constants pulled from the HPACK spec, and converted to C using the vim
- command:
- :%s/.* \([0-9a-f]\+\) \[ *\([0-9]\+\)\]/{0x\1, \2},/g */
-static const struct {
- unsigned bits;
- unsigned length;
-} huffsyms[NSYMS] = {
- {0x1ff8, 13},
- {0x7fffd8, 23},
- {0xfffffe2, 28},
- {0xfffffe3, 28},
- {0xfffffe4, 28},
- {0xfffffe5, 28},
- {0xfffffe6, 28},
- {0xfffffe7, 28},
- {0xfffffe8, 28},
- {0xffffea, 24},
- {0x3ffffffc, 30},
- {0xfffffe9, 28},
- {0xfffffea, 28},
- {0x3ffffffd, 30},
- {0xfffffeb, 28},
- {0xfffffec, 28},
- {0xfffffed, 28},
- {0xfffffee, 28},
- {0xfffffef, 28},
- {0xffffff0, 28},
- {0xffffff1, 28},
- {0xffffff2, 28},
- {0x3ffffffe, 30},
- {0xffffff3, 28},
- {0xffffff4, 28},
- {0xffffff5, 28},
- {0xffffff6, 28},
- {0xffffff7, 28},
- {0xffffff8, 28},
- {0xffffff9, 28},
- {0xffffffa, 28},
- {0xffffffb, 28},
- {0x14, 6},
- {0x3f8, 10},
- {0x3f9, 10},
- {0xffa, 12},
- {0x1ff9, 13},
- {0x15, 6},
- {0xf8, 8},
- {0x7fa, 11},
- {0x3fa, 10},
- {0x3fb, 10},
- {0xf9, 8},
- {0x7fb, 11},
- {0xfa, 8},
- {0x16, 6},
- {0x17, 6},
- {0x18, 6},
- {0x0, 5},
- {0x1, 5},
- {0x2, 5},
- {0x19, 6},
- {0x1a, 6},
- {0x1b, 6},
- {0x1c, 6},
- {0x1d, 6},
- {0x1e, 6},
- {0x1f, 6},
- {0x5c, 7},
- {0xfb, 8},
- {0x7ffc, 15},
- {0x20, 6},
- {0xffb, 12},
- {0x3fc, 10},
- {0x1ffa, 13},
- {0x21, 6},
- {0x5d, 7},
- {0x5e, 7},
- {0x5f, 7},
- {0x60, 7},
- {0x61, 7},
- {0x62, 7},
- {0x63, 7},
- {0x64, 7},
- {0x65, 7},
- {0x66, 7},
- {0x67, 7},
- {0x68, 7},
- {0x69, 7},
- {0x6a, 7},
- {0x6b, 7},
- {0x6c, 7},
- {0x6d, 7},
- {0x6e, 7},
- {0x6f, 7},
- {0x70, 7},
- {0x71, 7},
- {0x72, 7},
- {0xfc, 8},
- {0x73, 7},
- {0xfd, 8},
- {0x1ffb, 13},
- {0x7fff0, 19},
- {0x1ffc, 13},
- {0x3ffc, 14},
- {0x22, 6},
- {0x7ffd, 15},
- {0x3, 5},
- {0x23, 6},
- {0x4, 5},
- {0x24, 6},
- {0x5, 5},
- {0x25, 6},
- {0x26, 6},
- {0x27, 6},
- {0x6, 5},
- {0x74, 7},
- {0x75, 7},
- {0x28, 6},
- {0x29, 6},
- {0x2a, 6},
- {0x7, 5},
- {0x2b, 6},
- {0x76, 7},
- {0x2c, 6},
- {0x8, 5},
- {0x9, 5},
- {0x2d, 6},
- {0x77, 7},
- {0x78, 7},
- {0x79, 7},
- {0x7a, 7},
- {0x7b, 7},
- {0x7ffe, 15},
- {0x7fc, 11},
- {0x3ffd, 14},
- {0x1ffd, 13},
- {0xffffffc, 28},
- {0xfffe6, 20},
- {0x3fffd2, 22},
- {0xfffe7, 20},
- {0xfffe8, 20},
- {0x3fffd3, 22},
- {0x3fffd4, 22},
- {0x3fffd5, 22},
- {0x7fffd9, 23},
- {0x3fffd6, 22},
- {0x7fffda, 23},
- {0x7fffdb, 23},
- {0x7fffdc, 23},
- {0x7fffdd, 23},
- {0x7fffde, 23},
- {0xffffeb, 24},
- {0x7fffdf, 23},
- {0xffffec, 24},
- {0xffffed, 24},
- {0x3fffd7, 22},
- {0x7fffe0, 23},
- {0xffffee, 24},
- {0x7fffe1, 23},
- {0x7fffe2, 23},
- {0x7fffe3, 23},
- {0x7fffe4, 23},
- {0x1fffdc, 21},
- {0x3fffd8, 22},
- {0x7fffe5, 23},
- {0x3fffd9, 22},
- {0x7fffe6, 23},
- {0x7fffe7, 23},
- {0xffffef, 24},
- {0x3fffda, 22},
- {0x1fffdd, 21},
- {0xfffe9, 20},
- {0x3fffdb, 22},
- {0x3fffdc, 22},
- {0x7fffe8, 23},
- {0x7fffe9, 23},
- {0x1fffde, 21},
- {0x7fffea, 23},
- {0x3fffdd, 22},
- {0x3fffde, 22},
- {0xfffff0, 24},
- {0x1fffdf, 21},
- {0x3fffdf, 22},
- {0x7fffeb, 23},
- {0x7fffec, 23},
- {0x1fffe0, 21},
- {0x1fffe1, 21},
- {0x3fffe0, 22},
- {0x1fffe2, 21},
- {0x7fffed, 23},
- {0x3fffe1, 22},
- {0x7fffee, 23},
- {0x7fffef, 23},
- {0xfffea, 20},
- {0x3fffe2, 22},
- {0x3fffe3, 22},
- {0x3fffe4, 22},
- {0x7ffff0, 23},
- {0x3fffe5, 22},
- {0x3fffe6, 22},
- {0x7ffff1, 23},
- {0x3ffffe0, 26},
- {0x3ffffe1, 26},
- {0xfffeb, 20},
- {0x7fff1, 19},
- {0x3fffe7, 22},
- {0x7ffff2, 23},
- {0x3fffe8, 22},
- {0x1ffffec, 25},
- {0x3ffffe2, 26},
- {0x3ffffe3, 26},
- {0x3ffffe4, 26},
- {0x7ffffde, 27},
- {0x7ffffdf, 27},
- {0x3ffffe5, 26},
- {0xfffff1, 24},
- {0x1ffffed, 25},
- {0x7fff2, 19},
- {0x1fffe3, 21},
- {0x3ffffe6, 26},
- {0x7ffffe0, 27},
- {0x7ffffe1, 27},
- {0x3ffffe7, 26},
- {0x7ffffe2, 27},
- {0xfffff2, 24},
- {0x1fffe4, 21},
- {0x1fffe5, 21},
- {0x3ffffe8, 26},
- {0x3ffffe9, 26},
- {0xffffffd, 28},
- {0x7ffffe3, 27},
- {0x7ffffe4, 27},
- {0x7ffffe5, 27},
- {0xfffec, 20},
- {0xfffff3, 24},
- {0xfffed, 20},
- {0x1fffe6, 21},
- {0x3fffe9, 22},
- {0x1fffe7, 21},
- {0x1fffe8, 21},
- {0x7ffff3, 23},
- {0x3fffea, 22},
- {0x3fffeb, 22},
- {0x1ffffee, 25},
- {0x1ffffef, 25},
- {0xfffff4, 24},
- {0xfffff5, 24},
- {0x3ffffea, 26},
- {0x7ffff4, 23},
- {0x3ffffeb, 26},
- {0x7ffffe6, 27},
- {0x3ffffec, 26},
- {0x3ffffed, 26},
- {0x7ffffe7, 27},
- {0x7ffffe8, 27},
- {0x7ffffe9, 27},
- {0x7ffffea, 27},
- {0x7ffffeb, 27},
- {0xffffffe, 28},
- {0x7ffffec, 27},
- {0x7ffffed, 27},
- {0x7ffffee, 27},
- {0x7ffffef, 27},
- {0x7fffff0, 27},
- {0x3ffffee, 26},
- {0x3fffffff, 30},
-};
-
/* represents a set of symbols as an array of booleans indicating inclusion */
-typedef struct { char included[NSYMS]; } symset;
+typedef struct { char included[GRPC_CHTTP2_NUM_HUFFSYMS]; } symset;
/* represents a lookup table indexed by a nibble */
typedef struct { int values[16]; } nibblelut;
@@ -430,7 +164,7 @@ static nibblelut nibblelut_empty() {
static int nsyms(symset s) {
int i;
int c = 0;
- for (i = 0; i < NSYMS; i++) {
+ for (i = 0; i < GRPC_CHTTP2_NUM_HUFFSYMS; i++) {
c += s.included[i] != 0;
}
return c;
@@ -458,7 +192,8 @@ static int state_index(int bitofs, symset syms, int *isnew) {
int i;
for (i = 0; i < nhuffstates; i++) {
if (huffstates[i].bitofs != bitofs) continue;
- if (0 != memcmp(huffstates[i].syms.included, syms.included, NSYMS))
+ if (0 != memcmp(huffstates[i].syms.included, syms.included,
+ GRPC_CHTTP2_NUM_HUFFSYMS))
continue;
*isnew = 0;
return i;
@@ -510,12 +245,13 @@ static void build_dec_tbl(int state, int nibble, int nibbits, int bitofs,
for (bit = 0; bit < 2; bit++) {
/* walk over active symbols and see if they have this bit set */
symset nextsyms = symset_none();
- for (i = 0; i < NSYMS; i++) {
+ for (i = 0; i < GRPC_CHTTP2_NUM_HUFFSYMS; i++) {
if (!syms.included[i]) continue; /* disregard inactive symbols */
- if (((huffsyms[i].bits >> (huffsyms[i].length - bitofs - 1)) & 1) ==
- bit) {
+ if (((grpc_chttp2_huffsyms[i].bits >>
+ (grpc_chttp2_huffsyms[i].length - bitofs - 1)) &
+ 1) == bit) {
/* the bit is set, include it in the next recursive set */
- if (huffsyms[i].length == bitofs + 1) {
+ if (grpc_chttp2_huffsyms[i].length == bitofs + 1) {
/* additionally, we've gotten to the end of a symbol - this is a
special recursion step: re-activate all the symbols, reset
bitofs to zero, and recurse */
@@ -581,9 +317,25 @@ static void generate_huff_tables() {
dump_ctbl("emit_sub_tbl");
}
+static void generate_base64_huff_encoder_table() {
+ static const char alphabet[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int i;
+
+ printf(
+ "static const struct { gpr_uint16 bits, gpr_uint8 length } "
+ "base64_syms[64] = {\n");
+ for (i = 0; i < 64; i++) {
+ printf("{0x%x, %d},", grpc_chttp2_huffsyms[(unsigned char)alphabet[i]].bits,
+ grpc_chttp2_huffsyms[(unsigned char)alphabet[i]].length);
+ }
+ printf("};\n");
+}
+
int main(void) {
generate_huff_tables();
generate_first_byte_lut();
+ generate_base64_huff_encoder_table();
return 0;
}
diff --git a/src/core/transport/chttp2/huffsyms.c b/src/core/transport/chttp2/huffsyms.c
new file mode 100644
index 0000000000..0f86f1bc30
--- /dev/null
+++ b/src/core/transport/chttp2/huffsyms.c
@@ -0,0 +1,297 @@
+/*
+ *
+ * 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/transport/chttp2/huffsyms.h"
+
+/* Constants pulled from the HPACK spec, and converted to C using the vim
+ command:
+ :%s/.* \([0-9a-f]\+\) \[ *\([0-9]\+\)\]/{0x\1, \2},/g */
+const grpc_chttp2_huffsym grpc_chttp2_huffsyms[GRPC_CHTTP2_NUM_HUFFSYMS] = {
+ {0x1ff8, 13},
+ {0x7fffd8, 23},
+ {0xfffffe2, 28},
+ {0xfffffe3, 28},
+ {0xfffffe4, 28},
+ {0xfffffe5, 28},
+ {0xfffffe6, 28},
+ {0xfffffe7, 28},
+ {0xfffffe8, 28},
+ {0xffffea, 24},
+ {0x3ffffffc, 30},
+ {0xfffffe9, 28},
+ {0xfffffea, 28},
+ {0x3ffffffd, 30},
+ {0xfffffeb, 28},
+ {0xfffffec, 28},
+ {0xfffffed, 28},
+ {0xfffffee, 28},
+ {0xfffffef, 28},
+ {0xffffff0, 28},
+ {0xffffff1, 28},
+ {0xffffff2, 28},
+ {0x3ffffffe, 30},
+ {0xffffff3, 28},
+ {0xffffff4, 28},
+ {0xffffff5, 28},
+ {0xffffff6, 28},
+ {0xffffff7, 28},
+ {0xffffff8, 28},
+ {0xffffff9, 28},
+ {0xffffffa, 28},
+ {0xffffffb, 28},
+ {0x14, 6},
+ {0x3f8, 10},
+ {0x3f9, 10},
+ {0xffa, 12},
+ {0x1ff9, 13},
+ {0x15, 6},
+ {0xf8, 8},
+ {0x7fa, 11},
+ {0x3fa, 10},
+ {0x3fb, 10},
+ {0xf9, 8},
+ {0x7fb, 11},
+ {0xfa, 8},
+ {0x16, 6},
+ {0x17, 6},
+ {0x18, 6},
+ {0x0, 5},
+ {0x1, 5},
+ {0x2, 5},
+ {0x19, 6},
+ {0x1a, 6},
+ {0x1b, 6},
+ {0x1c, 6},
+ {0x1d, 6},
+ {0x1e, 6},
+ {0x1f, 6},
+ {0x5c, 7},
+ {0xfb, 8},
+ {0x7ffc, 15},
+ {0x20, 6},
+ {0xffb, 12},
+ {0x3fc, 10},
+ {0x1ffa, 13},
+ {0x21, 6},
+ {0x5d, 7},
+ {0x5e, 7},
+ {0x5f, 7},
+ {0x60, 7},
+ {0x61, 7},
+ {0x62, 7},
+ {0x63, 7},
+ {0x64, 7},
+ {0x65, 7},
+ {0x66, 7},
+ {0x67, 7},
+ {0x68, 7},
+ {0x69, 7},
+ {0x6a, 7},
+ {0x6b, 7},
+ {0x6c, 7},
+ {0x6d, 7},
+ {0x6e, 7},
+ {0x6f, 7},
+ {0x70, 7},
+ {0x71, 7},
+ {0x72, 7},
+ {0xfc, 8},
+ {0x73, 7},
+ {0xfd, 8},
+ {0x1ffb, 13},
+ {0x7fff0, 19},
+ {0x1ffc, 13},
+ {0x3ffc, 14},
+ {0x22, 6},
+ {0x7ffd, 15},
+ {0x3, 5},
+ {0x23, 6},
+ {0x4, 5},
+ {0x24, 6},
+ {0x5, 5},
+ {0x25, 6},
+ {0x26, 6},
+ {0x27, 6},
+ {0x6, 5},
+ {0x74, 7},
+ {0x75, 7},
+ {0x28, 6},
+ {0x29, 6},
+ {0x2a, 6},
+ {0x7, 5},
+ {0x2b, 6},
+ {0x76, 7},
+ {0x2c, 6},
+ {0x8, 5},
+ {0x9, 5},
+ {0x2d, 6},
+ {0x77, 7},
+ {0x78, 7},
+ {0x79, 7},
+ {0x7a, 7},
+ {0x7b, 7},
+ {0x7ffe, 15},
+ {0x7fc, 11},
+ {0x3ffd, 14},
+ {0x1ffd, 13},
+ {0xffffffc, 28},
+ {0xfffe6, 20},
+ {0x3fffd2, 22},
+ {0xfffe7, 20},
+ {0xfffe8, 20},
+ {0x3fffd3, 22},
+ {0x3fffd4, 22},
+ {0x3fffd5, 22},
+ {0x7fffd9, 23},
+ {0x3fffd6, 22},
+ {0x7fffda, 23},
+ {0x7fffdb, 23},
+ {0x7fffdc, 23},
+ {0x7fffdd, 23},
+ {0x7fffde, 23},
+ {0xffffeb, 24},
+ {0x7fffdf, 23},
+ {0xffffec, 24},
+ {0xffffed, 24},
+ {0x3fffd7, 22},
+ {0x7fffe0, 23},
+ {0xffffee, 24},
+ {0x7fffe1, 23},
+ {0x7fffe2, 23},
+ {0x7fffe3, 23},
+ {0x7fffe4, 23},
+ {0x1fffdc, 21},
+ {0x3fffd8, 22},
+ {0x7fffe5, 23},
+ {0x3fffd9, 22},
+ {0x7fffe6, 23},
+ {0x7fffe7, 23},
+ {0xffffef, 24},
+ {0x3fffda, 22},
+ {0x1fffdd, 21},
+ {0xfffe9, 20},
+ {0x3fffdb, 22},
+ {0x3fffdc, 22},
+ {0x7fffe8, 23},
+ {0x7fffe9, 23},
+ {0x1fffde, 21},
+ {0x7fffea, 23},
+ {0x3fffdd, 22},
+ {0x3fffde, 22},
+ {0xfffff0, 24},
+ {0x1fffdf, 21},
+ {0x3fffdf, 22},
+ {0x7fffeb, 23},
+ {0x7fffec, 23},
+ {0x1fffe0, 21},
+ {0x1fffe1, 21},
+ {0x3fffe0, 22},
+ {0x1fffe2, 21},
+ {0x7fffed, 23},
+ {0x3fffe1, 22},
+ {0x7fffee, 23},
+ {0x7fffef, 23},
+ {0xfffea, 20},
+ {0x3fffe2, 22},
+ {0x3fffe3, 22},
+ {0x3fffe4, 22},
+ {0x7ffff0, 23},
+ {0x3fffe5, 22},
+ {0x3fffe6, 22},
+ {0x7ffff1, 23},
+ {0x3ffffe0, 26},
+ {0x3ffffe1, 26},
+ {0xfffeb, 20},
+ {0x7fff1, 19},
+ {0x3fffe7, 22},
+ {0x7ffff2, 23},
+ {0x3fffe8, 22},
+ {0x1ffffec, 25},
+ {0x3ffffe2, 26},
+ {0x3ffffe3, 26},
+ {0x3ffffe4, 26},
+ {0x7ffffde, 27},
+ {0x7ffffdf, 27},
+ {0x3ffffe5, 26},
+ {0xfffff1, 24},
+ {0x1ffffed, 25},
+ {0x7fff2, 19},
+ {0x1fffe3, 21},
+ {0x3ffffe6, 26},
+ {0x7ffffe0, 27},
+ {0x7ffffe1, 27},
+ {0x3ffffe7, 26},
+ {0x7ffffe2, 27},
+ {0xfffff2, 24},
+ {0x1fffe4, 21},
+ {0x1fffe5, 21},
+ {0x3ffffe8, 26},
+ {0x3ffffe9, 26},
+ {0xffffffd, 28},
+ {0x7ffffe3, 27},
+ {0x7ffffe4, 27},
+ {0x7ffffe5, 27},
+ {0xfffec, 20},
+ {0xfffff3, 24},
+ {0xfffed, 20},
+ {0x1fffe6, 21},
+ {0x3fffe9, 22},
+ {0x1fffe7, 21},
+ {0x1fffe8, 21},
+ {0x7ffff3, 23},
+ {0x3fffea, 22},
+ {0x3fffeb, 22},
+ {0x1ffffee, 25},
+ {0x1ffffef, 25},
+ {0xfffff4, 24},
+ {0xfffff5, 24},
+ {0x3ffffea, 26},
+ {0x7ffff4, 23},
+ {0x3ffffeb, 26},
+ {0x7ffffe6, 27},
+ {0x3ffffec, 26},
+ {0x3ffffed, 26},
+ {0x7ffffe7, 27},
+ {0x7ffffe8, 27},
+ {0x7ffffe9, 27},
+ {0x7ffffea, 27},
+ {0x7ffffeb, 27},
+ {0xffffffe, 28},
+ {0x7ffffec, 27},
+ {0x7ffffed, 27},
+ {0x7ffffee, 27},
+ {0x7ffffef, 27},
+ {0x7fffff0, 27},
+ {0x3ffffee, 26},
+ {0x3fffffff, 30},
+};
diff --git a/src/core/transport/chttp2/huffsyms.h b/src/core/transport/chttp2/huffsyms.h
new file mode 100644
index 0000000000..02d0e53fdd
--- /dev/null
+++ b/src/core/transport/chttp2/huffsyms.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * 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_TRANSPORT_CHTTP2_HUFFSYMS_H_
+#define __GRPC_INTERNAL_TRANSPORT_CHTTP2_HUFFSYMS_H_
+
+/* HPACK static huffman table */
+
+#define GRPC_CHTTP2_NUM_HUFFSYMS 257
+
+typedef struct {
+ unsigned bits;
+ unsigned length;
+} grpc_chttp2_huffsym;
+
+extern const grpc_chttp2_huffsym grpc_chttp2_huffsyms[GRPC_CHTTP2_NUM_HUFFSYMS];
+
+#endif /* __GRPC_INTERNAL_TRANSPORT_CHTTP2_HUFFSYMS_H_ */
diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c
index 8a6b427559..e6629ac74c 100644
--- a/src/core/transport/chttp2_transport.c
+++ b/src/core/transport/chttp2_transport.c
@@ -37,23 +37,24 @@
#include <stdio.h>
#include <string.h>
-#include <grpc/support/alloc.h>
-#include <grpc/support/log.h>
-#include <grpc/support/slice_buffer.h>
-#include <grpc/support/string.h>
-#include <grpc/support/useful.h>
-#include "src/core/transport/transport_impl.h"
-#include "src/core/transport/chttp2/http2_errors.h"
-#include "src/core/transport/chttp2/hpack_parser.h"
#include "src/core/transport/chttp2/frame_data.h"
+#include "src/core/transport/chttp2/frame_goaway.h"
#include "src/core/transport/chttp2/frame_ping.h"
#include "src/core/transport/chttp2/frame_rst_stream.h"
#include "src/core/transport/chttp2/frame_settings.h"
#include "src/core/transport/chttp2/frame_window_update.h"
+#include "src/core/transport/chttp2/hpack_parser.h"
+#include "src/core/transport/chttp2/http2_errors.h"
#include "src/core/transport/chttp2/status_conversion.h"
#include "src/core/transport/chttp2/stream_encoder.h"
#include "src/core/transport/chttp2/stream_map.h"
#include "src/core/transport/chttp2/timeout_encoding.h"
+#include "src/core/transport/transport_impl.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice_buffer.h>
+#include <grpc/support/string.h>
+#include <grpc/support/useful.h>
#define DEFAULT_WINDOW 65536
#define MAX_WINDOW 0x7fffffffu
@@ -160,6 +161,11 @@ typedef struct {
void *user_data;
} outstanding_ping;
+typedef struct {
+ grpc_status_code status;
+ gpr_slice debug;
+} pending_goaway;
+
struct transport {
grpc_transport base; /* must be first */
const grpc_transport_callbacks *cb;
@@ -180,6 +186,7 @@ struct transport {
/* stream indexing */
gpr_uint32 next_stream_id;
+ gpr_uint32 last_incoming_stream_id;
/* settings */
gpr_uint32 settings[NUM_SETTING_SETS][GRPC_CHTTP2_NUM_SETTINGS];
@@ -211,6 +218,12 @@ struct transport {
grpc_chttp2_ping_parser ping;
} simple_parsers;
+ /* goaway */
+ grpc_chttp2_goaway_parser goaway_parser;
+ pending_goaway *pending_goaways;
+ size_t num_pending_goaways;
+ size_t cap_pending_goaways;
+
/* state for a stream that's not yet been created */
grpc_stream_op_buffer new_stream_sopb;
@@ -310,6 +323,7 @@ static void unref_transport(transport *t) {
gpr_slice_buffer_destroy(&t->qbuf);
grpc_chttp2_hpack_parser_destroy(&t->hpack_parser);
grpc_chttp2_hpack_compressor_destroy(&t->hpack_compressor);
+ grpc_chttp2_goaway_parser_destroy(&t->goaway_parser);
grpc_mdstr_unref(t->str_grpc_timeout);
@@ -332,6 +346,11 @@ static void unref_transport(transport *t) {
}
gpr_free(t->pings);
+ for (i = 0; i < t->num_pending_goaways; i++) {
+ gpr_slice_unref(t->pending_goaways[i].debug);
+ }
+ gpr_free(t->pending_goaways);
+
gpr_free(t);
}
@@ -360,6 +379,7 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup,
t->writing = 0;
t->error_state = ERROR_STATE_NONE;
t->next_stream_id = is_client ? 1 : 2;
+ t->last_incoming_stream_id = 0;
t->is_client = is_client;
t->outgoing_window = DEFAULT_WINDOW;
t->incoming_window = DEFAULT_WINDOW;
@@ -370,6 +390,10 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup,
t->ping_capacity = 0;
t->ping_counter = gpr_now().tv_nsec;
grpc_chttp2_hpack_compressor_init(&t->hpack_compressor, mdctx);
+ grpc_chttp2_goaway_parser_init(&t->goaway_parser);
+ t->pending_goaways = NULL;
+ t->num_pending_goaways = 0;
+ t->cap_pending_goaways = 0;
gpr_slice_buffer_init(&t->outbuf);
gpr_slice_buffer_init(&t->qbuf);
if (is_client) {
@@ -456,6 +480,16 @@ static void close_transport(grpc_transport *gt) {
gpr_mu_unlock(&t->mu);
}
+static void goaway(grpc_transport *gt, grpc_status_code status,
+ gpr_slice debug_data) {
+ transport *t = (transport *)gt;
+ lock(t);
+ grpc_chttp2_goaway_append(t->last_incoming_stream_id,
+ grpc_chttp2_grpc_status_to_http2_error(status),
+ debug_data, &t->qbuf);
+ unlock(t);
+}
+
static int init_stream(grpc_transport *gt, grpc_stream *gs,
const void *server_data) {
transport *t = (transport *)gt;
@@ -609,6 +643,9 @@ static void unlock(transport *t) {
int start_write = 0;
int perform_callbacks = 0;
int call_closed = 0;
+ int num_goaways = 0;
+ int i;
+ pending_goaway *goaways = NULL;
grpc_endpoint *ep = t->ep;
/* see if we need to trigger a write - and if so, get the data ready */
@@ -630,9 +667,16 @@ static void unlock(transport *t) {
t->calling_back = 1;
t->error_state = ERROR_STATE_NOTIFIED;
}
+ if (t->num_pending_goaways) {
+ goaways = t->pending_goaways;
+ num_goaways = t->num_pending_goaways;
+ t->pending_goaways = NULL;
+ t->num_pending_goaways = 0;
+ t->calling_back = 1;
+ }
}
- if (perform_callbacks || call_closed) {
+ if (perform_callbacks || call_closed || num_goaways) {
ref_transport(t);
}
@@ -640,6 +684,11 @@ static void unlock(transport *t) {
gpr_mu_unlock(&t->mu);
/* perform some callbacks if necessary */
+ for (i = 0; i < num_goaways; i++) {
+ t->cb->goaway(t->cb_user_data, &t->base, goaways[i].status,
+ goaways[i].debug);
+ }
+
if (perform_callbacks) {
run_callbacks(t);
}
@@ -698,13 +747,15 @@ static void unlock(transport *t) {
}
}
- if (perform_callbacks || call_closed) {
+ if (perform_callbacks || call_closed || num_goaways) {
lock(t);
t->calling_back = 0;
gpr_cv_broadcast(&t->cv);
unlock(t);
unref_transport(t);
}
+
+ gpr_free(goaways);
}
/*
@@ -1130,6 +1181,12 @@ static int init_header_frame_parser(transport *t, int is_continuation) {
gpr_log(GPR_ERROR, "ignoring new stream creation on client");
}
return init_skip_frame(t, 1);
+ } else if (t->last_incoming_stream_id > t->incoming_stream_id) {
+ gpr_log(GPR_ERROR,
+ "ignoring out of order new stream request on server; last stream "
+ "id=%d, new stream id=%d",
+ t->last_incoming_stream_id, t->incoming_stream);
+ return init_skip_frame(t, 1);
}
t->incoming_stream = NULL;
/* if stream is accepted, we set incoming_stream in init_stream */
@@ -1187,6 +1244,19 @@ static int init_ping_parser(transport *t) {
return ok;
}
+static int init_goaway_parser(transport *t) {
+ int ok =
+ GRPC_CHTTP2_PARSE_OK ==
+ grpc_chttp2_goaway_parser_begin_frame(
+ &t->goaway_parser, t->incoming_frame_size, t->incoming_frame_flags);
+ if (!ok) {
+ drop_connection(t);
+ }
+ t->parser = grpc_chttp2_goaway_parser_parse;
+ t->parser_data = &t->goaway_parser;
+ return ok;
+}
+
static int init_settings_frame_parser(transport *t) {
int ok = GRPC_CHTTP2_PARSE_OK ==
grpc_chttp2_settings_parser_begin_frame(
@@ -1240,6 +1310,8 @@ static int init_frame_parser(transport *t) {
return init_window_update_frame_parser(t);
case GRPC_CHTTP2_FRAME_PING:
return init_ping_parser(t);
+ case GRPC_CHTTP2_FRAME_GOAWAY:
+ return init_goaway_parser(t);
default:
gpr_log(GPR_ERROR, "Unknown frame type %02x", t->incoming_frame_type);
return init_skip_frame(t, 0);
@@ -1277,6 +1349,18 @@ static int parse_frame_slice(transport *t, gpr_slice slice, int is_last) {
&t->qbuf,
grpc_chttp2_ping_create(1, t->simple_parsers.ping.opaque_8bytes));
}
+ if (st.goaway) {
+ if (t->num_pending_goaways == t->cap_pending_goaways) {
+ t->cap_pending_goaways = GPR_MAX(1, t->cap_pending_goaways * 2);
+ t->pending_goaways =
+ gpr_realloc(t->pending_goaways,
+ sizeof(pending_goaway) * t->cap_pending_goaways);
+ }
+ t->pending_goaways[t->num_pending_goaways].status =
+ grpc_chttp2_http2_error_to_grpc_status(st.goaway_error);
+ t->pending_goaways[t->num_pending_goaways].debug = st.goaway_text;
+ t->num_pending_goaways++;
+ }
if (st.process_ping_reply) {
for (i = 0; i < t->ping_count; i++) {
if (0 ==
@@ -1455,6 +1539,7 @@ static int process_read(transport *t, gpr_slice slice) {
if (!init_frame_parser(t)) {
return 0;
}
+ t->last_incoming_stream_id = t->incoming_stream_id;
if (t->incoming_frame_size == 0) {
if (!parse_frame_slice(t, gpr_empty_slice(), 1)) {
return 0;
@@ -1599,7 +1684,7 @@ static void run_callbacks(transport *t) {
static const grpc_transport_vtable vtable = {
sizeof(stream), init_stream, send_batch, set_allow_window_updates,
- destroy_stream, abort_stream, close_transport, send_ping,
+ destroy_stream, abort_stream, goaway, close_transport, send_ping,
destroy_transport};
void grpc_create_chttp2_transport(grpc_transport_setup_callback setup,
diff --git a/src/core/transport/transport.c b/src/core/transport/transport.c
index d3291bbbb9..1c44abcadf 100644
--- a/src/core/transport/transport.c
+++ b/src/core/transport/transport.c
@@ -38,6 +38,11 @@ size_t grpc_transport_stream_size(grpc_transport *transport) {
return transport->vtable->sizeof_stream;
}
+void grpc_transport_goaway(grpc_transport *transport, grpc_status_code status,
+ gpr_slice message) {
+ transport->vtable->goaway(transport, status, message);
+}
+
void grpc_transport_close(grpc_transport *transport) {
transport->vtable->close(transport);
}
diff --git a/src/core/transport/transport.h b/src/core/transport/transport.h
index 1872947208..6a089a2a15 100644
--- a/src/core/transport/transport.h
+++ b/src/core/transport/transport.h
@@ -116,6 +116,10 @@ struct grpc_transport_callbacks {
grpc_stream *stream, grpc_stream_op *ops, size_t ops_count,
grpc_stream_state final_state);
+ /* The transport received a goaway */
+ void (*goaway)(void *user_data, grpc_transport *transport,
+ grpc_status_code status, gpr_slice debug);
+
/* The transport has been closed */
void (*closed)(void *user_data, grpc_transport *transport);
};
@@ -198,6 +202,10 @@ void grpc_transport_ping(grpc_transport *transport, void (*cb)(void *user_data),
void grpc_transport_abort_stream(grpc_transport *transport, grpc_stream *stream,
grpc_status_code status);
+/* Advise peer of pending connection termination. */
+void grpc_transport_goaway(struct grpc_transport *transport,
+ grpc_status_code status, gpr_slice debug_data);
+
/* Close a transport. Aborts all open streams. */
void grpc_transport_close(struct grpc_transport *transport);
diff --git a/src/core/transport/transport_impl.h b/src/core/transport/transport_impl.h
index 6acdbf2525..328ead2872 100644
--- a/src/core/transport/transport_impl.h
+++ b/src/core/transport/transport_impl.h
@@ -60,6 +60,10 @@ typedef struct grpc_transport_vtable {
void (*abort_stream)(grpc_transport *self, grpc_stream *stream,
grpc_status_code status);
+ /* implementation of grpc_transport_goaway */
+ void (*goaway)(grpc_transport *self, grpc_status_code status,
+ gpr_slice debug_data);
+
/* implementation of grpc_transport_close */
void (*close)(grpc_transport *self);
diff --git a/src/core/tsi/ssl_transport_security.c b/src/core/tsi/ssl_transport_security.c
index 7bd178b372..2c74b3635a 100644
--- a/src/core/tsi/ssl_transport_security.c
+++ b/src/core/tsi/ssl_transport_security.c
@@ -289,6 +289,17 @@ static tsi_result peer_from_x509(X509* cert, int include_certificate_type,
return result;
}
+/* Logs the SSL error stack. */
+static void log_ssl_error_stack(void) {
+ unsigned long err;
+ while ((err = ERR_get_error()) != 0) {
+ char details[256];
+ ERR_error_string_n(err, details, sizeof(details));
+ gpr_log(GPR_ERROR, "%s", details);
+ }
+}
+
+
/* Performs an SSL_read and handle errors. */
static tsi_result do_ssl_read(SSL* ssl, unsigned char* unprotected_bytes,
uint32_t* unprotected_bytes_size) {
@@ -312,6 +323,7 @@ static tsi_result do_ssl_read(SSL* ssl, unsigned char* unprotected_bytes,
return TSI_UNIMPLEMENTED;
case SSL_ERROR_SSL:
gpr_log(GPR_ERROR, "Corruption detected.");
+ log_ssl_error_stack();
return TSI_DATA_CORRUPTED;
default:
gpr_log(GPR_ERROR, "SSL_read failed with error %s.",
@@ -364,7 +376,10 @@ static tsi_result ssl_ctx_use_certificate_chain(
}
while (1) {
X509* certificate_authority = PEM_read_bio_X509(pem, NULL, NULL, "");
- if (certificate_authority == NULL) break; /* Done reading. */
+ if (certificate_authority == NULL) {
+ ERR_clear_error();
+ break; /* Done reading. */
+ }
if (!SSL_CTX_add_extra_chain_cert(context, certificate_authority)) {
X509_free(certificate_authority);
result = TSI_INVALID_ARGUMENT;
@@ -425,7 +440,10 @@ static tsi_result ssl_ctx_load_verification_certs(
while (1) {
root = PEM_read_bio_X509_AUX(pem, NULL, NULL, "");
- if (root == NULL) break; /* We're at the end of stream. */
+ if (root == NULL) {
+ ERR_clear_error();
+ break; /* We're at the end of stream. */
+ }
if (root_names != NULL) {
root_name = X509_get_subject_name(root);
if (root_name == NULL) {
diff --git a/src/cpp/client/channel.cc b/src/cpp/client/channel.cc
index 7a7529104f..3792869d83 100644
--- a/src/cpp/client/channel.cc
+++ b/src/cpp/client/channel.cc
@@ -59,67 +59,19 @@ Channel::Channel(const grpc::string& target) : target_(target) {
Channel::~Channel() { grpc_channel_destroy(c_channel_); }
namespace {
-// Poll one event from the compeletion queue. Return false when an error
-// occured or the polled type is not expected. If a finished event has been
-// polled, set finished and set status if it has not been set.
-bool NextEvent(grpc_completion_queue* cq, grpc_completion_type expected_type,
- bool* finished, bool* status_set, Status* status,
- google::protobuf::Message* result) {
- // We rely on the c layer to enforce deadline and thus do not use deadline
- // here.
- grpc_event* ev = grpc_completion_queue_next(cq, gpr_inf_future);
- if (!ev) {
- return false;
- }
- bool ret = ev->type == expected_type;
- switch (ev->type) {
- case GRPC_INVOKE_ACCEPTED:
- ret = ret && (ev->data.invoke_accepted == GRPC_OP_OK);
- break;
- case GRPC_READ:
- ret = ret && (ev->data.read != nullptr);
- if (ret && !DeserializeProto(ev->data.read, result)) {
- *status_set = true;
- *status =
- Status(StatusCode::DATA_LOSS, "Failed to parse response proto.");
- ret = false;
- }
- break;
- case GRPC_WRITE_ACCEPTED:
- ret = ret && (ev->data.write_accepted == GRPC_OP_OK);
- break;
- case GRPC_FINISH_ACCEPTED:
- ret = ret && (ev->data.finish_accepted == GRPC_OP_OK);
- break;
- case GRPC_CLIENT_METADATA_READ:
- break;
- case GRPC_FINISHED:
- *finished = true;
- if (!*status_set) {
- *status_set = true;
- StatusCode error_code = static_cast<StatusCode>(ev->data.finished.code);
- grpc::string details(
- ev->data.finished.details ? ev->data.finished.details : "");
- *status = Status(error_code, details);
- }
- break;
- default:
- gpr_log(GPR_ERROR, "Dropping unhandled event with type %d", ev->type);
- break;
- }
- grpc_event_finish(ev);
- return ret;
-}
-
-// If finished is not true, get final status by polling until a finished
-// event is obtained.
-void GetFinalStatus(grpc_completion_queue* cq, bool status_set, bool finished,
+// Pluck the finished event and set to status when it is not nullptr.
+void GetFinalStatus(grpc_completion_queue* cq, void* finished_tag,
Status* status) {
- while (!finished) {
- NextEvent(cq, GRPC_FINISHED, &finished, &status_set, status, nullptr);
+ grpc_event* ev =
+ grpc_completion_queue_pluck(cq, finished_tag, gpr_inf_future);
+ if (status) {
+ StatusCode error_code = static_cast<StatusCode>(ev->data.finished.code);
+ grpc::string details(ev->data.finished.details ? ev->data.finished.details
+ : "");
+ *status = Status(error_code, details);
}
+ grpc_event_finish(ev);
}
-
} // namespace
// TODO(yangg) more error handling
@@ -128,8 +80,6 @@ Status Channel::StartBlockingRpc(const RpcMethod& method,
const google::protobuf::Message& request,
google::protobuf::Message* result) {
Status status;
- bool status_set = false;
- bool finished = false;
gpr_timespec absolute_deadline;
AbsoluteDeadlineTimepoint2Timespec(context->absolute_deadline(),
&absolute_deadline);
@@ -137,59 +87,68 @@ Status Channel::StartBlockingRpc(const RpcMethod& method,
// FIXME(yangg)
"localhost", absolute_deadline);
context->set_call(call);
+ grpc_event* ev;
+ void* finished_tag = reinterpret_cast<char*>(call);
+ void* invoke_tag = reinterpret_cast<char*>(call) + 1;
+ void* metadata_read_tag = reinterpret_cast<char*>(call) + 2;
+ void* write_tag = reinterpret_cast<char*>(call) + 3;
+ void* halfclose_tag = reinterpret_cast<char*>(call) + 4;
+ void* read_tag = reinterpret_cast<char*>(call) + 5;
+
grpc_completion_queue* cq = grpc_completion_queue_create();
context->set_cq(cq);
// add_metadata from context
//
// invoke
- GPR_ASSERT(grpc_call_start_invoke(call, cq, call, call, call,
+ GPR_ASSERT(grpc_call_start_invoke(call, cq, invoke_tag, metadata_read_tag,
+ finished_tag,
GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
- if (!NextEvent(cq, GRPC_INVOKE_ACCEPTED, &status_set, &finished, &status,
- nullptr)) {
- GetFinalStatus(cq, finished, status_set, &status);
- return status;
- }
+ ev = grpc_completion_queue_pluck(cq, invoke_tag, gpr_inf_future);
+ grpc_event_finish(ev);
// write request
grpc_byte_buffer* write_buffer = nullptr;
bool success = SerializeProto(request, &write_buffer);
if (!success) {
grpc_call_cancel(call);
- status_set = true;
status =
Status(StatusCode::DATA_LOSS, "Failed to serialize request proto.");
- GetFinalStatus(cq, finished, status_set, &status);
+ GetFinalStatus(cq, finished_tag, nullptr);
return status;
}
- GPR_ASSERT(grpc_call_start_write(call, write_buffer, call,
+ GPR_ASSERT(grpc_call_start_write(call, write_buffer, write_tag,
GRPC_WRITE_BUFFER_HINT) == GRPC_CALL_OK);
grpc_byte_buffer_destroy(write_buffer);
- if (!NextEvent(cq, GRPC_WRITE_ACCEPTED, &finished, &status_set, &status,
- nullptr)) {
- GetFinalStatus(cq, finished, status_set, &status);
+ ev = grpc_completion_queue_pluck(cq, write_tag, gpr_inf_future);
+
+ success = ev->data.write_accepted == GRPC_OP_OK;
+ grpc_event_finish(ev);
+ if (!success) {
+ GetFinalStatus(cq, finished_tag, &status);
return status;
}
// writes done
- GPR_ASSERT(grpc_call_writes_done(call, call) == GRPC_CALL_OK);
- if (!NextEvent(cq, GRPC_FINISH_ACCEPTED, &finished, &status_set, &status,
- nullptr)) {
- GetFinalStatus(cq, finished, status_set, &status);
- return status;
- }
+ GPR_ASSERT(grpc_call_writes_done(call, halfclose_tag) == GRPC_CALL_OK);
+ ev = grpc_completion_queue_pluck(cq, halfclose_tag, gpr_inf_future);
+ grpc_event_finish(ev);
// start read metadata
//
- if (!NextEvent(cq, GRPC_CLIENT_METADATA_READ, &finished, &status_set, &status,
- nullptr)) {
- GetFinalStatus(cq, finished, status_set, &status);
- return status;
- }
+ ev = grpc_completion_queue_pluck(cq, metadata_read_tag, gpr_inf_future);
+ grpc_event_finish(ev);
// start read
- GPR_ASSERT(grpc_call_start_read(call, call) == GRPC_CALL_OK);
- if (!NextEvent(cq, GRPC_READ, &finished, &status_set, &status, result)) {
- GetFinalStatus(cq, finished, status_set, &status);
- return status;
+ GPR_ASSERT(grpc_call_start_read(call, read_tag) == GRPC_CALL_OK);
+ ev = grpc_completion_queue_pluck(cq, read_tag, gpr_inf_future);
+ if (ev->data.read) {
+ if (!DeserializeProto(ev->data.read, result)) {
+ grpc_event_finish(ev);
+ status = Status(StatusCode::DATA_LOSS, "Failed to parse response proto.");
+ GetFinalStatus(cq, finished_tag, nullptr);
+ return status;
+ }
}
+ grpc_event_finish(ev);
+
// wait status
- GetFinalStatus(cq, finished, status_set, &status);
+ GetFinalStatus(cq, finished_tag, &status);
return status;
}
diff --git a/src/cpp/client/client_context.cc b/src/cpp/client/client_context.cc
index 78774a7f12..58a8ad252b 100644
--- a/src/cpp/client/client_context.cc
+++ b/src/cpp/client/client_context.cc
@@ -50,6 +50,14 @@ ClientContext::~ClientContext() {
}
if (cq_) {
grpc_completion_queue_shutdown(cq_);
+ // Drain cq_.
+ grpc_event* ev;
+ grpc_completion_type t;
+ do {
+ ev = grpc_completion_queue_next(cq_, gpr_inf_future);
+ t = ev->type;
+ grpc_event_finish(ev);
+ } while (t != GRPC_QUEUE_SHUTDOWN);
grpc_completion_queue_destroy(cq_);
}
}
diff --git a/src/cpp/server/async_server_context.cc b/src/cpp/server/async_server_context.cc
index b231f4b0cf..0a9c07f403 100644
--- a/src/cpp/server/async_server_context.cc
+++ b/src/cpp/server/async_server_context.cc
@@ -75,18 +75,11 @@ bool AsyncServerContext::StartWrite(const google::protobuf::Message& response,
return err == GRPC_CALL_OK;
}
-namespace {
-grpc_status TranslateStatus(const Status& status) {
- grpc_status c_status;
- // TODO(yangg)
- c_status.code = GRPC_STATUS_OK;
- c_status.details = nullptr;
- return c_status;
-}
-} // namespace
-
bool AsyncServerContext::StartWriteStatus(const Status& status) {
- grpc_status c_status = TranslateStatus(status);
+ grpc_status c_status = {static_cast<grpc_status_code>(status.code()),
+ status.details().empty()
+ ? nullptr
+ : const_cast<char*>(status.details().c_str())};
grpc_call_error err = grpc_call_start_write_status(call_, c_status, this);
return err == GRPC_CALL_OK;
}
diff --git a/src/cpp/server/rpc_service_method.h b/src/cpp/server/rpc_service_method.h
index ac2badda71..425545fd22 100644
--- a/src/cpp/server/rpc_service_method.h
+++ b/src/cpp/server/rpc_service_method.h
@@ -42,8 +42,10 @@
#include "src/cpp/rpc_method.h"
#include <google/protobuf/message.h>
#include <grpc++/status.h>
+#include <grpc++/stream.h>
namespace grpc {
+class StreamContextInterface;
// TODO(rocking): we might need to split this file into multiple ones.
@@ -53,23 +55,27 @@ class MethodHandler {
virtual ~MethodHandler() {}
struct HandlerParameter {
HandlerParameter(const google::protobuf::Message* req, google::protobuf::Message* resp)
- : request(req), response(resp) {}
+ : request(req), response(resp), stream_context(nullptr) {}
+ HandlerParameter(const google::protobuf::Message* req, google::protobuf::Message* resp,
+ StreamContextInterface* context)
+ : request(req), response(resp), stream_context(context) {}
const google::protobuf::Message* request;
google::protobuf::Message* response;
+ StreamContextInterface* stream_context;
};
- virtual ::grpc::Status RunHandler(const HandlerParameter& param) = 0;
+ virtual Status RunHandler(const HandlerParameter& param) = 0;
};
// A wrapper class of an application provided rpc method handler.
template <class ServiceType, class RequestType, class ResponseType>
class RpcMethodHandler : public MethodHandler {
public:
- RpcMethodHandler(std::function<::grpc::Status(
- ServiceType*, const RequestType*, ResponseType*)> func,
+ RpcMethodHandler(std::function<Status(ServiceType*, const RequestType*,
+ ResponseType*)> func,
ServiceType* service)
: func_(func), service_(service) {}
- ::grpc::Status RunHandler(const HandlerParameter& param) final {
+ Status RunHandler(const HandlerParameter& param) final {
// Invoke application function, cast proto messages to their actual types.
return func_(service_, dynamic_cast<const RequestType*>(param.request),
dynamic_cast<ResponseType*>(param.response));
@@ -77,20 +83,84 @@ class RpcMethodHandler : public MethodHandler {
private:
// Application provided rpc handler function.
- std::function<::grpc::Status(ServiceType*, const RequestType*, ResponseType*)>
- func_;
+ std::function<Status(ServiceType*, const RequestType*, ResponseType*)> func_;
// The class the above handler function lives in.
ServiceType* service_;
};
+// A wrapper class of an application provided client streaming handler.
+template <class ServiceType, class RequestType, class ResponseType>
+class ClientStreamingHandler : public MethodHandler {
+ public:
+ ClientStreamingHandler(
+ std::function<Status(ServiceType*, ServerReader<RequestType>*,
+ ResponseType*)> func,
+ ServiceType* service)
+ : func_(func), service_(service) {}
+
+ Status RunHandler(const HandlerParameter& param) final {
+ ServerReader<RequestType> reader(param.stream_context);
+ return func_(service_, &reader,
+ dynamic_cast<ResponseType*>(param.response));
+ }
+
+ private:
+ std::function<Status(ServiceType*, ServerReader<RequestType>*, ResponseType*)>
+ func_;
+ ServiceType* service_;
+};
+
+// A wrapper class of an application provided server streaming handler.
+template <class ServiceType, class RequestType, class ResponseType>
+class ServerStreamingHandler : public MethodHandler {
+ public:
+ ServerStreamingHandler(
+ std::function<Status(ServiceType*, const RequestType*,
+ ServerWriter<ResponseType>*)> func,
+ ServiceType* service)
+ : func_(func), service_(service) {}
+
+ Status RunHandler(const HandlerParameter& param) final {
+ ServerWriter<ResponseType> writer(param.stream_context);
+ return func_(service_, dynamic_cast<const RequestType*>(param.request),
+ &writer);
+ }
+
+ private:
+ std::function<Status(ServiceType*, const RequestType*,
+ ServerWriter<ResponseType>*)> func_;
+ ServiceType* service_;
+};
+
+// A wrapper class of an application provided bidi-streaming handler.
+template <class ServiceType, class RequestType, class ResponseType>
+class BidiStreamingHandler : public MethodHandler {
+ public:
+ BidiStreamingHandler(
+ std::function<Status(
+ ServiceType*, ServerReaderWriter<ResponseType, RequestType>*)> func,
+ ServiceType* service)
+ : func_(func), service_(service) {}
+
+ Status RunHandler(const HandlerParameter& param) final {
+ ServerReaderWriter<ResponseType, RequestType> stream(param.stream_context);
+ return func_(service_, &stream);
+ }
+
+ private:
+ std::function<Status(ServiceType*,
+ ServerReaderWriter<ResponseType, RequestType>*)> func_;
+ ServiceType* service_;
+};
+
// Server side rpc method class
class RpcServiceMethod : public RpcMethod {
public:
// Takes ownership of the handler and two prototype objects.
- RpcServiceMethod(const char* name, MethodHandler* handler,
- google::protobuf::Message* request_prototype,
+ RpcServiceMethod(const char* name, RpcMethod::RpcType type,
+ MethodHandler* handler, google::protobuf::Message* request_prototype,
google::protobuf::Message* response_prototype)
- : RpcMethod(name),
+ : RpcMethod(name, type),
handler_(handler),
request_prototype_(request_prototype),
response_prototype_(response_prototype) {}
diff --git a/src/cpp/server/server_credentials.cc b/src/cpp/server/server_credentials.cc
new file mode 100644
index 0000000000..d23a09f3c1
--- /dev/null
+++ b/src/cpp/server/server_credentials.cc
@@ -0,0 +1,62 @@
+/*
+ *
+ * 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 <grpc/grpc_security.h>
+
+#include <grpc++/server_credentials.h>
+
+namespace grpc {
+
+ServerCredentials::ServerCredentials(grpc_server_credentials* c_creds)
+ : creds_(c_creds) {}
+
+ServerCredentials::~ServerCredentials() {
+ grpc_server_credentials_release(creds_);
+}
+
+grpc_server_credentials* ServerCredentials::GetRawCreds() { return creds_; }
+
+std::shared_ptr<ServerCredentials> ServerCredentialsFactory::SslCredentials(
+ const SslServerCredentialsOptions& options) {
+ grpc_server_credentials* c_creds = grpc_ssl_server_credentials_create(
+ reinterpret_cast<const unsigned char*>(options.pem_root_certs.c_str()),
+ options.pem_root_certs.size(),
+ reinterpret_cast<const unsigned char*>(options.pem_private_key.c_str()),
+ options.pem_private_key.size(),
+ reinterpret_cast<const unsigned char*>(options.pem_cert_chain.c_str()),
+ options.pem_cert_chain.size());
+ return std::shared_ptr<ServerCredentials>(new ServerCredentials(c_creds));
+}
+
+} // namespace grpc
diff --git a/src/cpp/server/server_rpc_handler.cc b/src/cpp/server/server_rpc_handler.cc
index 2d5a081deb..4c8d0cd04e 100644
--- a/src/cpp/server/server_rpc_handler.cc
+++ b/src/cpp/server/server_rpc_handler.cc
@@ -35,20 +35,16 @@
#include <grpc/support/log.h>
#include "src/cpp/server/rpc_service_method.h"
+#include "src/cpp/stream/stream_context.h"
#include <grpc++/async_server_context.h>
namespace grpc {
ServerRpcHandler::ServerRpcHandler(AsyncServerContext* server_context,
RpcServiceMethod* method)
- : server_context_(server_context),
- method_(method) {
-}
+ : server_context_(server_context), method_(method) {}
void ServerRpcHandler::StartRpc() {
- // Start the rpc on this dedicated completion queue.
- server_context_->Accept(cq_.cq());
-
if (method_ == nullptr) {
// Method not supported, finish the rpc with error.
// TODO(rocking): do we need to call read to consume the request?
@@ -56,30 +52,54 @@ void ServerRpcHandler::StartRpc() {
return;
}
- // Allocate request and response.
- std::unique_ptr<google::protobuf::Message> request(method_->AllocateRequestProto());
- std::unique_ptr<google::protobuf::Message> response(method_->AllocateResponseProto());
-
- // Read request
- server_context_->StartRead(request.get());
- auto type = WaitForNextEvent();
- GPR_ASSERT(type == CompletionQueue::SERVER_READ_OK);
-
- // Run the application's rpc handler
- MethodHandler* handler = method_->handler();
- Status status = handler->RunHandler(
- MethodHandler::HandlerParameter(request.get(), response.get()));
-
- if (status.IsOk()) {
- // Send the response if we get an ok status.
- server_context_->StartWrite(*response, 0);
- type = WaitForNextEvent();
- if (type != CompletionQueue::SERVER_WRITE_OK) {
- status = Status(StatusCode::INTERNAL, "Error writing response.");
+ if (method_->method_type() == RpcMethod::NORMAL_RPC) {
+ // Start the rpc on this dedicated completion queue.
+ server_context_->Accept(cq_.cq());
+
+ // Allocate request and response.
+ std::unique_ptr<google::protobuf::Message> request(method_->AllocateRequestProto());
+ std::unique_ptr<google::protobuf::Message> response(method_->AllocateResponseProto());
+
+ // Read request
+ server_context_->StartRead(request.get());
+ auto type = WaitForNextEvent();
+ GPR_ASSERT(type == CompletionQueue::SERVER_READ_OK);
+
+ // Run the application's rpc handler
+ MethodHandler* handler = method_->handler();
+ Status status = handler->RunHandler(
+ MethodHandler::HandlerParameter(request.get(), response.get()));
+
+ if (status.IsOk()) {
+ // Send the response if we get an ok status.
+ server_context_->StartWrite(*response, 0);
+ type = WaitForNextEvent();
+ if (type != CompletionQueue::SERVER_WRITE_OK) {
+ status = Status(StatusCode::INTERNAL, "Error writing response.");
+ }
}
- }
- FinishRpc(status);
+ FinishRpc(status);
+ } else {
+ // Allocate request and response.
+ // TODO(yangg) maybe not allocate both when not needed?
+ std::unique_ptr<google::protobuf::Message> request(method_->AllocateRequestProto());
+ std::unique_ptr<google::protobuf::Message> response(method_->AllocateResponseProto());
+
+ StreamContext stream_context(*method_, server_context_->call(), cq_.cq(),
+ request.get(), response.get());
+
+ // Run the application's rpc handler
+ MethodHandler* handler = method_->handler();
+ Status status = handler->RunHandler(MethodHandler::HandlerParameter(
+ request.get(), response.get(), &stream_context));
+ if (status.IsOk() &&
+ method_->method_type() == RpcMethod::CLIENT_STREAMING) {
+ stream_context.Write(response.get(), false);
+ }
+ // TODO(yangg) Do we need to consider the status in stream_context?
+ FinishRpc(status);
+ }
}
CompletionQueue::CompletionType ServerRpcHandler::WaitForNextEvent() {
@@ -94,11 +114,15 @@ CompletionQueue::CompletionType ServerRpcHandler::WaitForNextEvent() {
void ServerRpcHandler::FinishRpc(const Status& status) {
server_context_->StartWriteStatus(status);
- CompletionQueue::CompletionType type = WaitForNextEvent();
- // TODO(rocking): do we care about this return type?
+ CompletionQueue::CompletionType type;
+ // HALFCLOSE_OK and RPC_END events come in either order.
+ type = WaitForNextEvent();
+ GPR_ASSERT(type == CompletionQueue::HALFCLOSE_OK ||
+ type == CompletionQueue::RPC_END);
type = WaitForNextEvent();
- GPR_ASSERT(type == CompletionQueue::RPC_END);
+ GPR_ASSERT(type == CompletionQueue::HALFCLOSE_OK ||
+ type == CompletionQueue::RPC_END);
cq_.Shutdown();
type = WaitForNextEvent();
diff --git a/src/cpp/stream/stream_context.cc b/src/cpp/stream/stream_context.cc
index 07e771f7e1..706e90c481 100644
--- a/src/cpp/stream/stream_context.cc
+++ b/src/cpp/stream/stream_context.cc
@@ -33,7 +33,6 @@
#include "src/cpp/stream/stream_context.h"
-#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include "src/cpp/rpc_method.h"
#include "src/cpp/proto/proto_utils.h"
@@ -50,227 +49,146 @@ StreamContext::StreamContext(const RpcMethod& method, ClientContext* context,
google::protobuf::Message* result)
: is_client_(true),
method_(&method),
- context_(context),
- request_(request),
+ call_(context->call()),
+ cq_(context->cq()),
+ request_(const_cast<google::protobuf::Message*>(request)),
result_(result),
- invoke_ev_(nullptr),
- read_ev_(nullptr),
- write_ev_(nullptr),
- reading_(false),
- writing_(false),
- got_read_(false),
- got_write_(false),
peer_halfclosed_(false),
- self_halfclosed_(false),
- stream_finished_(false),
- waiting_(false) {
+ self_halfclosed_(false) {
GPR_ASSERT(method_->method_type() != RpcMethod::RpcType::NORMAL_RPC);
}
-StreamContext::~StreamContext() { cq_poller_.join(); }
-
-void StreamContext::PollingLoop() {
- grpc_event* ev = nullptr;
- gpr_timespec absolute_deadline;
- AbsoluteDeadlineTimepoint2Timespec(context_->absolute_deadline(),
- &absolute_deadline);
- std::condition_variable* cv_to_notify = nullptr;
- std::unique_lock<std::mutex> lock(mu_, std::defer_lock);
- while (1) {
- cv_to_notify = nullptr;
- lock.lock();
- if (stream_finished_ && !reading_ && !writing_) {
- return;
- }
- lock.unlock();
- ev = grpc_completion_queue_next(context_->cq(), absolute_deadline);
- lock.lock();
- if (!ev) {
- stream_finished_ = true;
- final_status_ = Status(StatusCode::DEADLINE_EXCEEDED);
- std::condition_variable* cvs[3] = {reading_ ? &read_cv_ : nullptr,
- writing_ ? &write_cv_ : nullptr,
- waiting_ ? &finish_cv_ : nullptr};
- got_read_ = reading_;
- got_write_ = writing_;
- read_ev_ = nullptr;
- write_ev_ = nullptr;
- lock.unlock();
- for (int i = 0; i < 3; i++) {
- if (cvs[i]) cvs[i]->notify_one();
- }
- break;
- }
- switch (ev->type) {
- case GRPC_READ:
- GPR_ASSERT(reading_);
- got_read_ = true;
- read_ev_ = ev;
- cv_to_notify = &read_cv_;
- reading_ = false;
- break;
- case GRPC_FINISH_ACCEPTED:
- case GRPC_WRITE_ACCEPTED:
- got_write_ = true;
- write_ev_ = ev;
- cv_to_notify = &write_cv_;
- writing_ = false;
- break;
- case GRPC_FINISHED: {
- grpc::string error_details(
- ev->data.finished.details ? ev->data.finished.details : "");
- final_status_ = Status(static_cast<StatusCode>(ev->data.finished.code),
- error_details);
- grpc_event_finish(ev);
- stream_finished_ = true;
- if (waiting_) {
- cv_to_notify = &finish_cv_;
- }
- break;
- }
- case GRPC_INVOKE_ACCEPTED:
- invoke_ev_ = ev;
- cv_to_notify = &invoke_cv_;
- break;
- case GRPC_CLIENT_METADATA_READ:
- grpc_event_finish(ev);
- break;
- default:
- grpc_event_finish(ev);
- // not handling other types now
- gpr_log(GPR_ERROR, "unknown event type");
- abort();
- }
- lock.unlock();
- if (cv_to_notify) {
- cv_to_notify->notify_one();
- }
- }
+// Server only ctor
+StreamContext::StreamContext(const RpcMethod& method, grpc_call* call,
+ grpc_completion_queue* cq,
+ google::protobuf::Message* request, google::protobuf::Message* result)
+ : is_client_(false),
+ method_(&method),
+ call_(call),
+ cq_(cq),
+ request_(request),
+ result_(result),
+ peer_halfclosed_(false),
+ self_halfclosed_(false) {
+ GPR_ASSERT(method_->method_type() != RpcMethod::RpcType::NORMAL_RPC);
}
-void StreamContext::Start(bool buffered) {
- // TODO(yangg) handle metadata send path
- int flag = buffered ? GRPC_WRITE_BUFFER_HINT : 0;
- grpc_call_error error = grpc_call_start_invoke(
- context_->call(), context_->cq(), this, this, this, flag);
- GPR_ASSERT(GRPC_CALL_OK == error);
- // kicks off the poller thread
- cq_poller_ = std::thread(&StreamContext::PollingLoop, this);
- std::unique_lock<std::mutex> lock(mu_);
- while (!invoke_ev_) {
- invoke_cv_.wait(lock);
- }
- lock.unlock();
- GPR_ASSERT(invoke_ev_->data.invoke_accepted == GRPC_OP_OK);
- grpc_event_finish(invoke_ev_);
-}
+StreamContext::~StreamContext() {}
-namespace {
-// Wait for got_event with event_cv protected by mu, return event.
-grpc_event* WaitForEvent(bool* got_event, std::condition_variable* event_cv,
- std::mutex* mu, grpc_event** event) {
- std::unique_lock<std::mutex> lock(*mu);
- while (*got_event == false) {
- event_cv->wait(lock);
+void StreamContext::Start(bool buffered) {
+ if (is_client_) {
+ // TODO(yangg) handle metadata send path
+ int flag = buffered ? GRPC_WRITE_BUFFER_HINT : 0;
+ grpc_call_error error = grpc_call_start_invoke(call(), cq(), invoke_tag(),
+ client_metadata_read_tag(),
+ finished_tag(), flag);
+ GPR_ASSERT(GRPC_CALL_OK == error);
+ grpc_event* invoke_ev =
+ grpc_completion_queue_pluck(cq(), invoke_tag(), gpr_inf_future);
+ grpc_event_finish(invoke_ev);
+ } else {
+ // TODO(yangg) metadata needs to be added before accept
+ // TODO(yangg) correctly set flag to accept
+ grpc_call_error error = grpc_call_accept(call(), cq(), finished_tag(), 0);
+ GPR_ASSERT(GRPC_CALL_OK == error);
}
- *got_event = false;
- return *event;
}
-} // namespace
bool StreamContext::Read(google::protobuf::Message* msg) {
- std::unique_lock<std::mutex> lock(mu_);
- if (stream_finished_) {
- peer_halfclosed_ = true;
- return false;
- }
- reading_ = true;
- lock.unlock();
-
- grpc_call_error err = grpc_call_start_read(context_->call(), this);
+ // TODO(yangg) check peer_halfclosed_ here for possible early return.
+ grpc_call_error err = grpc_call_start_read(call(), read_tag());
GPR_ASSERT(err == GRPC_CALL_OK);
-
- grpc_event* ev = WaitForEvent(&got_read_, &read_cv_, &mu_, &read_ev_);
- if (!ev) {
- return false;
- }
- GPR_ASSERT(ev->type == GRPC_READ);
+ grpc_event* read_ev =
+ grpc_completion_queue_pluck(cq(), read_tag(), gpr_inf_future);
+ GPR_ASSERT(read_ev->type == GRPC_READ);
bool ret = true;
- if (ev->data.read) {
- if (!DeserializeProto(ev->data.read, msg)) {
- ret = false; // parse error
- // TODO(yangg) cancel the stream.
+ if (read_ev->data.read) {
+ if (!DeserializeProto(read_ev->data.read, msg)) {
+ ret = false;
+ FinishStream(
+ Status(StatusCode::DATA_LOSS, "Failed to parse incoming proto"),
+ true);
}
} else {
ret = false;
peer_halfclosed_ = true;
}
- grpc_event_finish(ev);
+ grpc_event_finish(read_ev);
return ret;
}
bool StreamContext::Write(const google::protobuf::Message* msg, bool is_last) {
+ // TODO(yangg) check self_halfclosed_ for possible early return.
bool ret = true;
grpc_event* ev = nullptr;
- std::unique_lock<std::mutex> lock(mu_);
- if (stream_finished_) {
- self_halfclosed_ = true;
- return false;
- }
- writing_ = true;
- lock.unlock();
-
if (msg) {
grpc_byte_buffer* out_buf = nullptr;
if (!SerializeProto(*msg, &out_buf)) {
FinishStream(Status(StatusCode::INVALID_ARGUMENT,
- "Failed to serialize request proto"),
+ "Failed to serialize outgoing proto"),
true);
return false;
}
int flag = is_last ? GRPC_WRITE_BUFFER_HINT : 0;
grpc_call_error err =
- grpc_call_start_write(context_->call(), out_buf, this, flag);
+ grpc_call_start_write(call(), out_buf, write_tag(), flag);
grpc_byte_buffer_destroy(out_buf);
GPR_ASSERT(err == GRPC_CALL_OK);
- ev = WaitForEvent(&got_write_, &write_cv_, &mu_, &write_ev_);
- if (!ev) {
- return false;
- }
+ ev = grpc_completion_queue_pluck(cq(), write_tag(), gpr_inf_future);
GPR_ASSERT(ev->type == GRPC_WRITE_ACCEPTED);
ret = ev->data.write_accepted == GRPC_OP_OK;
grpc_event_finish(ev);
}
- if (is_last) {
- grpc_call_error err = grpc_call_writes_done(context_->call(), this);
+ if (ret && is_last) {
+ grpc_call_error err = grpc_call_writes_done(call(), halfclose_tag());
GPR_ASSERT(err == GRPC_CALL_OK);
- ev = WaitForEvent(&got_write_, &write_cv_, &mu_, &write_ev_);
- if (!ev) {
- return false;
- }
+ ev = grpc_completion_queue_pluck(cq(), halfclose_tag(), gpr_inf_future);
GPR_ASSERT(ev->type == GRPC_FINISH_ACCEPTED);
grpc_event_finish(ev);
+
self_halfclosed_ = true;
+ } else if (!ret) { // Stream broken
+ self_halfclosed_ = true;
+ peer_halfclosed_ = true;
}
+
return ret;
}
const Status& StreamContext::Wait() {
- std::unique_lock<std::mutex> lock(mu_);
- // TODO(yangg) if not halfclosed cancel the stream
- GPR_ASSERT(self_halfclosed_);
- GPR_ASSERT(peer_halfclosed_);
- GPR_ASSERT(!waiting_);
- waiting_ = true;
- while (!stream_finished_) {
- finish_cv_.wait(lock);
+ // TODO(yangg) properly support metadata
+ grpc_event* metadata_ev = grpc_completion_queue_pluck(
+ cq(), client_metadata_read_tag(), gpr_inf_future);
+ grpc_event_finish(metadata_ev);
+ // TODO(yangg) protect states by a mutex, including other places.
+ if (!self_halfclosed_ || !peer_halfclosed_) {
+ FinishStream(Status::Cancelled, true);
+ } else {
+ grpc_event* finish_ev =
+ grpc_completion_queue_pluck(cq(), finished_tag(), gpr_inf_future);
+ GPR_ASSERT(finish_ev->type == GRPC_FINISHED);
+ std::string error_details(finish_ev->data.finished.details
+ ? finish_ev->data.finished.details
+ : "");
+ final_status_ = Status(
+ static_cast<StatusCode>(finish_ev->data.finished.code), error_details);
+ grpc_event_finish(finish_ev);
}
return final_status_;
}
-void StreamContext::FinishStream(const Status& status, bool send) { return; }
+void StreamContext::FinishStream(const Status& status, bool send) {
+ if (send) {
+ grpc_call_cancel(call());
+ }
+ grpc_event* finish_ev =
+ grpc_completion_queue_pluck(cq(), finished_tag(), gpr_inf_future);
+ GPR_ASSERT(finish_ev->type == GRPC_FINISHED);
+ grpc_event_finish(finish_ev);
+ final_status_ = status;
+}
} // namespace grpc
diff --git a/src/cpp/stream/stream_context.h b/src/cpp/stream/stream_context.h
index b7f462f323..6c31095042 100644
--- a/src/cpp/stream/stream_context.h
+++ b/src/cpp/stream/stream_context.h
@@ -34,10 +34,7 @@
#ifndef __GRPCPP_INTERNAL_STREAM_STREAM_CONTEXT_H__
#define __GRPCPP_INTERNAL_STREAM_STREAM_CONTEXT_H__
-#include <condition_variable>
-#include <mutex>
-#include <thread>
-
+#include <grpc/grpc.h>
#include <grpc++/status.h>
#include <grpc++/stream_context_interface.h>
@@ -47,8 +44,6 @@ class Message;
}
}
-struct grpc_event;
-
namespace grpc {
class ClientContext;
class RpcMethod;
@@ -57,6 +52,9 @@ class StreamContext : public StreamContextInterface {
public:
StreamContext(const RpcMethod& method, ClientContext* context,
const google::protobuf::Message* request, google::protobuf::Message* result);
+ StreamContext(const RpcMethod& method, grpc_call* call,
+ grpc_completion_queue* cq, google::protobuf::Message* request,
+ google::protobuf::Message* result);
~StreamContext();
// Start the stream, if there is a final write following immediately, set
// buffered so that the messages can be sent in batch.
@@ -66,37 +64,31 @@ class StreamContext : public StreamContextInterface {
const Status& Wait() override;
void FinishStream(const Status& status, bool send) override;
- const google::protobuf::Message* request() override { return request_; }
+ google::protobuf::Message* request() override { return request_; }
google::protobuf::Message* response() override { return result_; }
private:
- void PollingLoop();
- bool BlockingStart();
+ // Unique tags for plucking events from the c layer. this pointer is casted
+ // to char* to create single byte step between tags. It implicitly relies on
+ // that StreamContext is large enough to contain all the pointers.
+ void* finished_tag() { return reinterpret_cast<char*>(this); }
+ void* read_tag() { return reinterpret_cast<char*>(this) + 1; }
+ void* write_tag() { return reinterpret_cast<char*>(this) + 2; }
+ void* halfclose_tag() { return reinterpret_cast<char*>(this) + 3; }
+ void* invoke_tag() { return reinterpret_cast<char*>(this) + 4; }
+ void* client_metadata_read_tag() { return reinterpret_cast<char*>(this) + 5; }
+ grpc_call* call() { return call_; }
+ grpc_completion_queue* cq() { return cq_; }
+
bool is_client_;
const RpcMethod* method_; // not owned
- ClientContext* context_; // now owned
- const google::protobuf::Message* request_; // not owned
- google::protobuf::Message* result_; // not owned
+ grpc_call* call_; // not owned
+ grpc_completion_queue* cq_; // not owned
+ google::protobuf::Message* request_; // first request, not owned
+ google::protobuf::Message* result_; // last response, not owned
- std::thread cq_poller_;
- std::mutex mu_;
- std::condition_variable invoke_cv_;
- std::condition_variable read_cv_;
- std::condition_variable write_cv_;
- std::condition_variable finish_cv_;
- grpc_event* invoke_ev_;
- // TODO(yangg) make these two into queues to support concurrent reads and
- // writes
- grpc_event* read_ev_;
- grpc_event* write_ev_;
- bool reading_;
- bool writing_;
- bool got_read_;
- bool got_write_;
bool peer_halfclosed_;
bool self_halfclosed_;
- bool stream_finished_;
- bool waiting_;
Status final_status_;
};
diff --git a/src/ruby/README.md b/src/ruby/README.md
index 8377866344..3a5c50819b 100755
--- a/src/ruby/README.md
+++ b/src/ruby/README.md
@@ -52,11 +52,25 @@ $ ./configure --prefix=/usr
$ make
$ sudo make install
+Install an update to OpenSSL with ALPN support
+
+$ wget https://www.openssl.org/source/openssl-1.0.2-beta3.tar.gz
+$ tar -zxvf openssl-1.0.2-beta3.tar.gz
+$ cd openssl-1.0.2-beta3
+$ ./config shared
+$ make
+$ sudo make install
+
Install RVM
+$ # the -with-openssl-dir ensures that ruby uses the updated version of SSL
+$ command curl -sSL https://rvm.io/mpapis.asc | gpg --import -
$ \curl -sSL https://get.rvm.io | bash -s stable --ruby
+$
$ # follow the instructions to ensure that your're using the latest stable version of Ruby
+$ # and that the rvm command is installed
$
+$ rvm reinstall 2.1.5 --with-openssl-dir=/usr/local/ssl
$ gem install bundler # install bundler, the standard ruby package manager
Install the patched beefcake, and update the Gemfile to reference
@@ -90,4 +104,3 @@ $ # update the Gemfile, modify the line beginning # gem 'beefcake' to refer to
$ # the patched beefcake dir
$
$ bundle install
-
diff --git a/src/ruby/Rakefile b/src/ruby/Rakefile
index 11b3d04f3f..0a0fbcecca 100755
--- a/src/ruby/Rakefile
+++ b/src/ruby/Rakefile
@@ -9,7 +9,10 @@ end
SPEC_SUITES = [
{ :id => :wrapper, :title => 'wrapper layer', :files => %w(spec/*.rb) },
- { :id => :idiomatic, :title => 'idiomatic layer', :dir => %w(spec/generic) }
+ { :id => :idiomatic, :title => 'idiomatic layer', :dir => %w(spec/generic),
+ :tag => '~bidi' },
+ { :id => :bidi, :title => 'bidi tests', :dir => %w(spec/generic),
+ :tag => 'bidi' }
]
desc "Run all RSpec tests"
@@ -28,11 +31,16 @@ namespace :spec do
end
t.pattern = spec_files
+
+ if suite[:tag]
+ t.rspec_opts = "--tag #{suite[:tag]}"
+ end
end
end
end
end
-desc "Run tests"
-task :default => [ "spec:suite:wrapper", "spec:suite:idiomatic"]
-task :spec => :compile
+task :default => "spec:suite:idiomatic" # this should be spec:suite:bidi
+task "spec:suite:wrapper" => :compile
+task "spec:suite:idiomatic" => "spec:suite:wrapper"
+task "spec:suite:bidi" => "spec:suite:idiomatic"
diff --git a/src/ruby/bin/math.pb.rb b/src/ruby/bin/math.pb.rb
index 9278a84382..f6976be568 100755
--- a/src/ruby/bin/math.pb.rb
+++ b/src/ruby/bin/math.pb.rb
@@ -51,7 +51,7 @@ module Math
class Service
include GRPC::GenericService
- self.marshal_instance_method = :encode
+ self.marshal_class_method = :encode
self.unmarshal_class_method = :decode
rpc :Div, DivArgs, DivReply
diff --git a/src/ruby/bin/math_client.rb b/src/ruby/bin/math_client.rb
index f8cf8580e8..947e86adf9 100644
--- a/src/ruby/bin/math_client.rb
+++ b/src/ruby/bin/math_client.rb
@@ -43,13 +43,16 @@ require 'grpc'
require 'grpc/generic/client_stub'
require 'grpc/generic/service'
require 'math.pb'
+require 'optparse'
+
+include GRPC::Core::TimeConsts
def do_div(stub)
logger.info('request_response')
logger.info('----------------')
req = Math::DivArgs.new(:dividend => 7, :divisor => 3)
logger.info("div(7/3): req=#{req.inspect}")
- resp = stub.div(req, deadline=GRPC::TimeConsts::INFINITE_FUTURE)
+ resp = stub.div(req, deadline=INFINITE_FUTURE)
logger.info("Answer: #{resp.inspect}")
logger.info('----------------')
end
@@ -70,7 +73,7 @@ def do_fib(stub)
logger.info('----------------')
req = Math::FibArgs.new(:limit => 11)
logger.info("fib(11): req=#{req.inspect}")
- resp = stub.fib(req, deadline=GRPC::TimeConsts::INFINITE_FUTURE)
+ resp = stub.fib(req, deadline=INFINITE_FUTURE)
resp.each do |r|
logger.info("Answer: #{r.inspect}")
end
@@ -92,15 +95,51 @@ def do_div_many(stub)
logger.info('----------------')
end
+def load_test_certs
+ this_dir = File.expand_path(File.dirname(__FILE__))
+ data_dir = File.join(File.dirname(this_dir), 'spec/testdata')
+ files = ['ca.pem', 'server1.key', 'server1.pem']
+ files.map { |f| File.open(File.join(data_dir, f)).read }
+end
+
+def test_creds
+ certs = load_test_certs
+ creds = GRPC::Core::Credentials.new(certs[0])
+end
def main
- host_port = 'localhost:7070'
- if ARGV.size > 0
- host_port = ARGV[0]
- end
+ options = {
+ 'host' => 'localhost:7071',
+ 'secure' => false
+ }
+ OptionParser.new do |opts|
+ opts.banner = 'Usage: [--host|-h <hostname>:<port>] [--secure|-s]'
+ opts.on('-h', '--host', '<hostname>:<port>') do |v|
+ options['host'] = v
+ end
+ opts.on('-s', '--secure', 'access using test creds') do |v|
+ options['secure'] = true
+ end
+ end.parse!
+
# The Math::Math:: module occurs because the service has the same name as its
# package. That practice should be avoided by defining real services.
- stub = Math::Math::Stub.new(host_port)
+
+ p options
+ if options['secure']
+ stub_opts = {
+ :creds => test_creds,
+ GRPC::Core::Channel::SSL_TARGET => 'foo.test.google.com',
+ }
+ p stub_opts
+ p options['host']
+ stub = Math::Math::Stub.new(options['host'], **stub_opts)
+ logger.info("... connecting securely on #{options['host']}")
+ else
+ stub = Math::Math::Stub.new(options['host'])
+ logger.info("... connecting insecurely on #{options['host']}")
+ end
+
do_div(stub)
do_sum(stub)
do_fib(stub)
diff --git a/src/ruby/bin/math_server.rb b/src/ruby/bin/math_server.rb
index 72a1f6b398..d21ae7ee27 100644
--- a/src/ruby/bin/math_server.rb
+++ b/src/ruby/bin/math_server.rb
@@ -44,6 +44,7 @@ require 'grpc'
require 'grpc/generic/service'
require 'grpc/generic/rpc_server'
require 'math.pb'
+require 'optparse'
# Holds state for a fibonacci series
class Fibber
@@ -151,14 +152,43 @@ class Calculator < Math::Math::Service
end
+def load_test_certs
+ this_dir = File.expand_path(File.dirname(__FILE__))
+ data_dir = File.join(File.dirname(this_dir), 'spec/testdata')
+ files = ['ca.pem', 'server1.key', 'server1.pem']
+ files.map { |f| File.open(File.join(data_dir, f)).read }
+end
+
+def test_server_creds
+ certs = load_test_certs
+ server_creds = GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
+end
+
def main
- host_port = 'localhost:7070'
- if ARGV.size > 0
- host_port = ARGV[0]
+ options = {
+ 'host' => 'localhost:7071',
+ 'secure' => false
+ }
+ OptionParser.new do |opts|
+ opts.banner = 'Usage: [--host|-h <hostname>:<port>] [--secure|-s]'
+ opts.on('-h', '--host', '<hostname>:<port>') do |v|
+ options['host'] = v
+ end
+ opts.on('-s', '--secure', 'access using test creds') do |v|
+ options['secure'] = true
+ end
+ end.parse!
+
+ if options['secure']
+ s = GRPC::RpcServer.new(creds: test_server_creds)
+ s.add_http2_port(options['host'], true)
+ logger.info("... running securely on #{options['host']}")
+ else
+ s = GRPC::RpcServer.new
+ s.add_http2_port(options['host'])
+ logger.info("... running insecurely on #{options['host']}")
end
- s = GRPC::RpcServer.new()
- s.add_http2_port(host_port)
s.handle(Calculator)
s.run
end
diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb
index 06bfad9e6c..9a181f5859 100644
--- a/src/ruby/ext/grpc/extconf.rb
+++ b/src/ruby/ext/grpc/extconf.rb
@@ -80,7 +80,9 @@ $CFLAGS << ' -Wno-return-type '
$CFLAGS << ' -Wall '
$CFLAGS << ' -pedantic '
-$LDFLAGS << ' -lgrpc -lgpr -levent -levent_pthreads -levent_core'
+$LDFLAGS << ' -lgrpc -lgpr -levent -levent_pthreads -levent_core '
+$LDFLAGS << ' -lssl -lcrypto '
+$DLDFLAGS << ' -Wl,-rpath,/usr/local/ssl/lib '
# crash('need grpc lib') unless have_library('grpc', 'grpc_channel_destroy')
#
diff --git a/src/ruby/ext/grpc/rb_byte_buffer.c b/src/ruby/ext/grpc/rb_byte_buffer.c
index a520ca44dd..b75e853d6b 100644
--- a/src/ruby/ext/grpc/rb_byte_buffer.c
+++ b/src/ruby/ext/grpc/rb_byte_buffer.c
@@ -205,7 +205,7 @@ static VALUE grpc_rb_byte_buffer_init(VALUE self, VALUE src) {
VALUE rb_cByteBuffer = Qnil;
void Init_google_rpc_byte_buffer() {
- rb_cByteBuffer = rb_define_class_under(rb_mGoogleRPC, "ByteBuffer",
+ rb_cByteBuffer = rb_define_class_under(rb_mGoogleRpcCore, "ByteBuffer",
rb_cObject);
/* Allocates an object managed by the ruby runtime */
diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c
index 07f70e041a..5ea66e6e21 100644
--- a/src/ruby/ext/grpc/rb_call.c
+++ b/src/ruby/ext/grpc/rb_call.c
@@ -429,7 +429,7 @@ VALUE rb_eCallError = Qnil;
void Init_google_rpc_error_codes() {
/* Constants representing the error codes of grpc_call_error in grpc.h */
- VALUE rb_RpcErrors = rb_define_module_under(rb_mGoogleRPC, "RpcErrors");
+ VALUE rb_RpcErrors = rb_define_module_under(rb_mGoogleRpcCore, "RpcErrors");
rb_define_const(rb_RpcErrors, "OK", UINT2NUM(GRPC_CALL_OK));
rb_define_const(rb_RpcErrors, "ERROR", UINT2NUM(GRPC_CALL_ERROR));
rb_define_const(rb_RpcErrors, "NOT_ON_SERVER",
@@ -475,9 +475,9 @@ void Init_google_rpc_error_codes() {
void Init_google_rpc_call() {
/* CallError inherits from Exception to signal that it is non-recoverable */
- rb_eCallError = rb_define_class_under(rb_mGoogleRPC, "CallError",
+ rb_eCallError = rb_define_class_under(rb_mGoogleRpcCore, "CallError",
rb_eException);
- rb_cCall = rb_define_class_under(rb_mGoogleRPC, "Call", rb_cObject);
+ rb_cCall = rb_define_class_under(rb_mGoogleRpcCore, "Call", rb_cObject);
/* Prevent allocation or inialization of the Call class */
rb_define_alloc_func(rb_cCall, grpc_rb_cannot_alloc);
diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c
index f4c09a392a..dbced1c7fd 100644
--- a/src/ruby/ext/grpc/rb_channel.c
+++ b/src/ruby/ext/grpc/rb_channel.c
@@ -36,10 +36,12 @@
#include <ruby.h>
#include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
#include "rb_grpc.h"
#include "rb_call.h"
#include "rb_channel_args.h"
#include "rb_completion_queue.h"
+#include "rb_credentials.h"
#include "rb_server.h"
/* id_channel is the name of the hidden ivar that preserves a reference to the
@@ -104,18 +106,36 @@ static VALUE grpc_rb_channel_alloc(VALUE cls) {
wrapper);
}
-/* Initializes channel instances */
-static VALUE grpc_rb_channel_init(VALUE self, VALUE target,
- VALUE channel_args) {
+/*
+ call-seq:
+ insecure_channel = Channel:new("myhost:8080", {'arg1': 'value1'})
+ creds = ...
+ secure_channel = Channel:new("myhost:443", {'arg1': 'value1'}, creds)
+
+ Creates channel instances. */
+static VALUE grpc_rb_channel_init(int argc, VALUE *argv, VALUE self) {
+ VALUE channel_args = Qnil;
+ VALUE credentials = Qnil;
+ VALUE target = Qnil;
grpc_rb_channel *wrapper = NULL;
+ grpc_credentials *creds = NULL;
grpc_channel *ch = NULL;
- char *target_chars = StringValueCStr(target);
+ char *target_chars = NULL;
grpc_channel_args args;
MEMZERO(&args, grpc_channel_args, 1);
+ /* "21" == 2 mandatory args, 1 (credentials) is optional */
+ rb_scan_args(argc, argv, "21", &target, &channel_args, &credentials);
+
Data_Get_Struct(self, grpc_rb_channel, wrapper);
+ target_chars = StringValueCStr(target);
grpc_rb_hash_convert_to_channel_args(channel_args, &args);
- ch = grpc_channel_create(target_chars, &args);
+ if (credentials == Qnil) {
+ ch = grpc_channel_create(target_chars, &args);
+ } else {
+ creds = grpc_rb_get_wrapped_credentials(credentials);
+ ch = grpc_secure_channel_create(creds, target_chars, &args);
+ }
if (args.args != NULL) {
xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */
}
@@ -208,13 +228,13 @@ VALUE rb_cChannel = Qnil;
void Init_google_rpc_channel() {
rb_cChannelArgs = rb_define_class("TmpChannelArgs", rb_cObject);
- rb_cChannel = rb_define_class_under(rb_mGoogleRPC, "Channel", rb_cObject);
+ rb_cChannel = rb_define_class_under(rb_mGoogleRpcCore, "Channel", rb_cObject);
/* Allocates an object managed by the ruby runtime */
rb_define_alloc_func(rb_cChannel, grpc_rb_channel_alloc);
/* Provides a ruby constructor and support for dup/clone. */
- rb_define_method(rb_cChannel, "initialize", grpc_rb_channel_init, 2);
+ rb_define_method(rb_cChannel, "initialize", grpc_rb_channel_init, -1);
rb_define_method(rb_cChannel, "initialize_copy", grpc_rb_channel_init_copy,
1);
@@ -225,6 +245,8 @@ void Init_google_rpc_channel() {
id_channel = rb_intern("__channel");
id_target = rb_intern("__target");
+ rb_define_const(rb_cChannel, "SSL_TARGET",
+ ID2SYM(rb_intern(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)));
}
/* Gets the wrapped channel from the ruby wrapper */
diff --git a/src/ruby/ext/grpc/rb_completion_queue.c b/src/ruby/ext/grpc/rb_completion_queue.c
index 62d045e971..dfde44218b 100644
--- a/src/ruby/ext/grpc/rb_completion_queue.c
+++ b/src/ruby/ext/grpc/rb_completion_queue.c
@@ -168,7 +168,7 @@ static VALUE grpc_rb_completion_queue_pluck(VALUE self, VALUE tag,
VALUE rb_cCompletionQueue = Qnil;
void Init_google_rpc_completion_queue() {
- rb_cCompletionQueue = rb_define_class_under(rb_mGoogleRPC,
+ rb_cCompletionQueue = rb_define_class_under(rb_mGoogleRpcCore,
"CompletionQueue",
rb_cObject);
diff --git a/src/ruby/ext/grpc/rb_credentials.c b/src/ruby/ext/grpc/rb_credentials.c
new file mode 100644
index 0000000000..658adacc06
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_credentials.c
@@ -0,0 +1,301 @@
+/*
+ *
+ * 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 "rb_credentials.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
+
+#include "rb_grpc.h"
+
+
+/* grpc_rb_credentials wraps a grpc_credentials. It provides a
+ * peer ruby object, 'mark' to minimize copying when a credential is
+ * created from ruby. */
+typedef struct grpc_rb_credentials {
+ /* Holder of ruby objects involved in constructing the credentials */
+ VALUE mark;
+
+ /* The actual credentials */
+ grpc_credentials *wrapped;
+} grpc_rb_credentials;
+
+/* Destroys the credentials instances. */
+static void grpc_rb_credentials_free(void *p) {
+ grpc_rb_credentials *wrapper = NULL;
+ if (p == NULL) {
+ return;
+ };
+ wrapper = (grpc_rb_credentials *)p;
+
+ /* Delete the wrapped object if the mark object is Qnil, which indicates that
+ * no other object is the actual owner. */
+ if (wrapper->wrapped != NULL && wrapper->mark == Qnil) {
+ grpc_credentials_release(wrapper->wrapped);
+ wrapper->wrapped = NULL;
+ }
+
+ xfree(p);
+}
+
+/* Protects the mark object from GC */
+static void grpc_rb_credentials_mark(void *p) {
+ grpc_rb_credentials *wrapper = NULL;
+ if (p == NULL) {
+ return;
+ }
+ wrapper = (grpc_rb_credentials *)p;
+
+ /* If it's not already cleaned up, mark the mark object */
+ if (wrapper->mark != Qnil) {
+ rb_gc_mark(wrapper->mark);
+ }
+}
+
+/* Allocates Credential instances.
+
+ Provides safe initial defaults for the instance fields. */
+static VALUE grpc_rb_credentials_alloc(VALUE cls) {
+ grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials);
+ wrapper->wrapped = NULL;
+ wrapper->mark = Qnil;
+ return Data_Wrap_Struct(cls, grpc_rb_credentials_mark,
+ grpc_rb_credentials_free,
+ wrapper);
+}
+
+/* Clones Credentials instances.
+
+ Gives Credentials a consistent implementation of Ruby's object copy/dup
+ protocol. */
+static VALUE grpc_rb_credentials_init_copy(VALUE copy, VALUE orig) {
+ grpc_rb_credentials *orig_cred = NULL;
+ grpc_rb_credentials *copy_cred = NULL;
+
+ if (copy == orig) {
+ return copy;
+ }
+
+ /* Raise an error if orig is not a credentials object or a subclass. */
+ if (TYPE(orig) != T_DATA ||
+ RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_credentials_free) {
+ rb_raise(rb_eTypeError, "not a %s",
+ rb_obj_classname(rb_cCredentials));
+ }
+
+ Data_Get_Struct(orig, grpc_rb_credentials, orig_cred);
+ Data_Get_Struct(copy, grpc_rb_credentials, copy_cred);
+
+ /* use ruby's MEMCPY to make a byte-for-byte copy of the credentials
+ * wrapper object. */
+ MEMCPY(copy_cred, orig_cred, grpc_rb_credentials, 1);
+ return copy;
+}
+
+/*
+ call-seq:
+ creds = Credentials.default()
+
+ Creates the default credential instances. */
+static VALUE grpc_rb_default_credentials_create(VALUE cls) {
+ grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials);
+ wrapper->wrapped = grpc_default_credentials_create();
+ if (wrapper->wrapped == NULL) {
+ rb_raise(rb_eRuntimeError,
+ "could not create default credentials, not sure why");
+ return Qnil;
+ }
+
+ wrapper->mark = Qnil;
+ return Data_Wrap_Struct(cls, grpc_rb_credentials_mark,
+ grpc_rb_credentials_free, wrapper);
+}
+
+/*
+ call-seq:
+ creds = Credentials.compute_engine()
+
+ Creates the default credential instances. */
+static VALUE grpc_rb_compute_engine_credentials_create(VALUE cls) {
+ grpc_rb_credentials *wrapper = ALLOC(grpc_rb_credentials);
+ wrapper->wrapped = grpc_compute_engine_credentials_create();
+ if (wrapper->wrapped == NULL) {
+ rb_raise(rb_eRuntimeError,
+ "could not create composite engine credentials, not sure why");
+ return Qnil;
+ }
+
+ wrapper->mark = Qnil;
+ return Data_Wrap_Struct(cls, grpc_rb_credentials_mark,
+ grpc_rb_credentials_free, wrapper);
+}
+
+/*
+ call-seq:
+ creds1 = ...
+ creds2 = ...
+ creds3 = creds1.add(creds2)
+
+ Creates the default credential instances. */
+static VALUE grpc_rb_composite_credentials_create(VALUE self, VALUE other) {
+ grpc_rb_credentials *self_wrapper = NULL;
+ grpc_rb_credentials *other_wrapper = NULL;
+ grpc_rb_credentials *wrapper = NULL;
+
+ Data_Get_Struct(self, grpc_rb_credentials, self_wrapper);
+ Data_Get_Struct(other, grpc_rb_credentials, other_wrapper);
+ wrapper = ALLOC(grpc_rb_credentials);
+ wrapper->wrapped = grpc_composite_credentials_create(self_wrapper->wrapped,
+ other_wrapper->wrapped);
+ if (wrapper->wrapped == NULL) {
+ rb_raise(rb_eRuntimeError,
+ "could not create composite credentials, not sure why");
+ return Qnil;
+ }
+
+ wrapper->mark = Qnil;
+ return Data_Wrap_Struct(rb_cCredentials, grpc_rb_credentials_mark,
+ grpc_rb_credentials_free, wrapper);
+}
+
+/* The attribute used on the mark object to hold the pem_root_certs. */
+static ID id_pem_root_certs;
+
+/* The attribute used on the mark object to hold the pem_private_key. */
+static ID id_pem_private_key;
+
+/* The attribute used on the mark object to hold the pem_private_key. */
+static ID id_pem_cert_chain;
+
+/*
+ call-seq:
+ creds1 = Credentials.new(pem_root_certs)
+ ...
+ creds2 = Credentials.new(pem_root_certs, pem_private_key,
+ pem_cert_chain)
+
+ pem_root_certs: (required) PEM encoding of the server root certificate
+ pem_private_key: (optional) PEM encoding of the client's private key
+ pem_cert_chain: (optional) PEM encoding of the client's cert chain
+
+ Initializes Credential instances. */
+static VALUE grpc_rb_credentials_init(int argc, VALUE *argv, VALUE self) {
+ VALUE pem_root_certs = Qnil;
+ VALUE pem_private_key = Qnil;
+ VALUE pem_cert_chain = Qnil;
+ grpc_rb_credentials *wrapper = NULL;
+ grpc_credentials *creds = NULL;
+ /* "12" == 1 mandatory arg, 2 (credentials) is optional */
+ rb_scan_args(argc, argv, "12", &pem_root_certs, &pem_private_key,
+ &pem_cert_chain);
+
+ Data_Get_Struct(self, grpc_rb_credentials, wrapper);
+ if (pem_root_certs == Qnil) {
+ rb_raise(rb_eRuntimeError,
+ "could not create a credential: nil pem_root_certs");
+ return Qnil;
+ }
+ if (pem_private_key == Qnil && pem_cert_chain == Qnil) {
+ creds = grpc_ssl_credentials_create(RSTRING_PTR(pem_root_certs),
+ RSTRING_LEN(pem_root_certs), NULL, 0,
+ NULL, 0);
+ } else if (pem_cert_chain == Qnil) {
+ creds = grpc_ssl_credentials_create(
+ RSTRING_PTR(pem_root_certs), RSTRING_LEN(pem_root_certs),
+ RSTRING_PTR(pem_private_key), RSTRING_LEN(pem_private_key),
+ RSTRING_PTR(pem_cert_chain), RSTRING_LEN(pem_cert_chain));
+ } else if (pem_private_key == Qnil) {
+ creds = grpc_ssl_credentials_create(
+ RSTRING_PTR(pem_root_certs), RSTRING_LEN(pem_root_certs),
+ NULL, 0,
+ RSTRING_PTR(pem_cert_chain), RSTRING_LEN(pem_cert_chain));
+ } else {
+ creds = grpc_ssl_credentials_create(
+ RSTRING_PTR(pem_root_certs), RSTRING_LEN(pem_root_certs),
+ RSTRING_PTR(pem_private_key), RSTRING_LEN(pem_private_key),
+ NULL, 0);
+ }
+ if (creds == NULL) {
+ rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why");
+ return Qnil;
+ }
+ wrapper->wrapped = creds;
+
+ /* Add the input objects as hidden fields to preserve them. */
+ rb_ivar_set(self, id_pem_cert_chain, pem_cert_chain);
+ rb_ivar_set(self, id_pem_private_key, pem_private_key);
+ rb_ivar_set(self, id_pem_root_certs, pem_root_certs);
+
+ return self;
+}
+
+/* rb_cCredentials is the ruby class that proxies grpc_credentials. */
+VALUE rb_cCredentials = Qnil;
+
+void Init_google_rpc_credentials() {
+ rb_cCredentials = rb_define_class_under(rb_mGoogleRpcCore, "Credentials",
+ rb_cObject);
+
+ /* Allocates an object managed by the ruby runtime */
+ rb_define_alloc_func(rb_cCredentials, grpc_rb_credentials_alloc);
+
+ /* Provides a ruby constructor and support for dup/clone. */
+ rb_define_method(rb_cCredentials, "initialize",
+ grpc_rb_credentials_init, -1);
+ rb_define_method(rb_cCredentials, "initialize_copy",
+ grpc_rb_credentials_init_copy, 1);
+
+ /* Provide static funcs that create new special instances. */
+ rb_define_singleton_method(rb_cCredentials, "default",
+ grpc_rb_default_credentials_create, 0);
+
+ rb_define_singleton_method(rb_cCredentials, "compute_engine",
+ grpc_rb_compute_engine_credentials_create, 0);
+
+ /* Provide other methods. */
+ rb_define_method(rb_cCredentials, "compose",
+ grpc_rb_composite_credentials_create, 1);
+
+ id_pem_cert_chain = rb_intern("__pem_cert_chain");
+ id_pem_private_key = rb_intern("__pem_private_key");
+ id_pem_root_certs = rb_intern("__pem_root_certs");
+}
+
+/* Gets the wrapped grpc_credentials from the ruby wrapper */
+grpc_credentials* grpc_rb_get_wrapped_credentials(VALUE v) {
+ grpc_rb_credentials *wrapper = NULL;
+ Data_Get_Struct(v, grpc_rb_credentials, wrapper);
+ return wrapper->wrapped;
+}
diff --git a/src/ruby/ext/grpc/rb_credentials.h b/src/ruby/ext/grpc/rb_credentials.h
new file mode 100644
index 0000000000..d18c88ac34
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_credentials.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * 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_RB_CREDENTIALS_H_
+#define GRPC_RB_CREDENTIALS_H_
+
+#include <ruby.h>
+#include <grpc/grpc_security.h>
+
+/* rb_cCredentials is the ruby class whose instances proxy
+ grpc_credentials. */
+extern VALUE rb_cCredentials;
+
+/* Initializes the ruby Credentials class. */
+void Init_google_rpc_credentials();
+
+/* Gets the wrapped credentials from the ruby wrapper */
+grpc_credentials* grpc_rb_get_wrapped_credentials(VALUE v);
+
+#endif /* GRPC_RB_CREDENTIALS_H_ */
diff --git a/src/ruby/ext/grpc/rb_event.c b/src/ruby/ext/grpc/rb_event.c
index 6f542f9eba..6708ea397c 100644
--- a/src/ruby/ext/grpc/rb_event.c
+++ b/src/ruby/ext/grpc/rb_event.c
@@ -245,9 +245,9 @@ VALUE rb_cEvent = Qnil;
VALUE rb_eEventError = Qnil;
void Init_google_rpc_event() {
- rb_eEventError = rb_define_class_under(rb_mGoogleRPC, "EventError",
+ rb_eEventError = rb_define_class_under(rb_mGoogleRpcCore, "EventError",
rb_eStandardError);
- rb_cEvent = rb_define_class_under(rb_mGoogleRPC, "Event", rb_cObject);
+ rb_cEvent = rb_define_class_under(rb_mGoogleRpcCore, "Event", rb_cObject);
rb_sNewServerRpc = rb_struct_define("NewServerRpc", "method", "host",
"deadline", "metadata", NULL);
@@ -263,7 +263,7 @@ void Init_google_rpc_event() {
rb_define_method(rb_cEvent, "type", grpc_rb_event_type, 0);
/* Constants representing the completion types */
- rb_mCompletionType = rb_define_module_under(rb_mGoogleRPC, "CompletionType");
+ rb_mCompletionType = rb_define_module_under(rb_mGoogleRpcCore, "CompletionType");
rb_define_const(rb_mCompletionType, "QUEUE_SHUTDOWN",
INT2NUM(GRPC_QUEUE_SHUTDOWN));
rb_define_const(rb_mCompletionType, "READ", INT2NUM(GRPC_READ));
diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c
index 5cc45cf743..9c54a05c45 100644
--- a/src/ruby/ext/grpc/rb_grpc.c
+++ b/src/ruby/ext/grpc/rb_grpc.c
@@ -46,6 +46,8 @@
#include "rb_event.h"
#include "rb_metadata.h"
#include "rb_server.h"
+#include "rb_credentials.h"
+#include "rb_server_credentials.h"
#include "rb_status.h"
/* Define common vars and funcs declared in rb.h */
@@ -184,8 +186,10 @@ VALUE grpc_rb_time_val_to_s(VALUE self) {
/* Adds a module with constants that map to gpr's static timeval structs. */
void Init_google_time_consts() {
- VALUE rb_mTimeConsts = rb_define_module_under(rb_mGoogleRPC, "TimeConsts");
- rb_cTimeVal = rb_define_class_under(rb_mGoogleRPC, "TimeSpec", rb_cObject);
+ VALUE rb_mTimeConsts = rb_define_module_under(rb_mGoogleRpcCore,
+ "TimeConsts");
+ rb_cTimeVal = rb_define_class_under(rb_mGoogleRpcCore, "TimeSpec",
+ rb_cObject);
rb_define_const(rb_mTimeConsts, "ZERO",
Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED,
GC_DONT_FREE, (void *)&gpr_time_0));
@@ -212,19 +216,23 @@ void grpc_rb_shutdown(void *vm) {
/* Initialize the Google RPC module. */
VALUE rb_mGoogle = Qnil;
VALUE rb_mGoogleRPC = Qnil;
+VALUE rb_mGoogleRpcCore = Qnil;
void Init_grpc() {
grpc_init();
ruby_vm_at_exit(grpc_rb_shutdown);
rb_mGoogle = rb_define_module("Google");
rb_mGoogleRPC = rb_define_module_under(rb_mGoogle, "RPC");
+ rb_mGoogleRpcCore = rb_define_module_under(rb_mGoogleRPC, "Core");
Init_google_rpc_byte_buffer();
Init_google_rpc_event();
Init_google_rpc_channel();
Init_google_rpc_completion_queue();
Init_google_rpc_call();
+ Init_google_rpc_credentials();
Init_google_rpc_metadata();
Init_google_rpc_server();
+ Init_google_rpc_server_credentials();
Init_google_rpc_status();
Init_google_time_consts();
}
diff --git a/src/ruby/ext/grpc/rb_grpc.h b/src/ruby/ext/grpc/rb_grpc.h
index fd43c3795f..68f8a06c30 100644
--- a/src/ruby/ext/grpc/rb_grpc.h
+++ b/src/ruby/ext/grpc/rb_grpc.h
@@ -41,8 +41,8 @@
/* rb_mGoogle is the top-level Google module. */
extern VALUE rb_mGoogle;
-/* rb_mGoogleRPC is the module containing all the ruby wrapper GRPC classes. */
-extern VALUE rb_mGoogleRPC;
+/* rb_mGoogleRpcCore is the module containing the ruby wrapper GRPC classes. */
+extern VALUE rb_mGoogleRpcCore;
/* Class used to wrap timeval structs. */
extern VALUE rb_cTimeVal;
diff --git a/src/ruby/ext/grpc/rb_metadata.c b/src/ruby/ext/grpc/rb_metadata.c
index 13d515a929..733a53a7e9 100644
--- a/src/ruby/ext/grpc/rb_metadata.c
+++ b/src/ruby/ext/grpc/rb_metadata.c
@@ -189,7 +189,7 @@ static VALUE grpc_rb_metadata_value(VALUE self) {
/* rb_cMetadata is the Metadata class whose instances proxy grpc_metadata. */
VALUE rb_cMetadata = Qnil;
void Init_google_rpc_metadata() {
- rb_cMetadata = rb_define_class_under(rb_mGoogleRPC, "Metadata", rb_cObject);
+ rb_cMetadata = rb_define_class_under(rb_mGoogleRpcCore, "Metadata", rb_cObject);
/* Allocates an object managed by the ruby runtime */
rb_define_alloc_func(rb_cMetadata, grpc_rb_metadata_alloc);
diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c
index f4230bd471..84fba5be4f 100644
--- a/src/ruby/ext/grpc/rb_server.c
+++ b/src/ruby/ext/grpc/rb_server.c
@@ -36,16 +36,18 @@
#include <ruby.h>
#include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
#include "rb_call.h"
#include "rb_channel_args.h"
#include "rb_completion_queue.h"
+#include "rb_server_credentials.h"
#include "rb_grpc.h"
/* rb_cServer is the ruby class that proxies grpc_server. */
VALUE rb_cServer = Qnil;
/* grpc_rb_server wraps a grpc_server. It provides a peer ruby object,
- * 'mark' to minimize copying when a server is created from ruby. */
+ 'mark' to minimize copying when a server is created from ruby. */
typedef struct grpc_rb_server {
/* Holder of ruby objects involved in constructing the server */
VALUE mark;
@@ -62,7 +64,7 @@ static void grpc_rb_server_free(void *p) {
svr = (grpc_rb_server *)p;
/* Deletes the wrapped object if the mark object is Qnil, which indicates
- * that no other object is the actual owner. */
+ that no other object is the actual owner. */
if (svr->wrapped != NULL && svr->mark == Qnil) {
grpc_server_shutdown(svr->wrapped);
grpc_server_destroy(svr->wrapped);
@@ -92,17 +94,39 @@ static VALUE grpc_rb_server_alloc(VALUE cls) {
wrapper);
}
-/* Initializes Server instances. */
-static VALUE grpc_rb_server_init(VALUE self, VALUE cqueue, VALUE channel_args) {
- grpc_completion_queue *cq = grpc_rb_get_wrapped_completion_queue(cqueue);
+/*
+ call-seq:
+ cq = CompletionQueue.new
+ insecure_server = Server.new(cq, {'arg1': 'value1'})
+ server_creds = ...
+ secure_server = Server.new(cq, {'arg1': 'value1'}, server_creds)
+
+ Initializes server instances. */
+static VALUE grpc_rb_server_init(int argc, VALUE *argv, VALUE self) {
+ VALUE cqueue = Qnil;
+ VALUE credentials = Qnil;
+ VALUE channel_args = Qnil;
+ grpc_completion_queue *cq = NULL;
+ grpc_server_credentials *creds = NULL;
grpc_rb_server *wrapper = NULL;
grpc_server *srv = NULL;
grpc_channel_args args;
MEMZERO(&args, grpc_channel_args, 1);
+ /* "21" == 2 mandatory args, 1 (credentials) is optional */
+ rb_scan_args(argc, argv, "21", &cqueue, &channel_args, &credentials);
+ cq = grpc_rb_get_wrapped_completion_queue(cqueue);
+
Data_Get_Struct(self, grpc_rb_server, wrapper);
grpc_rb_hash_convert_to_channel_args(channel_args, &args);
srv = grpc_server_create(cq, &args);
+ if (credentials == Qnil) {
+ srv = grpc_server_create(cq, &args);
+ } else {
+ creds = grpc_rb_get_wrapped_server_credentials(credentials);
+ srv = grpc_secure_server_create(creds, cq, &args);
+ }
+
if (args.args != NULL) {
xfree(args.args); /* Allocated by grpc_rb_hash_convert_to_channel_args */
}
@@ -112,7 +136,7 @@ static VALUE grpc_rb_server_init(VALUE self, VALUE cqueue, VALUE channel_args) {
wrapper->wrapped = srv;
/* Add the cq as the server's mark object. This ensures the ruby cq can't be
- * GCed before the server */
+ GCed before the server */
wrapper->mark = cqueue;
return self;
}
@@ -139,7 +163,7 @@ static VALUE grpc_rb_server_init_copy(VALUE copy, VALUE orig) {
Data_Get_Struct(copy, grpc_rb_server, copy_srv);
/* use ruby's MEMCPY to make a byte-for-byte copy of the server wrapper
- * object. */
+ object. */
MEMCPY(copy_srv, orig_srv, grpc_rb_server, 1);
return copy;
}
@@ -183,16 +207,44 @@ static VALUE grpc_rb_server_destroy(VALUE self) {
return Qnil;
}
-static VALUE grpc_rb_server_add_http2_port(VALUE self, VALUE port) {
+/*
+ call-seq:
+ // insecure port
+ insecure_server = Server.new(cq, {'arg1': 'value1'})
+ insecure_server.add_http2_port('mydomain:7575')
+
+ // secure port
+ server_creds = ...
+ secure_server = Server.new(cq, {'arg1': 'value1'}, creds)
+ secure_server.add_http_port('mydomain:7575', True)
+
+ Adds a http2 port to server */
+static VALUE grpc_rb_server_add_http2_port(int argc, VALUE *argv, VALUE self) {
+ VALUE port = Qnil;
+ VALUE is_secure = Qnil;
grpc_rb_server *s = NULL;
int added_ok = 0;
+
+ /* "11" == 1 mandatory args, 1 (is_secure) is optional */
+ rb_scan_args(argc, argv, "11", &port, &is_secure);
+
Data_Get_Struct(self, grpc_rb_server, s);
if (s->wrapped == NULL) {
rb_raise(rb_eRuntimeError, "closed!");
- } else {
+ return Qnil;
+ } else if (is_secure == Qnil || TYPE(is_secure) != T_TRUE) {
added_ok = grpc_server_add_http2_port(s->wrapped, StringValueCStr(port));
if (added_ok == 0) {
- rb_raise(rb_eRuntimeError, "could not add port %s to server, not sure why",
+ rb_raise(rb_eRuntimeError,
+ "could not add port %s to server, not sure why",
+ StringValueCStr(port));
+ }
+ } else if (TYPE(is_secure) != T_FALSE) {
+ added_ok = grpc_server_add_secure_http2_port(s->wrapped,
+ StringValueCStr(port));
+ if (added_ok == 0) {
+ rb_raise(rb_eRuntimeError,
+ "could not add secure port %s to server, not sure why",
StringValueCStr(port));
}
}
@@ -200,13 +252,13 @@ static VALUE grpc_rb_server_add_http2_port(VALUE self, VALUE port) {
}
void Init_google_rpc_server() {
- rb_cServer = rb_define_class_under(rb_mGoogleRPC, "Server", rb_cObject);
+ rb_cServer = rb_define_class_under(rb_mGoogleRpcCore, "Server", rb_cObject);
/* Allocates an object managed by the ruby runtime */
rb_define_alloc_func(rb_cServer, grpc_rb_server_alloc);
/* Provides a ruby constructor and support for dup/clone. */
- rb_define_method(rb_cServer, "initialize", grpc_rb_server_init, 2);
+ rb_define_method(rb_cServer, "initialize", grpc_rb_server_init, -1);
rb_define_method(rb_cServer, "initialize_copy", grpc_rb_server_init_copy, 1);
/* Add the server methods. */
@@ -215,7 +267,7 @@ void Init_google_rpc_server() {
rb_define_method(rb_cServer, "destroy", grpc_rb_server_destroy, 0);
rb_define_alias(rb_cServer, "close", "destroy");
rb_define_method(rb_cServer, "add_http2_port", grpc_rb_server_add_http2_port,
- 1);
+ -1);
}
/* Gets the wrapped server from the ruby wrapper */
diff --git a/src/ruby/ext/grpc/rb_server.h b/src/ruby/ext/grpc/rb_server.h
index 4619203d60..7fcd32c32f 100644
--- a/src/ruby/ext/grpc/rb_server.h
+++ b/src/ruby/ext/grpc/rb_server.h
@@ -31,7 +31,7 @@
*
*/
-#ifndef GRPC_RB_BYTE_BUFFER_H_
+#ifndef GRPC_RB_SERVER_H_
#define GRPC_RB_SERVER_H_
#include <ruby.h>
diff --git a/src/ruby/ext/grpc/rb_server_credentials.c b/src/ruby/ext/grpc/rb_server_credentials.c
new file mode 100644
index 0000000000..fa0d6f2ce8
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_server_credentials.c
@@ -0,0 +1,215 @@
+/*
+ *
+ * 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 "rb_server_credentials.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
+
+#include "rb_grpc.h"
+
+
+/* grpc_rb_server_credentials wraps a grpc_server_credentials. It provides a
+ peer ruby object, 'mark' to minimize copying when a server credential is
+ created from ruby. */
+typedef struct grpc_rb_server_credentials {
+ /* Holder of ruby objects involved in constructing the server credentials */
+ VALUE mark;
+ /* The actual server credentials */
+ grpc_server_credentials *wrapped;
+} grpc_rb_server_credentials;
+
+/* Destroys the server credentials instances. */
+static void grpc_rb_server_credentials_free(void *p) {
+ grpc_rb_server_credentials *wrapper = NULL;
+ if (p == NULL) {
+ return;
+ };
+ wrapper = (grpc_rb_server_credentials *)p;
+
+ /* Delete the wrapped object if the mark object is Qnil, which indicates that
+ no other object is the actual owner. */
+ if (wrapper->wrapped != NULL && wrapper->mark == Qnil) {
+ grpc_server_credentials_release(wrapper->wrapped);
+ wrapper->wrapped = NULL;
+ }
+
+ xfree(p);
+}
+
+/* Protects the mark object from GC */
+static void grpc_rb_server_credentials_mark(void *p) {
+ grpc_rb_server_credentials *wrapper = NULL;
+ if (p == NULL) {
+ return;
+ }
+ wrapper = (grpc_rb_server_credentials *)p;
+
+ /* If it's not already cleaned up, mark the mark object */
+ if (wrapper->mark != Qnil) {
+ rb_gc_mark(wrapper->mark);
+ }
+}
+
+/* Allocates ServerCredential instances.
+
+ Provides safe initial defaults for the instance fields. */
+static VALUE grpc_rb_server_credentials_alloc(VALUE cls) {
+ grpc_rb_server_credentials *wrapper = ALLOC(grpc_rb_server_credentials);
+ wrapper->wrapped = NULL;
+ wrapper->mark = Qnil;
+ return Data_Wrap_Struct(cls, grpc_rb_server_credentials_mark,
+ grpc_rb_server_credentials_free,
+ wrapper);
+}
+
+/* Clones ServerCredentials instances.
+
+ Gives ServerCredentials a consistent implementation of Ruby's object copy/dup
+ protocol. */
+static VALUE grpc_rb_server_credentials_init_copy(VALUE copy, VALUE orig) {
+ grpc_rb_server_credentials *orig_ch = NULL;
+ grpc_rb_server_credentials *copy_ch = NULL;
+
+ if (copy == orig) {
+ return copy;
+ }
+
+ /* Raise an error if orig is not a server_credentials object or a subclass. */
+ if (TYPE(orig) != T_DATA ||
+ RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_server_credentials_free) {
+ rb_raise(rb_eTypeError, "not a %s",
+ rb_obj_classname(rb_cServerCredentials));
+ }
+
+ Data_Get_Struct(orig, grpc_rb_server_credentials, orig_ch);
+ Data_Get_Struct(copy, grpc_rb_server_credentials, copy_ch);
+
+ /* use ruby's MEMCPY to make a byte-for-byte copy of the server_credentials
+ wrapper object. */
+ MEMCPY(copy_ch, orig_ch, grpc_rb_server_credentials, 1);
+ return copy;
+}
+
+
+/* The attribute used on the mark object to hold the pem_root_certs. */
+static ID id_pem_root_certs;
+
+/* The attribute used on the mark object to hold the pem_private_key. */
+static ID id_pem_private_key;
+
+/* The attribute used on the mark object to hold the pem_private_key. */
+static ID id_pem_cert_chain;
+
+/*
+ call-seq:
+ creds = ServerCredentials.new(pem_root_certs, pem_private_key,
+ pem_cert_chain)
+ creds = ServerCredentials.new(nil, pem_private_key,
+ pem_cert_chain)
+
+ pem_root_certs: (required) PEM encoding of the server root certificate
+ pem_private_key: (optional) PEM encoding of the server's private key
+ pem_cert_chain: (optional) PEM encoding of the server's cert chain
+
+ Initializes ServerCredential instances. */
+static VALUE grpc_rb_server_credentials_init(VALUE self, VALUE pem_root_certs,
+ VALUE pem_private_key,
+ VALUE pem_cert_chain) {
+ grpc_rb_server_credentials *wrapper = NULL;
+ grpc_server_credentials *creds = NULL;
+ Data_Get_Struct(self, grpc_rb_server_credentials, wrapper);
+ if (pem_cert_chain == Qnil) {
+ rb_raise(rb_eRuntimeError,
+ "could not create a server credential: nil pem_cert_chain");
+ return Qnil;
+ } else if (pem_private_key == Qnil) {
+ rb_raise(rb_eRuntimeError,
+ "could not create a server credential: nil pem_private_key");
+ return Qnil;
+ }
+ if (pem_root_certs == Qnil) {
+ creds = grpc_ssl_server_credentials_create(
+ NULL, 0, RSTRING_PTR(pem_private_key), RSTRING_LEN(pem_private_key),
+ RSTRING_PTR(pem_cert_chain), RSTRING_LEN(pem_cert_chain));
+ } else {
+ creds = grpc_ssl_server_credentials_create(
+ RSTRING_PTR(pem_root_certs), RSTRING_LEN(pem_root_certs),
+ RSTRING_PTR(pem_private_key), RSTRING_LEN(pem_private_key),
+ RSTRING_PTR(pem_cert_chain), RSTRING_LEN(pem_cert_chain));
+ }
+ if (creds == NULL) {
+ rb_raise(rb_eRuntimeError, "could not create a credentials, not sure why");
+ }
+ wrapper->wrapped = creds;
+
+ /* Add the input objects as hidden fields to preserve them. */
+ rb_ivar_set(self, id_pem_cert_chain, pem_cert_chain);
+ rb_ivar_set(self, id_pem_private_key, pem_private_key);
+ rb_ivar_set(self, id_pem_root_certs, pem_root_certs);
+
+ return self;
+}
+
+/* rb_cServerCredentials is the ruby class that proxies
+ grpc_server_credentials. */
+VALUE rb_cServerCredentials = Qnil;
+
+void Init_google_rpc_server_credentials() {
+ rb_cServerCredentials = rb_define_class_under(rb_mGoogleRpcCore,
+ "ServerCredentials",
+ rb_cObject);
+
+ /* Allocates an object managed by the ruby runtime */
+ rb_define_alloc_func(rb_cServerCredentials, grpc_rb_server_credentials_alloc);
+
+ /* Provides a ruby constructor and support for dup/clone. */
+ rb_define_method(rb_cServerCredentials, "initialize",
+ grpc_rb_server_credentials_init, 3);
+ rb_define_method(rb_cServerCredentials, "initialize_copy",
+ grpc_rb_server_credentials_init_copy,
+ 1);
+
+ id_pem_cert_chain = rb_intern("__pem_cert_chain");
+ id_pem_private_key = rb_intern("__pem_private_key");
+ id_pem_root_certs = rb_intern("__pem_root_certs");
+}
+
+/* Gets the wrapped grpc_server_credentials from the ruby wrapper */
+grpc_server_credentials* grpc_rb_get_wrapped_server_credentials(VALUE v) {
+ grpc_rb_server_credentials *wrapper = NULL;
+ Data_Get_Struct(v, grpc_rb_server_credentials, wrapper);
+ return wrapper->wrapped;
+}
diff --git a/src/ruby/ext/grpc/rb_server_credentials.h b/src/ruby/ext/grpc/rb_server_credentials.h
new file mode 100644
index 0000000000..39189511ab
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_server_credentials.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * 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_RB_SERVER_CREDENTIALS_H_
+#define GRPC_RB_SERVER_CREDENTIALS_H_
+
+#include <ruby.h>
+#include <grpc/grpc_security.h>
+
+/* rb_cServerCredentials is the ruby class whose instances proxy
+ grpc_server_credentials. */
+extern VALUE rb_cServerCredentials;
+
+/* Initializes the ruby ServerCredentials class. */
+void Init_google_rpc_server_credentials();
+
+/* Gets the wrapped server_credentials from the ruby wrapper */
+grpc_server_credentials* grpc_rb_get_wrapped_server_credentials(VALUE v);
+
+#endif /* GRPC_RB_SERVER_CREDENTIALS_H_ */
diff --git a/src/ruby/ext/grpc/rb_status.c b/src/ruby/ext/grpc/rb_status.c
index 747c47c556..4c1b6c733a 100644
--- a/src/ruby/ext/grpc/rb_status.c
+++ b/src/ruby/ext/grpc/rb_status.c
@@ -172,7 +172,8 @@ static VALUE grpc_rb_status_details(VALUE self) {
void Init_google_status_codes() {
/* Constants representing the status codes or grpc_status_code in status.h */
- VALUE rb_mStatusCodes = rb_define_module_under(rb_mGoogleRPC, "StatusCodes");
+ VALUE rb_mStatusCodes = rb_define_module_under(rb_mGoogleRpcCore,
+ "StatusCodes");
rb_define_const(rb_mStatusCodes, "OK", INT2NUM(GRPC_STATUS_OK));
rb_define_const(rb_mStatusCodes, "CANCELLED", INT2NUM(GRPC_STATUS_CANCELLED));
rb_define_const(rb_mStatusCodes, "UNKNOWN", INT2NUM(GRPC_STATUS_UNKNOWN));
@@ -207,7 +208,7 @@ VALUE rb_cStatus = Qnil;
/* Initializes the Status class. */
void Init_google_rpc_status() {
- rb_cStatus = rb_define_class_under(rb_mGoogleRPC, "Status", rb_cObject);
+ rb_cStatus = rb_define_class_under(rb_mGoogleRpcCore, "Status", rb_cObject);
/* Allocates an object whose memory is managed by the Ruby. */
rb_define_alloc_func(rb_cStatus, grpc_rb_status_alloc);
diff --git a/src/ruby/lib/grpc.rb b/src/ruby/lib/grpc.rb
index 60a3b96527..1012a836b7 100644
--- a/src/ruby/lib/grpc.rb
+++ b/src/ruby/lib/grpc.rb
@@ -27,12 +27,13 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-require 'grpc/event'
+require 'grpc/beefcake' # extends beefcake
require 'grpc/errors'
require 'grpc/grpc'
require 'grpc/logconfig'
-require 'grpc/time_consts'
require 'grpc/version'
+require 'grpc/core/event'
+require 'grpc/core/time_consts'
# alias GRPC
GRPC = Google::RPC
diff --git a/src/ruby/lib/grpc/beefcake.rb b/src/ruby/lib/grpc/beefcake.rb
new file mode 100644
index 0000000000..e8d7f0c2cd
--- /dev/null
+++ b/src/ruby/lib/grpc/beefcake.rb
@@ -0,0 +1,62 @@
+# 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.
+
+require 'beefcake'
+
+# Re-open the beefcake message module to add a static encode
+#
+# This is a temporary measure while beefcake is used as the default proto
+# library for developing grpc ruby. Once that changes to the official proto
+# library this can be removed. It's necessary to allow the update the service
+# module to assume a static encode method.
+#
+# TODO(temiola): remove me, once official code generation is available in protoc
+module Beefcake
+ module Message
+
+ # additional mixin module that adds static encode method when include
+ module StaticEncode
+
+ # encodes o with its instance#encode method
+ def encode(o)
+ o.encode
+ end
+
+ end
+
+ # extend self.included in Beefcake::Message to include StaticEncode
+ def self.included(o)
+ o.extend StaticEncode
+ o.extend Dsl
+ o.extend Decode
+ o.send(:include, Encode)
+ end
+
+ end
+end
diff --git a/src/ruby/lib/grpc/event.rb b/src/ruby/lib/grpc/core/event.rb
index c108cd4c1e..29486763d5 100644
--- a/src/ruby/lib/grpc/event.rb
+++ b/src/ruby/lib/grpc/core/event.rb
@@ -29,9 +29,11 @@
module Google
module RPC
- class Event # Add an inspect method to C-defined Event class.
- def inspect
- '<%s: type:%s, tag:%s result:%s>' % [self.class, type, tag, result]
+ module Core
+ class Event # Add an inspect method to C-defined Event class.
+ def inspect
+ '<%s: type:%s, tag:%s result:%s>' % [self.class, type, tag, result]
+ end
end
end
end
diff --git a/src/ruby/lib/grpc/time_consts.rb b/src/ruby/lib/grpc/core/time_consts.rb
index 2cbab5d965..52e4c3f9b9 100644
--- a/src/ruby/lib/grpc/time_consts.rb
+++ b/src/ruby/lib/grpc/core/time_consts.rb
@@ -31,39 +31,42 @@ require 'grpc'
module Google
module RPC
- module TimeConsts # re-opens a module in the C extension.
+ module Core
- # Converts a time delta to an absolute deadline.
- #
- # Assumes timeish is a relative time, and converts its to an absolute,
- # with following exceptions:
- #
- # * if timish is one of the TimeConsts.TimeSpec constants the value is
- # preserved.
- # * timish < 0 => TimeConsts.INFINITE_FUTURE
- # * timish == 0 => TimeConsts.ZERO
- #
- # @param timeish [Number|TimeSpec]
- # @return timeish [Number|TimeSpec]
- def from_relative_time(timeish)
- if timeish.is_a?TimeSpec
- timeish
- elsif timeish.nil?
- TimeConsts::ZERO
- elsif !timeish.is_a?Numeric
- raise TypeError('Cannot make an absolute deadline from %s',
- timeish.inspect)
- elsif timeish < 0
- TimeConsts::INFINITE_FUTURE
- elsif timeish == 0
- TimeConsts::ZERO
- else !timeish.nil?
- Time.now + timeish
+ module TimeConsts # re-opens a module in the C extension.
+
+ # Converts a time delta to an absolute deadline.
+ #
+ # Assumes timeish is a relative time, and converts its to an absolute,
+ # with following exceptions:
+ #
+ # * if timish is one of the TimeConsts.TimeSpec constants the value is
+ # preserved.
+ # * timish < 0 => TimeConsts.INFINITE_FUTURE
+ # * timish == 0 => TimeConsts.ZERO
+ #
+ # @param timeish [Number|TimeSpec]
+ # @return timeish [Number|TimeSpec]
+ def from_relative_time(timeish)
+ if timeish.is_a?TimeSpec
+ timeish
+ elsif timeish.nil?
+ TimeConsts::ZERO
+ elsif !timeish.is_a?Numeric
+ raise TypeError('Cannot make an absolute deadline from %s',
+ timeish.inspect)
+ elsif timeish < 0
+ TimeConsts::INFINITE_FUTURE
+ elsif timeish == 0
+ TimeConsts::ZERO
+ else !timeish.nil?
+ Time.now + timeish
+ end
end
- end
- module_function :from_relative_time
+ module_function :from_relative_time
+ end
end
end
end
diff --git a/src/ruby/lib/grpc/generic/active_call.rb b/src/ruby/lib/grpc/generic/active_call.rb
index d987b3966f..de35f278a1 100644
--- a/src/ruby/lib/grpc/generic/active_call.rb
+++ b/src/ruby/lib/grpc/generic/active_call.rb
@@ -40,8 +40,9 @@ module GRPC
# The ActiveCall class provides simple methods for sending marshallable
# data to a call
class ActiveCall
- include CompletionType
- include StatusCodes
+ include Core::CompletionType
+ include Core::StatusCodes
+ include Core::TimeConsts
attr_reader(:deadline)
# client_start_invoke begins a client invocation.
@@ -55,15 +56,15 @@ module GRPC
# @param q [CompletionQueue] used to wait for INVOKE_ACCEPTED
# @param deadline [Fixnum,TimeSpec] the deadline for INVOKE_ACCEPTED
def self.client_start_invoke(call, q, deadline)
- raise ArgumentError.new('not a call') unless call.is_a?Call
- if !q.is_a?CompletionQueue
+ raise ArgumentError.new('not a call') unless call.is_a?Core::Call
+ if !q.is_a?Core::CompletionQueue
raise ArgumentError.new('not a CompletionQueue')
end
invoke_accepted, client_metadata_read = Object.new, Object.new
finished_tag = Object.new
call.start_invoke(q, invoke_accepted, client_metadata_read, finished_tag)
# wait for the invocation to be accepted
- ev = q.pluck(invoke_accepted, TimeConsts::INFINITE_FUTURE)
+ ev = q.pluck(invoke_accepted, INFINITE_FUTURE)
raise OutOfTime if ev.nil?
finished_tag
end
@@ -93,8 +94,8 @@ module GRPC
# @param started [true|false] (default true) indicates if the call has begun
def initialize(call, q, marshal, unmarshal, deadline, finished_tag: nil,
started: true)
- raise ArgumentError.new('not a call') unless call.is_a?Call
- if !q.is_a?CompletionQueue
+ raise ArgumentError.new('not a call') unless call.is_a?Core::Call
+ if !q.is_a?Core::CompletionQueue
raise ArgumentError.new('not a CompletionQueue')
end
@call = call
@@ -178,11 +179,11 @@ module GRPC
# FINISHED.
def writes_done(assert_finished=true)
@call.writes_done(self)
- ev = @cq.pluck(self, TimeConsts::INFINITE_FUTURE)
+ ev = @cq.pluck(self, INFINITE_FUTURE)
assert_event_type(ev.type, FINISH_ACCEPTED)
logger.debug("Writes done: waiting for finish? #{assert_finished}")
if assert_finished
- ev = @cq.pluck(@finished_tag, TimeConsts::INFINITE_FUTURE)
+ ev = @cq.pluck(@finished_tag, INFINITE_FUTURE)
raise "unexpected event: #{ev.inspect}" if ev.nil?
return @call.status
end
@@ -193,9 +194,9 @@ module GRPC
# It blocks until the remote endpoint acknowledges by sending a FINISHED
# event.
def finished
- ev = @cq.pluck(@finished_tag, TimeConsts::INFINITE_FUTURE)
+ ev = @cq.pluck(@finished_tag, INFINITE_FUTURE)
raise "unexpected event: #{ev.inspect}" unless ev.type == FINISHED
- if ev.result.code != StatusCodes::OK
+ if ev.result.code != Core::StatusCodes::OK
raise BadStatus.new(ev.result.code, ev.result.details)
end
res = ev.result
@@ -223,11 +224,11 @@ module GRPC
else
payload = @marshal.call(req)
end
- @call.start_write(ByteBuffer.new(payload), self)
+ @call.start_write(Core::ByteBuffer.new(payload), self)
# call queue#pluck, and wait for WRITE_ACCEPTED, so as not to return
# until the flow control allows another send on this call.
- ev = @cq.pluck(self, TimeConsts::INFINITE_FUTURE)
+ ev = @cq.pluck(self, INFINITE_FUTURE)
assert_event_type(ev.type, WRITE_ACCEPTED)
ev = nil
end
@@ -240,8 +241,8 @@ module GRPC
# FINISHED.
def send_status(code=OK, details='', assert_finished=false)
assert_queue_is_ready
- @call.start_write_status(Status.new(code, details), self)
- ev = @cq.pluck(self, TimeConsts::INFINITE_FUTURE)
+ @call.start_write_status(Core::Status.new(code, details), self)
+ ev = @cq.pluck(self, INFINITE_FUTURE)
assert_event_type(ev.type, FINISH_ACCEPTED)
logger.debug("Status sent: #{code}:'#{details}'")
if assert_finished
@@ -257,7 +258,7 @@ module GRPC
# FINISHED, it returns nil if the status is OK, otherwise raising BadStatus
def remote_read
@call.start_read(self)
- ev = @cq.pluck(self, TimeConsts::INFINITE_FUTURE)
+ ev = @cq.pluck(self, INFINITE_FUTURE)
assert_event_type(ev.type, READ)
logger.debug("received req: #{ev.result.inspect}")
if !ev.result.nil?
@@ -291,7 +292,7 @@ module GRPC
return enum_for(:each_remote_read) if !block_given?
loop do
resp = remote_read()
- break if resp.is_a?Status # this will be an OK status, bad statii raise
+ break if resp.is_a?Core::Status # is an OK status, bad statii raise
break if resp.nil? # the last response was received
yield resp
end
@@ -321,7 +322,7 @@ module GRPC
return enum_for(:each_remote_read_then_finish) if !block_given?
loop do
resp = remote_read
- break if resp.is_a?Status # this will be an OK status, bad statii raise
+ break if resp.is_a?Core::Status # is an OK status, bad statii raise
if resp.nil? # the last response was received, but not finished yet
finished
break
@@ -339,7 +340,7 @@ module GRPC
remote_send(req)
writes_done(false)
response = remote_read
- if !response.is_a?(Status) # finish if status not yet received
+ if !response.is_a?(Core::Status) # finish if status not yet received
finished
end
response
@@ -360,7 +361,7 @@ module GRPC
requests.each { |r| remote_send(r) }
writes_done(false)
response = remote_read
- if !response.is_a?(Status) # finish if status not yet received
+ if !response.is_a?(Core::Status) # finish if status not yet received
finished
end
response
@@ -472,7 +473,7 @@ module GRPC
# shutdown.
def assert_queue_is_ready
begin
- ev = @cq.pluck(self, TimeConsts::ZERO)
+ ev = @cq.pluck(self, ZERO)
raise "unexpected event #{ev.inspect}" unless ev.nil?
rescue OutOfTime
# expected, nothing should be on the queue and the deadline was ZERO,
diff --git a/src/ruby/lib/grpc/generic/bidi_call.rb b/src/ruby/lib/grpc/generic/bidi_call.rb
index a3566e1118..91ceaa90e5 100644
--- a/src/ruby/lib/grpc/generic/bidi_call.rb
+++ b/src/ruby/lib/grpc/generic/bidi_call.rb
@@ -35,8 +35,9 @@ module GRPC
# The BiDiCall class orchestrates exection of a BiDi stream on a client or
# server.
class BidiCall
- include CompletionType
- include StatusCodes
+ include Core::CompletionType
+ include Core::StatusCodes
+ include Core::TimeConsts
# Creates a BidiCall.
#
@@ -59,8 +60,8 @@ module GRPC
# @param deadline [Fixnum] the deadline for the call to complete
# @param finished_tag [Object] the object used as the call's finish tag,
def initialize(call, q, marshal, unmarshal, deadline, finished_tag)
- raise ArgumentError.new('not a call') unless call.is_a?Call
- if !q.is_a?CompletionQueue
+ raise ArgumentError.new('not a call') unless call.is_a?Core::Call
+ if !q.is_a?Core::CompletionQueue
raise ArgumentError.new('not a CompletionQueue')
end
@call = call
@@ -210,7 +211,7 @@ module GRPC
# send the payload
payload = @marshal.call(req)
- @call.start_write(ByteBuffer.new(payload), self)
+ @call.start_write(Core::ByteBuffer.new(payload), self)
logger.debug("rwloop: sent payload #{req.inspect}")
in_write = true
return [in_write, done_writing]
@@ -259,10 +260,10 @@ module GRPC
logger.debug('waiting for another event')
if in_write or !done_reading or !pre_finished
logger.debug('waiting for another event')
- ev = @cq.pluck(self, TimeConsts::INFINITE_FUTURE)
+ ev = @cq.pluck(self, INFINITE_FUTURE)
elsif !finished
logger.debug('waiting for another event')
- ev = @cq.pluck(@finish_tag, TimeConsts::INFINITE_FUTURE)
+ ev = @cq.pluck(@finished_tag, INFINITE_FUTURE)
else
next # no events to wait on, but not done writing
end
@@ -270,7 +271,7 @@ module GRPC
break if done_writing and done_reading
if in_write or !done_reading
logger.debug('waiting for another event')
- ev = @cq.pluck(self, TimeConsts::INFINITE_FUTURE)
+ ev = @cq.pluck(self, INFINITE_FUTURE)
else
next # no events to wait on, but not done writing
end
diff --git a/src/ruby/lib/grpc/generic/client_stub.rb b/src/ruby/lib/grpc/generic/client_stub.rb
index fee31e3353..144f9d93ec 100644
--- a/src/ruby/lib/grpc/generic/client_stub.rb
+++ b/src/ruby/lib/grpc/generic/client_stub.rb
@@ -35,7 +35,7 @@ module GRPC
# ClientStub represents an endpoint used to send requests to GRPC servers.
class ClientStub
- include StatusCodes
+ include Core::StatusCodes
# Default deadline is 5 seconds.
DEFAULT_DEADLINE = 5
@@ -62,23 +62,29 @@ module GRPC
# when present, this is the default deadline used for calls
#
# @param host [String] the host the stub connects to
- # @param q [TaggedCompletionQueue] used to wait for events
- # @param channel_override [Channel] a pre-created channel
+ # @param q [Core::CompletionQueue] used to wait for events
+ # @param channel_override [Core::Channel] a pre-created channel
# @param deadline [Number] the default deadline to use in requests
+ # @param creds [Core::Credentials] secures and/or authenticates the channel
# @param kw [KeywordArgs] the channel arguments
def initialize(host, q,
channel_override:nil,
deadline:DEFAULT_DEADLINE,
+ creds:nil,
**kw)
- if !q.is_a?CompletionQueue
+ if !q.is_a?Core::CompletionQueue
raise ArgumentError.new('not a CompletionQueue')
end
@host = host
if !channel_override.nil?
ch = channel_override
- raise ArgumentError.new('not a Channel') unless ch.is_a?(Channel)
+ raise ArgumentError.new('not a Channel') unless ch.is_a?(Core::Channel)
+ elsif creds.nil?
+ ch = Core::Channel.new(host, kw)
+ elsif !creds.is_a?(Core::Credentials)
+ raise ArgumentError.new('not a Credentials')
else
- ch = Channel.new(host, **kw)
+ ch = Core::Channel.new(host, kw, creds)
end
@deadline = deadline
@@ -347,7 +353,7 @@ module GRPC
# @param unmarshal [Function] f(string)->obj that unmarshals responses
# @param deadline [TimeConst]
def new_active_call(ch, marshal, unmarshal, deadline=nil)
- absolute_deadline = TimeConsts.from_relative_time(deadline)
+ absolute_deadline = Core::TimeConsts.from_relative_time(deadline)
call = @ch.create_call(ch, @host, absolute_deadline)
ActiveCall.new(call, @queue, marshal, unmarshal, absolute_deadline,
started:false)
diff --git a/src/ruby/lib/grpc/generic/rpc_desc.rb b/src/ruby/lib/grpc/generic/rpc_desc.rb
index 43b4d4ffc6..eef886a72f 100644
--- a/src/ruby/lib/grpc/generic/rpc_desc.rb
+++ b/src/ruby/lib/grpc/generic/rpc_desc.rb
@@ -34,6 +34,7 @@ module GRPC
# RpcDesc is a Descriptor of an RPC method.
class RpcDesc < Struct.new(:name, :input, :output, :marshal_method,
:unmarshal_method)
+ include Core::StatusCodes
# Used to wrap a message class to indicate that it needs to be streamed.
class Stream
@@ -46,7 +47,7 @@ module GRPC
# @return [Proc] { |instance| marshalled(instance) }
def marshal_proc
- Proc.new { |o| o.method(marshal_method).call.to_s }
+ Proc.new { |o| o.class.method(marshal_method).call(o).to_s }
end
# @param [:input, :output] target determines whether to produce the an
@@ -82,7 +83,7 @@ module GRPC
else # is a bidi_stream
active_call.run_server_bidi(mth)
end
- send_status(active_call, StatusCodes::OK, 'OK')
+ send_status(active_call, OK, 'OK')
active_call.finished
rescue BadStatus => e
# this is raised by handlers that want GRPC to send an application
@@ -97,7 +98,7 @@ module GRPC
# This is raised when active_call#method.call exceeeds the deadline
# event. Send a status of deadline exceeded
logger.warn("late call: #{active_call}")
- send_status(active_call, StatusCodes::DEADLINE_EXCEEDED, 'late')
+ send_status(active_call, DEADLINE_EXCEEDED, 'late')
rescue EventError => e
# This is raised by GRPC internals but should rarely, if ever happen.
# Log it, but don't notify the other endpoint..
@@ -107,7 +108,7 @@ module GRPC
# Send back a UNKNOWN status to the client
logger.warn("failed handler: #{active_call}; sending status:UNKNOWN")
logger.warn(e)
- send_status(active_call, StatusCodes::UNKNOWN, 'no reason given')
+ send_status(active_call, UNKNOWN, 'no reason given')
end
end
diff --git a/src/ruby/lib/grpc/generic/rpc_server.rb b/src/ruby/lib/grpc/generic/rpc_server.rb
index e6efdc32c1..ebbf3f9780 100644
--- a/src/ruby/lib/grpc/generic/rpc_server.rb
+++ b/src/ruby/lib/grpc/generic/rpc_server.rb
@@ -38,7 +38,8 @@ module GRPC
# RpcServer hosts a number of services and makes them available on the
# network.
class RpcServer
- include CompletionType
+ include Core::CompletionType
+ include Core::TimeConsts
extend ::Forwardable
def_delegators :@server, :add_http2_port
@@ -57,7 +58,7 @@ module GRPC
# instance, however other arbitrary are allowed and when present are used
# to configure the listeninng connection set up by the RpcServer.
#
- # * server_override: which if passed must be a [GRPC::Server]. When
+ # * server_override: which if passed must be a [GRPC::Core::Server]. When
# present.
#
# * poll_period: when present, the server polls for new events with this
@@ -70,30 +71,38 @@ module GRPC
# completion_queue that the server uses to receive network events,
# otherwise its creates a new instance itself
#
+ # * creds: [GRPC::Core::ServerCredentials]
+ # the credentials used to secure the server
+ #
# * max_waiting_requests: the maximum number of requests that are not
# being handled to allow. When this limit is exceeded, the server responds
# with not available to new requests
def initialize(pool_size:DEFAULT_POOL_SIZE,
max_waiting_requests:DEFAULT_MAX_WAITING_REQUESTS,
- poll_period:TimeConsts::INFINITE_FUTURE,
+ poll_period:INFINITE_FUTURE,
completion_queue_override:nil,
+ creds:nil,
server_override:nil,
**kw)
if !completion_queue_override.nil?
cq = completion_queue_override
- if !cq.is_a?(CompletionQueue)
+ if !cq.is_a?(Core::CompletionQueue)
raise ArgumentError.new('not a CompletionQueue')
end
else
- cq = CompletionQueue.new
+ cq = Core::CompletionQueue.new
end
@cq = cq
if !server_override.nil?
srv = server_override
- raise ArgumentError.new('not a Server') unless srv.is_a?(Server)
+ raise ArgumentError.new('not a Server') unless srv.is_a?(Core::Server)
+ elsif creds.nil?
+ srv = Core::Server.new(@cq, kw)
+ elsif !creds.is_a?(Core::ServerCredentials)
+ raise ArgumentError.new('not a ServerCredentials')
else
- srv = Server.new(@cq, **kw)
+ srv = Core::Server.new(@cq, kw, creds)
end
@server = srv
@@ -236,7 +245,7 @@ module GRPC
# Accept the call. This is necessary even if a status is to be sent back
# immediately
finished_tag = Object.new
- call_queue = CompletionQueue.new
+ call_queue = Core::CompletionQueue.new
call.accept(call_queue, finished_tag)
# Send UNAVAILABLE if there are too many unprocessed jobs
diff --git a/src/ruby/lib/grpc/generic/service.rb b/src/ruby/lib/grpc/generic/service.rb
index 1a3d0dc63e..05bb0af809 100644
--- a/src/ruby/lib/grpc/generic/service.rb
+++ b/src/ruby/lib/grpc/generic/service.rb
@@ -88,12 +88,12 @@ module GRPC
# - unmarshal_class method must be a class method on the serializable
# message type that takes a string (byte stream) and produces and object
#
- # - marshal_instance_method is called on a serializable message instance
+ # - marshal_class_method is called on a serializable message instance
# and produces a serialized string.
#
# The Dsl verifies that the types in the descriptor have both the
# unmarshal and marshal methods.
- attr_writer(:marshal_instance_method, :unmarshal_class_method)
+ attr_writer(:marshal_class_method, :unmarshal_class_method)
attr_accessor(:service_name)
# Adds an RPC spec.
@@ -113,7 +113,7 @@ module GRPC
assert_can_marshal(input)
assert_can_marshal(output)
rpc_descs[name] = RpcDesc.new(name, input, output,
- marshal_instance_method,
+ marshal_class_method,
unmarshal_class_method)
end
@@ -125,8 +125,8 @@ module GRPC
end
# the name of the instance method used to marshal events to a byte stream.
- def marshal_instance_method
- @marshal_instance_method ||= :marshal
+ def marshal_class_method
+ @marshal_class_method ||= :marshal
end
# the name of the class method used to unmarshal from a byte stream.
@@ -144,9 +144,9 @@ module GRPC
raise ArgumentError, "#{cls} needs #{cls}.#{mth}"
end
- mth = marshal_instance_method
- if !cls.instance_methods.include?(mth)
- raise ArgumentError, "#{cls} needs #{cls}.new.#{mth}"
+ mth = marshal_class_method
+ if !cls.methods.include?(mth)
+ raise ArgumentError, "#{cls} needs #{cls}.#{mth}"
end
end
@@ -173,7 +173,7 @@ module GRPC
# @param kw [KeywordArgs] the channel arguments, plus any optional
# args for configuring the client's channel
def initialize(host, **kw)
- super(host, CompletionQueue.new, **kw)
+ super(host, Core::CompletionQueue.new, **kw)
end
# Used define_method to add a method for each rpc_desc. Each method
diff --git a/src/ruby/spec/alloc_spec.rb b/src/ruby/spec/alloc_spec.rb
index 99cc39d0ba..305405e9bd 100644
--- a/src/ruby/spec/alloc_spec.rb
+++ b/src/ruby/spec/alloc_spec.rb
@@ -31,15 +31,15 @@ require 'grpc'
describe 'Wrapped classes where .new cannot create an instance' do
- describe GRPC::Event do
+ describe GRPC::Core::Event do
it 'should fail .new fail with a runtime error' do
- expect { GRPC::Event.new }.to raise_error(TypeError)
+ expect { GRPC::Core::Event.new }.to raise_error(TypeError)
end
end
- describe GRPC::Call do
+ describe GRPC::Core::Call do
it 'should fail .new fail with a runtime error' do
- expect { GRPC::Event.new }.to raise_error(TypeError)
+ expect { GRPC::Core::Event.new }.to raise_error(TypeError)
end
end
diff --git a/src/ruby/spec/byte_buffer_spec.rb b/src/ruby/spec/byte_buffer_spec.rb
index d4d3a692b1..b89d7f3640 100644
--- a/src/ruby/spec/byte_buffer_spec.rb
+++ b/src/ruby/spec/byte_buffer_spec.rb
@@ -29,25 +29,25 @@
require 'grpc'
-describe GRPC::ByteBuffer do
+describe GRPC::Core::ByteBuffer do
describe '#new' do
it 'is constructed from a string' do
- expect { GRPC::ByteBuffer.new('#new') }.not_to raise_error
+ expect { GRPC::Core::ByteBuffer.new('#new') }.not_to raise_error
end
it 'can be constructed from the empty string' do
- expect { GRPC::ByteBuffer.new('') }.not_to raise_error
+ expect { GRPC::Core::ByteBuffer.new('') }.not_to raise_error
end
it 'cannot be constructed from nil' do
- expect { GRPC::ByteBuffer.new(nil) }.to raise_error TypeError
+ expect { GRPC::Core::ByteBuffer.new(nil) }.to raise_error TypeError
end
it 'cannot be constructed from non-strings' do
[1, Object.new, :a_symbol].each do |x|
- expect { GRPC::ByteBuffer.new(x) }.to raise_error TypeError
+ expect { GRPC::Core::ByteBuffer.new(x) }.to raise_error TypeError
end
end
@@ -55,13 +55,13 @@ describe GRPC::ByteBuffer do
describe '#to_s' do
it 'is the string value the ByteBuffer was constructed with' do
- expect(GRPC::ByteBuffer.new('#to_s').to_s).to eq('#to_s')
+ expect(GRPC::Core::ByteBuffer.new('#to_s').to_s).to eq('#to_s')
end
end
describe '#dup' do
it 'makes an instance whose #to_s is the original string value' do
- bb = GRPC::ByteBuffer.new('#dup')
+ bb = GRPC::Core::ByteBuffer.new('#dup')
a_copy = bb.dup
expect(a_copy.to_s).to eq('#dup')
expect(a_copy.dup.to_s).to eq('#dup')
diff --git a/src/ruby/spec/call_spec.rb b/src/ruby/spec/call_spec.rb
index 339f2c1a94..9228f7df27 100644
--- a/src/ruby/spec/call_spec.rb
+++ b/src/ruby/spec/call_spec.rb
@@ -30,9 +30,9 @@
require 'grpc'
require 'port_picker'
-include GRPC::StatusCodes
+include GRPC::Core::StatusCodes
-describe GRPC::RpcErrors do
+describe GRPC::Core::RpcErrors do
before(:each) do
@known_types = {
@@ -60,24 +60,24 @@ describe GRPC::RpcErrors do
end
it 'should have symbols for all the known error codes' do
- m = GRPC::RpcErrors
+ m = GRPC::Core::RpcErrors
syms_and_codes = m.constants.collect { |c| [c, m.const_get(c)] }
expect(Hash[syms_and_codes]).to eq(@known_types)
end
end
-describe GRPC::Call do
+describe GRPC::Core::Call do
before(:each) do
@tag = Object.new
- @client_queue = GRPC::CompletionQueue.new
- @server_queue = GRPC::CompletionQueue.new
+ @client_queue = GRPC::Core::CompletionQueue.new
+ @server_queue = GRPC::Core::CompletionQueue.new
port = find_unused_tcp_port
host = "localhost:#{port}"
- @server = GRPC::Server.new(@server_queue, nil)
+ @server = GRPC::Core::Server.new(@server_queue, nil)
@server.add_http2_port(host)
- @ch = GRPC::Channel.new(host, nil)
+ @ch = GRPC::Core::Channel.new(host, nil)
end
after(:each) do
@@ -86,29 +86,29 @@ describe GRPC::Call do
describe '#start_read' do
it 'should fail if called immediately' do
- expect { make_test_call.start_read(@tag) }.to raise_error GRPC::CallError
+ expect { make_test_call.start_read(@tag) }.to raise_error GRPC::Core::CallError
end
end
describe '#start_write' do
it 'should fail if called immediately' do
- bytes = GRPC::ByteBuffer.new('test string')
+ bytes = GRPC::Core::ByteBuffer.new('test string')
expect { make_test_call.start_write(bytes, @tag) }
- .to raise_error GRPC::CallError
+ .to raise_error GRPC::Core::CallError
end
end
describe '#start_write_status' do
it 'should fail if called immediately' do
- sts = GRPC::Status.new(153, 'test detail')
+ sts = GRPC::Core::Status.new(153, 'test detail')
expect { make_test_call.start_write_status(sts, @tag) }
- .to raise_error GRPC::CallError
+ .to raise_error GRPC::Core::CallError
end
end
describe '#writes_done' do
it 'should fail if called immediately' do
- expect { make_test_call.writes_done(@tag) }.to raise_error GRPC::CallError
+ expect { make_test_call.writes_done(@tag) }.to raise_error GRPC::Core::CallError
end
end
@@ -126,9 +126,9 @@ describe GRPC::Call do
call = make_test_call
expect(call.start_invoke(@client_queue, @tag, @tag, @tag)).to be_nil
ev = @client_queue.next(deadline)
- expect(ev.call).to be_a(GRPC::Call)
+ expect(ev.call).to be_a(GRPC::Core::Call)
expect(ev.tag).to be(@tag)
- expect(ev.type).to be(GRPC::CompletionType::INVOKE_ACCEPTED)
+ expect(ev.type).to be(GRPC::Core::CompletionType::INVOKE_ACCEPTED)
expect(ev.call).to_not be(call)
end
end
@@ -138,12 +138,12 @@ describe GRPC::Call do
call = make_test_call
call.start_invoke(@client_queue, @tag, @tag, @tag)
ev = @client_queue.next(deadline)
- expect(ev.type).to be(GRPC::CompletionType::INVOKE_ACCEPTED)
- expect(call.start_write(GRPC::ByteBuffer.new('test_start_write'),
+ expect(ev.type).to be(GRPC::Core::CompletionType::INVOKE_ACCEPTED)
+ expect(call.start_write(GRPC::Core::ByteBuffer.new('test_start_write'),
@tag)).to be_nil
ev = @client_queue.next(deadline)
- expect(ev.call).to be_a(GRPC::Call)
- expect(ev.type).to be(GRPC::CompletionType::WRITE_ACCEPTED)
+ expect(ev.call).to be_a(GRPC::Core::Call)
+ expect(ev.type).to be(GRPC::Core::CompletionType::WRITE_ACCEPTED)
expect(ev.tag).to be(@tag)
end
end
@@ -151,7 +151,7 @@ describe GRPC::Call do
describe '#status' do
it 'can save the status and read it back' do
call = make_test_call
- sts = GRPC::Status.new(OK, 'OK')
+ sts = GRPC::Core::Status.new(OK, 'OK')
expect { call.status = sts }.not_to raise_error
expect(call.status).to be(sts)
end
diff --git a/src/ruby/spec/channel_spec.rb b/src/ruby/spec/channel_spec.rb
index bd46bffc10..d2686127bb 100644
--- a/src/ruby/spec/channel_spec.rb
+++ b/src/ruby/spec/channel_spec.rb
@@ -30,135 +30,166 @@
require 'grpc'
require 'port_picker'
-module GRPC
+def load_test_certs
+ test_root = File.join(File.dirname(__FILE__), 'testdata')
+ files = ['ca.pem', 'server1.key', 'server1.pem']
+ files.map { |f| File.open(File.join(test_root, f)).read }
+end
- describe Channel do
+describe GRPC::Core::Channel do
- before(:each) do
- @cq = CompletionQueue.new
- end
- describe '#new' do
+ def create_test_cert
+ GRPC::Core::Credentials.new(load_test_certs[0])
+ end
- it 'take a host name without channel args' do
- expect { Channel.new('dummy_host', nil) }.not_to raise_error
- end
+ before(:each) do
+ @cq = GRPC::Core::CompletionQueue.new
+ end
- it 'does not take a hash with bad keys as channel args' do
- blk = construct_with_args(Object.new => 1)
- expect(&blk).to raise_error TypeError
- blk = construct_with_args(1 => 1)
- expect(&blk).to raise_error TypeError
- end
+ shared_examples '#new' do
- it 'does not take a hash with bad values as channel args' do
- blk = construct_with_args(:symbol => Object.new)
- expect(&blk).to raise_error TypeError
- blk = construct_with_args('1' => Hash.new)
- expect(&blk).to raise_error TypeError
- end
+ it 'take a host name without channel args' do
+ expect { GRPC::Core::Channel.new('dummy_host', nil) }.not_to raise_error
+ end
- it 'can take a hash with a symbol key as channel args' do
- blk = construct_with_args(:a_symbol => 1)
- expect(&blk).to_not raise_error
- end
+ it 'does not take a hash with bad keys as channel args' do
+ blk = construct_with_args(Object.new => 1)
+ expect(&blk).to raise_error TypeError
+ blk = construct_with_args(1 => 1)
+ expect(&blk).to raise_error TypeError
+ end
- it 'can take a hash with a string key as channel args' do
- blk = construct_with_args('a_symbol' => 1)
- expect(&blk).to_not raise_error
- end
+ it 'does not take a hash with bad values as channel args' do
+ blk = construct_with_args(:symbol => Object.new)
+ expect(&blk).to raise_error TypeError
+ blk = construct_with_args('1' => Hash.new)
+ expect(&blk).to raise_error TypeError
+ end
- it 'can take a hash with a string value as channel args' do
- blk = construct_with_args(:a_symbol => '1')
- expect(&blk).to_not raise_error
- end
+ it 'can take a hash with a symbol key as channel args' do
+ blk = construct_with_args(:a_symbol => 1)
+ expect(&blk).to_not raise_error
+ end
- it 'can take a hash with a symbol value as channel args' do
- blk = construct_with_args(:a_symbol => :another_symbol)
- expect(&blk).to_not raise_error
- end
+ it 'can take a hash with a string key as channel args' do
+ blk = construct_with_args('a_symbol' => 1)
+ expect(&blk).to_not raise_error
+ end
- it 'can take a hash with a numeric value as channel args' do
- blk = construct_with_args(:a_symbol => 1)
- expect(&blk).to_not raise_error
- end
+ it 'can take a hash with a string value as channel args' do
+ blk = construct_with_args(:a_symbol => '1')
+ expect(&blk).to_not raise_error
+ end
- it 'can take a hash with many args as channel args' do
- args = Hash[127.times.collect { |x| [x.to_s, x] } ]
- blk = construct_with_args(args)
- expect(&blk).to_not raise_error
- end
+ it 'can take a hash with a symbol value as channel args' do
+ blk = construct_with_args(:a_symbol => :another_symbol)
+ expect(&blk).to_not raise_error
+ end
+ it 'can take a hash with a numeric value as channel args' do
+ blk = construct_with_args(:a_symbol => 1)
+ expect(&blk).to_not raise_error
end
- describe '#create_call' do
- it 'creates a call OK' do
- port = find_unused_tcp_port
- host = "localhost:#{port}"
- ch = Channel.new(host, nil)
+ it 'can take a hash with many args as channel args' do
+ args = Hash[127.times.collect { |x| [x.to_s, x] } ]
+ blk = construct_with_args(args)
+ expect(&blk).to_not raise_error
+ end
- deadline = Time.now + 5
- expect(ch.create_call('dummy_method', 'dummy_host', deadline))
- .not_to be(nil)
- end
+ end
- it 'raises an error if called on a closed channel' do
- port = find_unused_tcp_port
- host = "localhost:#{port}"
- ch = Channel.new(host, nil)
- ch.close
-
- deadline = Time.now + 5
- blk = Proc.new do
- ch.create_call('dummy_method', 'dummy_host', deadline)
- end
- expect(&blk).to raise_error(RuntimeError)
- end
+ describe '#new for secure channels' do
+ def construct_with_args(a)
+ Proc.new { GRPC::Core::Channel.new('dummy_host', a, create_test_cert) }
end
- describe '#destroy' do
- it 'destroys a channel ok' do
- port = find_unused_tcp_port
- host = "localhost:#{port}"
- ch = Channel.new(host, nil)
- blk = Proc.new { ch.destroy }
- expect(&blk).to_not raise_error
- end
+ it_behaves_like '#new'
+ end
- it 'can be called more than once without error' do
- port = find_unused_tcp_port
- host = "localhost:#{port}"
- ch = Channel.new(host, nil)
- blk = Proc.new { ch.destroy }
- blk.call
- expect(&blk).to_not raise_error
- end
+ describe '#new for insecure channels' do
+ it_behaves_like '#new'
+
+ def construct_with_args(a)
+ Proc.new { GRPC::Core::Channel.new('dummy_host', a) }
end
+ end
- describe '#close' do
- it 'closes a channel ok' do
- port = find_unused_tcp_port
- host = "localhost:#{port}"
- ch = Channel.new(host, nil)
- blk = Proc.new { ch.close }
- expect(&blk).to_not raise_error
+ describe '#create_call' do
+ it 'creates a call OK' do
+ port = find_unused_tcp_port
+ host = "localhost:#{port}"
+ ch = GRPC::Core::Channel.new(host, nil)
+
+ deadline = Time.now + 5
+
+ blk = Proc.new do
+ ch.create_call('dummy_method', 'dummy_host', deadline)
end
+ expect(&blk).to_not raise_error
+ end
- it 'can be called more than once without error' do
- port = find_unused_tcp_port
- host = "localhost:#{port}"
- ch = Channel.new(host, nil)
- blk = Proc.new { ch.close }
- blk.call
- expect(&blk).to_not raise_error
+ it 'raises an error if called on a closed channel' do
+ port = find_unused_tcp_port
+ host = "localhost:#{port}"
+ ch = GRPC::Core::Channel.new(host, nil)
+ ch.close
+
+ deadline = Time.now + 5
+ blk = Proc.new do
+ ch.create_call('dummy_method', 'dummy_host', deadline)
end
+ expect(&blk).to raise_error(RuntimeError)
end
- def construct_with_args(a)
- Proc.new {Channel.new('dummy_host', a)}
+ end
+
+ describe '#destroy' do
+ it 'destroys a channel ok' do
+ port = find_unused_tcp_port
+ host = "localhost:#{port}"
+ ch = GRPC::Core::Channel.new(host, nil)
+ blk = Proc.new { ch.destroy }
+ expect(&blk).to_not raise_error
+ end
+
+ it 'can be called more than once without error' do
+ port = find_unused_tcp_port
+ host = "localhost:#{port}"
+ ch = GRPC::Core::Channel.new(host, nil)
+ blk = Proc.new { ch.destroy }
+ blk.call
+ expect(&blk).to_not raise_error
+ end
+ end
+
+ describe '::SSL_TARGET' do
+
+ it 'is a symbol' do
+ expect(GRPC::Core::Channel::SSL_TARGET).to be_a(Symbol)
+ end
+
+ end
+
+ describe '#close' do
+ it 'closes a channel ok' do
+ port = find_unused_tcp_port
+ host = "localhost:#{port}"
+ ch = GRPC::Core::Channel.new(host, nil)
+ blk = Proc.new { ch.close }
+ expect(&blk).to_not raise_error
end
+ it 'can be called more than once without error' do
+ port = find_unused_tcp_port
+ host = "localhost:#{port}"
+ ch = GRPC::Core::Channel.new(host, nil)
+ blk = Proc.new { ch.close }
+ blk.call
+ expect(&blk).to_not raise_error
+ end
end
end
diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb
index 64068ab391..c96fe979ac 100644
--- a/src/ruby/spec/client_server_spec.rb
+++ b/src/ruby/spec/client_server_spec.rb
@@ -31,8 +31,14 @@ require 'grpc'
require 'port_picker'
require 'spec_helper'
-include GRPC::CompletionType
-include GRPC
+include GRPC::Core::CompletionType
+include GRPC::Core
+
+def load_test_certs
+ test_root = File.join(File.dirname(__FILE__), 'testdata')
+ files = ['ca.pem', 'server1.key', 'server1.pem']
+ files.map { |f| File.open(File.join(test_root, f)).read }
+end
shared_context 'setup: tags' do
@@ -327,9 +333,9 @@ describe 'the http client/server' do
before(:example) do
port = find_unused_tcp_port
host = "localhost:#{port}"
- @client_queue = GRPC::CompletionQueue.new
- @server_queue = GRPC::CompletionQueue.new
- @server = GRPC::Server.new(@server_queue, nil)
+ @client_queue = GRPC::Core::CompletionQueue.new
+ @server_queue = GRPC::Core::CompletionQueue.new
+ @server = GRPC::Core::Server.new(@server_queue, nil)
@server.add_http2_port(host)
@server.start
@ch = Channel.new(host, nil)
@@ -339,6 +345,34 @@ describe 'the http client/server' do
@server.close
end
+ it_behaves_like 'basic GRPC message delivery is OK' do
+ end
+
+ it_behaves_like 'GRPC metadata delivery works OK' do
+ end
+
+end
+
+describe 'the secure http client/server' do
+
+ before(:example) do
+ certs = load_test_certs
+ port = find_unused_tcp_port
+ host = "localhost:#{port}"
+ @client_queue = GRPC::Core::CompletionQueue.new
+ @server_queue = GRPC::Core::CompletionQueue.new
+ server_creds = GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
+ @server = GRPC::Core::Server.new(@server_queue, nil, server_creds)
+ @server.add_http2_port(host, true)
+ @server.start
+ args = {Channel::SSL_TARGET => 'foo.test.google.com'}
+ @ch = Channel.new(host, args,
+ GRPC::Core::Credentials.new(certs[0], nil, nil))
+ end
+
+ after(:example) do
+ @server.close
+ end
it_behaves_like 'basic GRPC message delivery is OK' do
end
diff --git a/src/ruby/spec/completion_queue_spec.rb b/src/ruby/spec/completion_queue_spec.rb
index 37432443a9..50f74b5826 100644
--- a/src/ruby/spec/completion_queue_spec.rb
+++ b/src/ruby/spec/completion_queue_spec.rb
@@ -29,26 +29,25 @@
require 'grpc'
-describe GRPC::CompletionQueue do
+describe GRPC::Core::CompletionQueue do
describe '#new' do
it 'is constructed successufully' do
- expect { GRPC::CompletionQueue.new }.not_to raise_error
- expect(GRPC::CompletionQueue.new).to be_a(GRPC::CompletionQueue)
+ expect { GRPC::Core::CompletionQueue.new }.not_to raise_error
end
end
describe '#next' do
it 'can be called without failing' do
- ch = GRPC::CompletionQueue.new
+ ch = GRPC::Core::CompletionQueue.new
expect { ch.next(3) }.not_to raise_error
end
it 'can be called with the time constants' do
- ch = GRPC::CompletionQueue.new
+ ch = GRPC::Core::CompletionQueue.new
# don't use INFINITE_FUTURE, as there we have no events.
non_blocking_consts = [:ZERO, :INFINITE_PAST]
- m = GRPC::TimeConsts
+ m = GRPC::Core::TimeConsts
non_blocking_consts.each do |c|
a_time = m.const_get(c)
expect { ch.next(a_time) }.not_to raise_error
@@ -59,16 +58,16 @@ describe GRPC::CompletionQueue do
describe '#pluck' do
it 'can be called without failing' do
- ch = GRPC::CompletionQueue.new
+ ch = GRPC::Core::CompletionQueue.new
tag = Object.new
expect { ch.pluck(tag, 3) }.not_to raise_error
end
it 'can be called with the time constants' do
- ch = GRPC::CompletionQueue.new
+ ch = GRPC::Core::CompletionQueue.new
# don't use INFINITE_FUTURE, as there we have no events.
non_blocking_consts = [:ZERO, :INFINITE_PAST]
- m = GRPC::TimeConsts
+ m = GRPC::Core::TimeConsts
tag = Object.new
non_blocking_consts.each do |c|
a_time = m.const_get(c)
diff --git a/src/ruby/spec/credentials_spec.rb b/src/ruby/spec/credentials_spec.rb
new file mode 100644
index 0000000000..4d932db937
--- /dev/null
+++ b/src/ruby/spec/credentials_spec.rb
@@ -0,0 +1,87 @@
+# 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.
+
+require 'grpc'
+
+
+def load_test_certs
+ test_root = File.join(File.dirname(__FILE__), 'testdata')
+ files = ['ca.pem', 'server1.pem', 'server1.key']
+ files.map { |f| File.open(File.join(test_root, f)).read }
+end
+
+Credentials = GRPC::Core::Credentials
+
+describe Credentials do
+
+ describe '#new' do
+
+ it 'can be constructed with fake inputs' do
+ expect { Credentials.new('root_certs', 'key', 'cert') }.not_to raise_error
+ end
+
+ it 'it can be constructed using specific test certificates' do
+ certs = load_test_certs
+ expect { Credentials.new(*certs) }.not_to raise_error
+ end
+
+ it 'can be constructed with server roots certs only' do
+ root_cert, _, _ = load_test_certs
+ expect { Credentials.new(root_cert) }.not_to raise_error
+ end
+
+ it 'cannot be constructed with a nil server roots' do
+ _, client_key, client_chain = load_test_certs
+ blk = Proc.new { Credentials.new(nil, client_key, client_chain) }
+ expect(&blk).to raise_error
+ end
+
+ end
+
+ describe '#compose' do
+
+ it 'can be completed OK' do
+ certs = load_test_certs
+ cred1 = Credentials.new(*certs)
+ cred2 = Credentials.new(*certs)
+ expect { cred1.compose(cred2) }.to_not raise_error
+ end
+
+ end
+
+ describe 'Credentials#default' do
+
+ it 'is not implemented yet' do
+ expect { Credentials.default() }.to raise_error RuntimeError
+ end
+
+ end
+
+
+end
diff --git a/src/ruby/spec/event_spec.rb b/src/ruby/spec/event_spec.rb
index 19b9754d31..a61b926dea 100644
--- a/src/ruby/spec/event_spec.rb
+++ b/src/ruby/spec/event_spec.rb
@@ -29,7 +29,7 @@
require 'grpc'
-describe GRPC::CompletionType do
+describe GRPC::Core::CompletionType do
before(:each) do
@known_types = {
@@ -46,9 +46,9 @@ describe GRPC::CompletionType do
end
it 'should have all the known types' do
- mod = GRPC::CompletionType
- expect(Hash[mod.constants.collect { |c| [c, mod.const_get(c)] }])
- .to eq(@known_types)
+ mod = GRPC::Core::CompletionType
+ blk = Proc.new { Hash[mod.constants.collect { |c| [c, mod.const_get(c)] }] }
+ expect(blk.call).to eq(@known_types)
end
end
diff --git a/src/ruby/spec/generic/active_call_spec.rb b/src/ruby/spec/generic/active_call_spec.rb
index 872625ccf0..ceeef2a1d8 100644
--- a/src/ruby/spec/generic/active_call_spec.rb
+++ b/src/ruby/spec/generic/active_call_spec.rb
@@ -31,291 +31,289 @@ require 'grpc'
require 'grpc/generic/active_call'
require_relative '../port_picker'
-module GRPC
+ActiveCall = GRPC::ActiveCall
+
+describe GRPC::ActiveCall do
+
+ before(:each) do
+ @pass_through = Proc.new { |x| x }
+ @server_tag = Object.new
+ @server_finished_tag = Object.new
+ @tag = Object.new
+
+ @client_queue = GRPC::Core::CompletionQueue.new
+ @server_queue = GRPC::Core::CompletionQueue.new
+ port = find_unused_tcp_port
+ host = "localhost:#{port}"
+ @server = GRPC::Core::Server.new(@server_queue, nil)
+ @server.add_http2_port(host)
+ @server.start
+ @ch = GRPC::Core::Channel.new(host, nil)
+ end
- describe ActiveCall do
+ after(:each) do
+ @server.close
+ end
+ describe 'restricted view methods' do
before(:each) do
- @pass_through = Proc.new { |x| x }
- @server_tag = Object.new
- @server_finished_tag = Object.new
- @tag = Object.new
-
- @client_queue = CompletionQueue.new
- @server_queue = CompletionQueue.new
- port = find_unused_tcp_port
- host = "localhost:#{port}"
- @server = GRPC::Server.new(@server_queue, nil)
- @server.add_http2_port(host)
- @server.start
- @ch = GRPC::Channel.new(host, nil)
- end
-
- after(:each) do
- @server.close
+ call = make_test_call
+ finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+ deadline)
+ @client_call = ActiveCall.new(call, @client_queue, @pass_through,
+ @pass_through, deadline,
+ finished_tag: finished_tag)
end
- describe 'restricted view methods' do
- before(:each) do
- call = make_test_call
- finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
- deadline)
- @client_call = ActiveCall.new(call, @client_queue, @pass_through,
- @pass_through, deadline,
- finished_tag: finished_tag)
- end
-
- describe '#multi_req_view' do
- it 'exposes a fixed subset of the ActiveCall methods' do
- want = ['cancelled', 'deadline', 'each_remote_read', 'shutdown']
- v = @client_call.multi_req_view
- want.each do |w|
- expect(v.methods.include?(w))
- end
+ describe '#multi_req_view' do
+ it 'exposes a fixed subset of the ActiveCall methods' do
+ want = ['cancelled', 'deadline', 'each_remote_read', 'shutdown']
+ v = @client_call.multi_req_view
+ want.each do |w|
+ expect(v.methods.include?(w))
end
end
+ end
- describe '#single_req_view' do
- it 'exposes a fixed subset of the ActiveCall methods' do
- want = ['cancelled', 'deadline', 'shutdown']
- v = @client_call.single_req_view
- want.each do |w|
- expect(v.methods.include?(w))
- end
+ describe '#single_req_view' do
+ it 'exposes a fixed subset of the ActiveCall methods' do
+ want = ['cancelled', 'deadline', 'shutdown']
+ v = @client_call.single_req_view
+ want.each do |w|
+ expect(v.methods.include?(w))
end
end
end
+ end
- describe '#remote_send' do
- it 'allows a client to send a payload to the server' do
- call = make_test_call
- finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
- deadline)
- @client_call = ActiveCall.new(call, @client_queue, @pass_through,
- @pass_through, deadline,
- finished_tag: finished_tag)
- msg = 'message is a string'
- @client_call.remote_send(msg)
-
- # check that server rpc new was received
- @server.request_call(@server_tag)
- ev = @server_queue.next(deadline)
- expect(ev.type).to be(CompletionType::SERVER_RPC_NEW)
- expect(ev.call).to be_a(Call)
- expect(ev.tag).to be(@server_tag)
-
- # Accept the call, and verify that the server reads the response ok.
- ev.call.accept(@client_queue, @server_tag)
- server_call = ActiveCall.new(ev.call, @client_queue, @pass_through,
- @pass_through, deadline)
- expect(server_call.remote_read).to eq(msg)
- end
-
- it 'marshals the payload using the marshal func' do
- call = make_test_call
- finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
- deadline)
- marshal = Proc.new { |x| 'marshalled:' + x }
- client_call = ActiveCall.new(call, @client_queue, marshal,
- @pass_through, deadline,
- finished_tag: finished_tag)
- msg = 'message is a string'
- client_call.remote_send(msg)
-
- # confirm that the message was marshalled
- @server.request_call(@server_tag)
- ev = @server_queue.next(deadline)
- ev.call.accept(@client_queue, @server_tag)
- server_call = ActiveCall.new(ev.call, @client_queue, @pass_through,
- @pass_through, deadline)
- expect(server_call.remote_read).to eq('marshalled:' + msg)
- end
-
+ describe '#remote_send' do
+ it 'allows a client to send a payload to the server' do
+ call = make_test_call
+ finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+ deadline)
+ @client_call = ActiveCall.new(call, @client_queue, @pass_through,
+ @pass_through, deadline,
+ finished_tag: finished_tag)
+ msg = 'message is a string'
+ @client_call.remote_send(msg)
+
+ # check that server rpc new was received
+ @server.request_call(@server_tag)
+ ev = @server_queue.next(deadline)
+ expect(ev.type).to be(CompletionType::SERVER_RPC_NEW)
+ expect(ev.call).to be_a(Call)
+ expect(ev.tag).to be(@server_tag)
+
+ # Accept the call, and verify that the server reads the response ok.
+ ev.call.accept(@client_queue, @server_tag)
+ server_call = ActiveCall.new(ev.call, @client_queue, @pass_through,
+ @pass_through, deadline)
+ expect(server_call.remote_read).to eq(msg)
end
- describe '#remote_read' do
- it 'reads the response sent by a server' do
- call, pass_through = make_test_call, Proc.new { |x| x }
- finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
- deadline)
- client_call = ActiveCall.new(call, @client_queue, @pass_through,
- @pass_through, deadline,
- finished_tag: finished_tag)
- msg = 'message is a string'
- client_call.remote_send(msg)
- server_call = expect_server_to_receive(msg)
- server_call.remote_send('server_response')
- expect(client_call.remote_read).to eq('server_response')
- end
-
- it 'get a nil msg before a status when an OK status is sent' do
- call, pass_through = make_test_call, Proc.new { |x| x }
- finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
- deadline)
- client_call = ActiveCall.new(call, @client_queue, @pass_through,
- @pass_through, deadline,
- finished_tag: finished_tag)
- msg = 'message is a string'
- client_call.remote_send(msg)
- client_call.writes_done(false)
- server_call = expect_server_to_receive(msg)
- server_call.remote_send('server_response')
- server_call.send_status(StatusCodes::OK, 'OK')
- expect(client_call.remote_read).to eq('server_response')
- res = client_call.remote_read
- expect(res).to be_nil
- end
+ it 'marshals the payload using the marshal func' do
+ call = make_test_call
+ finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+ deadline)
+ marshal = Proc.new { |x| 'marshalled:' + x }
+ client_call = ActiveCall.new(call, @client_queue, marshal,
+ @pass_through, deadline,
+ finished_tag: finished_tag)
+ msg = 'message is a string'
+ client_call.remote_send(msg)
+
+ # confirm that the message was marshalled
+ @server.request_call(@server_tag)
+ ev = @server_queue.next(deadline)
+ ev.call.accept(@client_queue, @server_tag)
+ server_call = ActiveCall.new(ev.call, @client_queue, @pass_through,
+ @pass_through, deadline)
+ expect(server_call.remote_read).to eq('marshalled:' + msg)
+ end
+ end
- it 'unmarshals the response using the unmarshal func' do
- call = make_test_call
- finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
- deadline)
- unmarshal = Proc.new { |x| 'unmarshalled:' + x }
- client_call = ActiveCall.new(call, @client_queue, @pass_through,
- unmarshal, deadline,
- finished_tag: finished_tag)
-
- # confirm the client receives the unmarshalled message
- msg = 'message is a string'
- client_call.remote_send(msg)
- server_call = expect_server_to_receive(msg)
- server_call.remote_send('server_response')
- expect(client_call.remote_read).to eq('unmarshalled:server_response')
- end
+ describe '#remote_read' do
+ it 'reads the response sent by a server' do
+ call, pass_through = make_test_call, Proc.new { |x| x }
+ finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+ deadline)
+ client_call = ActiveCall.new(call, @client_queue, @pass_through,
+ @pass_through, deadline,
+ finished_tag: finished_tag)
+ msg = 'message is a string'
+ client_call.remote_send(msg)
+ server_call = expect_server_to_receive(msg)
+ server_call.remote_send('server_response')
+ expect(client_call.remote_read).to eq('server_response')
+ end
+ it 'get a nil msg before a status when an OK status is sent' do
+ call, pass_through = make_test_call, Proc.new { |x| x }
+ finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+ deadline)
+ client_call = ActiveCall.new(call, @client_queue, @pass_through,
+ @pass_through, deadline,
+ finished_tag: finished_tag)
+ msg = 'message is a string'
+ client_call.remote_send(msg)
+ client_call.writes_done(false)
+ server_call = expect_server_to_receive(msg)
+ server_call.remote_send('server_response')
+ server_call.send_status(StatusCodes::OK, 'OK')
+ expect(client_call.remote_read).to eq('server_response')
+ res = client_call.remote_read
+ expect(res).to be_nil
end
- describe '#each_remote_read' do
- it 'creates an Enumerator' do
- call = make_test_call
- client_call = ActiveCall.new(call, @client_queue, @pass_through,
- @pass_through, deadline)
- expect(client_call.each_remote_read).to be_a(Enumerator)
- end
- it 'the returns an enumerator that can read n responses' do
- call = make_test_call
- finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
- deadline)
- client_call = ActiveCall.new(call, @client_queue, @pass_through,
- @pass_through, deadline,
- finished_tag: finished_tag)
- msg = 'message is 4a string'
- reply = 'server_response'
- client_call.remote_send(msg)
- server_call = expect_server_to_receive(msg)
- e = client_call.each_remote_read
- n = 3 # arbitrary value > 1
- n.times do
- server_call.remote_send(reply)
- expect(e.next).to eq(reply)
- end
- end
+ it 'unmarshals the response using the unmarshal func' do
+ call = make_test_call
+ finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+ deadline)
+ unmarshal = Proc.new { |x| 'unmarshalled:' + x }
+ client_call = ActiveCall.new(call, @client_queue, @pass_through,
+ unmarshal, deadline,
+ finished_tag: finished_tag)
+
+ # confirm the client receives the unmarshalled message
+ msg = 'message is a string'
+ client_call.remote_send(msg)
+ server_call = expect_server_to_receive(msg)
+ server_call.remote_send('server_response')
+ expect(client_call.remote_read).to eq('unmarshalled:server_response')
+ end
- it 'the returns an enumerator that stops after an OK Status' do
- call = make_test_call
- finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
- deadline)
- client_call = ActiveCall.new(call, @client_queue, @pass_through,
- @pass_through, deadline,
- finished_tag: finished_tag)
- msg = 'message is a string'
- reply = 'server_response'
- client_call.remote_send(msg)
- client_call.writes_done(false)
- server_call = expect_server_to_receive(msg)
- e = client_call.each_remote_read
- n = 3 # arbitrary value > 1
- n.times do
- server_call.remote_send(reply)
- expect(e.next).to eq(reply)
- end
- server_call.send_status(StatusCodes::OK, 'OK')
- expect { e.next }.to raise_error(StopIteration)
- end
+ end
+ describe '#each_remote_read' do
+ it 'creates an Enumerator' do
+ call = make_test_call
+ client_call = ActiveCall.new(call, @client_queue, @pass_through,
+ @pass_through, deadline)
+ expect(client_call.each_remote_read).to be_a(Enumerator)
end
- describe '#writes_done' do
- it 'finishes ok if the server sends a status response' do
- call = make_test_call
- finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
- deadline)
- client_call = ActiveCall.new(call, @client_queue, @pass_through,
- @pass_through, deadline,
- finished_tag: finished_tag)
- msg = 'message is a string'
- client_call.remote_send(msg)
- expect { client_call.writes_done(false) }.to_not raise_error
- server_call = expect_server_to_receive(msg)
- server_call.remote_send('server_response')
- expect(client_call.remote_read).to eq('server_response')
- server_call.send_status(StatusCodes::OK, 'status code is OK')
- expect { server_call.finished }.to_not raise_error
- expect { client_call.finished }.to_not raise_error
+ it 'the returns an enumerator that can read n responses' do
+ call = make_test_call
+ finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+ deadline)
+ client_call = ActiveCall.new(call, @client_queue, @pass_through,
+ @pass_through, deadline,
+ finished_tag: finished_tag)
+ msg = 'message is 4a string'
+ reply = 'server_response'
+ client_call.remote_send(msg)
+ server_call = expect_server_to_receive(msg)
+ e = client_call.each_remote_read
+ n = 3 # arbitrary value > 1
+ n.times do
+ server_call.remote_send(reply)
+ expect(e.next).to eq(reply)
end
+ end
- it 'finishes ok if the server sends an early status response' do
- call = make_test_call
- finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
- deadline)
- client_call = ActiveCall.new(call, @client_queue, @pass_through,
- @pass_through, deadline,
- finished_tag: finished_tag)
- msg = 'message is a string'
- client_call.remote_send(msg)
- server_call = expect_server_to_receive(msg)
- server_call.remote_send('server_response')
- server_call.send_status(StatusCodes::OK, 'status code is OK')
- expect(client_call.remote_read).to eq('server_response')
- expect { client_call.writes_done(false) }.to_not raise_error
- expect { server_call.finished }.to_not raise_error
- expect { client_call.finished }.to_not raise_error
+ it 'the returns an enumerator that stops after an OK Status' do
+ call = make_test_call
+ finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+ deadline)
+ client_call = ActiveCall.new(call, @client_queue, @pass_through,
+ @pass_through, deadline,
+ finished_tag: finished_tag)
+ msg = 'message is a string'
+ reply = 'server_response'
+ client_call.remote_send(msg)
+ client_call.writes_done(false)
+ server_call = expect_server_to_receive(msg)
+ e = client_call.each_remote_read
+ n = 3 # arbitrary value > 1
+ n.times do
+ server_call.remote_send(reply)
+ expect(e.next).to eq(reply)
end
+ server_call.send_status(StatusCodes::OK, 'OK')
+ expect { e.next }.to raise_error(StopIteration)
+ end
- it 'finishes ok if writes_done is true' do
- call = make_test_call
- finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
- deadline)
- client_call = ActiveCall.new(call, @client_queue, @pass_through,
- @pass_through, deadline,
- finished_tag: finished_tag)
- msg = 'message is a string'
- client_call.remote_send(msg)
- server_call = expect_server_to_receive(msg)
- server_call.remote_send('server_response')
- server_call.send_status(StatusCodes::OK, 'status code is OK')
- expect(client_call.remote_read).to eq('server_response')
- expect { client_call.writes_done(true) }.to_not raise_error
- expect { server_call.finished }.to_not raise_error
- end
+ end
+ describe '#writes_done' do
+ it 'finishes ok if the server sends a status response' do
+ call = make_test_call
+ finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+ deadline)
+ client_call = ActiveCall.new(call, @client_queue, @pass_through,
+ @pass_through, deadline,
+ finished_tag: finished_tag)
+ msg = 'message is a string'
+ client_call.remote_send(msg)
+ expect { client_call.writes_done(false) }.to_not raise_error
+ server_call = expect_server_to_receive(msg)
+ server_call.remote_send('server_response')
+ expect(client_call.remote_read).to eq('server_response')
+ server_call.send_status(StatusCodes::OK, 'status code is OK')
+ expect { server_call.finished }.to_not raise_error
+ expect { client_call.finished }.to_not raise_error
end
- def expect_server_to_receive(sent_text)
- c = expect_server_to_be_invoked
- expect(c.remote_read).to eq(sent_text)
- c
+ it 'finishes ok if the server sends an early status response' do
+ call = make_test_call
+ finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+ deadline)
+ client_call = ActiveCall.new(call, @client_queue, @pass_through,
+ @pass_through, deadline,
+ finished_tag: finished_tag)
+ msg = 'message is a string'
+ client_call.remote_send(msg)
+ server_call = expect_server_to_receive(msg)
+ server_call.remote_send('server_response')
+ server_call.send_status(StatusCodes::OK, 'status code is OK')
+ expect(client_call.remote_read).to eq('server_response')
+ expect { client_call.writes_done(false) }.to_not raise_error
+ expect { server_call.finished }.to_not raise_error
+ expect { client_call.finished }.to_not raise_error
end
- def expect_server_to_be_invoked()
- @server.request_call(@server_tag)
- ev = @server_queue.next(deadline)
- ev.call.accept(@client_queue, @server_finished_tag)
- ActiveCall.new(ev.call, @client_queue, @pass_through,
- @pass_through, deadline,
- finished_tag: @server_finished_tag)
+ it 'finishes ok if writes_done is true' do
+ call = make_test_call
+ finished_tag = ActiveCall.client_start_invoke(call, @client_queue,
+ deadline)
+ client_call = ActiveCall.new(call, @client_queue, @pass_through,
+ @pass_through, deadline,
+ finished_tag: finished_tag)
+ msg = 'message is a string'
+ client_call.remote_send(msg)
+ server_call = expect_server_to_receive(msg)
+ server_call.remote_send('server_response')
+ server_call.send_status(StatusCodes::OK, 'status code is OK')
+ expect(client_call.remote_read).to eq('server_response')
+ expect { client_call.writes_done(true) }.to_not raise_error
+ expect { server_call.finished }.to_not raise_error
end
- def make_test_call
- @ch.create_call('dummy_method', 'dummy_host', deadline)
- end
+ end
- def deadline
- Time.now + 0.25 # in 0.25 seconds; arbitrary
- end
+ def expect_server_to_receive(sent_text)
+ c = expect_server_to_be_invoked
+ expect(c.remote_read).to eq(sent_text)
+ c
+ end
+
+ def expect_server_to_be_invoked()
+ @server.request_call(@server_tag)
+ ev = @server_queue.next(deadline)
+ ev.call.accept(@client_queue, @server_finished_tag)
+ ActiveCall.new(ev.call, @client_queue, @pass_through,
+ @pass_through, deadline,
+ finished_tag: @server_finished_tag)
+ end
+
+ def make_test_call
+ @ch.create_call('dummy_method', 'dummy_host', deadline)
+ end
+ def deadline
+ Time.now + 0.25 # in 0.25 seconds; arbitrary
end
end
diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb
index c8dee74563..4b01af9581 100644
--- a/src/ruby/spec/generic/client_stub_spec.rb
+++ b/src/ruby/spec/generic/client_stub_spec.rb
@@ -44,12 +44,16 @@ def wakey_thread(&blk)
t
end
+def load_test_certs
+ test_root = File.join(File.parent(File.dirname(__FILE__)), 'testdata')
+ files = ['ca.pem', 'server1.key', 'server1.pem']
+ files.map { |f| File.open(File.join(test_root, f)).read }
+end
-include GRPC::StatusCodes
+include GRPC::Core::StatusCodes
+include GRPC::Core::TimeConsts
describe 'ClientStub' do
- BadStatus = GRPC::BadStatus
- TimeConsts = GRPC::TimeConsts
before(:each) do
Thread.abort_on_exception = true
@@ -57,7 +61,7 @@ describe 'ClientStub' do
@method = 'an_rpc_method'
@pass = OK
@fail = INTERNAL
- @cq = GRPC::CompletionQueue.new
+ @cq = GRPC::Core::CompletionQueue.new
end
after(:each) do
@@ -102,6 +106,29 @@ describe 'ClientStub' do
expect(&blk).to raise_error
end
+ it 'cannot be created with bad credentials' do
+ host = new_test_host
+ blk = Proc.new do
+ opts = {:a_channel_arg => 'an_arg', :creds => Object.new}
+ GRPC::ClientStub.new(host, @cq, **opts)
+ end
+ expect(&blk).to raise_error
+ end
+
+ it 'can be created with test test credentials' do
+ certs = load_test_certs
+ host = new_test_host
+ blk = Proc.new do
+ opts = {
+ GRPC::Core::Channel::SSL_TARGET => 'foo.test.google.com',
+ :a_channel_arg => 'an_arg',
+ :creds => GRPC::Core::Credentials.new(certs[0], nil, nil)
+ }
+ GRPC::ClientStub.new(host, @cq, **opts)
+ end
+ expect(&blk).to_not raise_error
+ end
+
end
describe '#request_response' do
@@ -123,7 +150,7 @@ describe 'ClientStub' do
it 'should send a request when configured using an override channel' do
alt_host = new_test_host
th = run_request_response(alt_host, @sent_msg, @resp, @pass)
- ch = GRPC::Channel.new(alt_host, nil)
+ ch = GRPC::Core::Channel.new(alt_host, nil)
stub = GRPC::ClientStub.new('ignored-host', @cq,
channel_override:ch)
resp = stub.request_response(@method, @sent_msg, NOOP, NOOP)
@@ -138,7 +165,7 @@ describe 'ClientStub' do
blk = Proc.new do
stub.request_response(@method, @sent_msg, NOOP, NOOP)
end
- expect(&blk).to raise_error(BadStatus)
+ expect(&blk).to raise_error(GRPC::BadStatus)
th.join
end
@@ -168,7 +195,7 @@ describe 'ClientStub' do
blk = Proc.new do
op.execute()
end
- expect(&blk).to raise_error(BadStatus)
+ expect(&blk).to raise_error(GRPC::BadStatus)
th.join
end
@@ -309,7 +336,7 @@ describe 'ClientStub' do
describe 'without a call operation' do
- it 'supports a simple scenario with all requests sent first' do
+ it 'supports sending all the requests first', :bidi => true do
host = new_test_host
th = run_bidi_streamer_handle_inputs_first(host, @sent_msgs, @replys,
@pass)
@@ -320,7 +347,7 @@ describe 'ClientStub' do
th.join
end
- it 'supports a simple scenario with a client-initiated ping pong' do
+ it 'supports client-initiated ping pong', :bidi => true do
host = new_test_host
th = run_bidi_streamer_echo_ping_pong(host, @sent_msgs, @pass, true)
stub = GRPC::ClientStub.new(host, @cq)
@@ -336,7 +363,7 @@ describe 'ClientStub' do
# servers don't know if all the client metadata has been sent until
# they receive a message from the client. Without receiving all the
# metadata, the server does not accept the call, so this test hangs.
- xit 'supports a simple scenario with a server-initiated ping pong' do
+ xit 'supports a server-initiated ping pong', :bidi => true do
host = new_test_host
th = run_bidi_streamer_echo_ping_pong(host, @sent_msgs, @pass, false)
stub = GRPC::ClientStub.new(host, @cq)
@@ -350,7 +377,7 @@ describe 'ClientStub' do
describe 'via a call operation' do
- it 'supports a simple scenario with all requests sent first' do
+ it 'supports sending all the requests first', :bidi => true do
host = new_test_host
th = run_bidi_streamer_handle_inputs_first(host, @sent_msgs, @replys,
@pass)
@@ -364,7 +391,7 @@ describe 'ClientStub' do
th.join
end
- it 'supports a simple scenario with a client-initiated ping pong' do
+ it 'supports client-initiated ping pong', :bidi => true do
host = new_test_host
th = run_bidi_streamer_echo_ping_pong(host, @sent_msgs, @pass, true)
stub = GRPC::ClientStub.new(host, @cq)
@@ -383,7 +410,7 @@ describe 'ClientStub' do
# servers don't know if all the client metadata has been sent until
# they receive a message from the client. Without receiving all the
# metadata, the server does not accept the call, so this test hangs.
- xit 'supports a simple scenario with a server-initiated ping pong' do
+ xit 'supports server-initiated ping pong', :bidi => true do
th = run_bidi_streamer_echo_ping_pong(host, @sent_msgs, @pass, false)
stub = GRPC::ClientStub.new(host, @cq)
op = stub.bidi_streamer(@method, @sent_msgs, NOOP, NOOP,
@@ -454,8 +481,8 @@ describe 'ClientStub' do
end
def start_test_server(hostname, awake_mutex, awake_cond)
- server_queue = GRPC::CompletionQueue.new
- @server = GRPC::Server.new(server_queue, nil)
+ server_queue = GRPC::Core::CompletionQueue.new
+ @server = GRPC::Core::Server.new(server_queue, nil)
@server.add_http2_port(hostname)
@server.start
@server_tag = Object.new
@@ -467,12 +494,11 @@ describe 'ClientStub' do
def expect_server_to_be_invoked(hostname, awake_mutex, awake_cond)
server_queue = start_test_server(hostname, awake_mutex, awake_cond)
test_deadline = Time.now + 10 # fail tests after 10 seconds
- ev = server_queue.pluck(@server_tag, TimeConsts::INFINITE_FUTURE)
+ ev = server_queue.pluck(@server_tag, INFINITE_FUTURE)
raise OutOfTime if ev.nil?
finished_tag = Object.new
ev.call.accept(server_queue, finished_tag)
- GRPC::ActiveCall.new(ev.call, server_queue, NOOP,
- NOOP, TimeConsts::INFINITE_FUTURE,
+ GRPC::ActiveCall.new(ev.call, server_queue, NOOP, NOOP, INFINITE_FUTURE,
finished_tag: finished_tag)
end
diff --git a/src/ruby/spec/generic/rpc_desc_spec.rb b/src/ruby/spec/generic/rpc_desc_spec.rb
index 141fb1187d..efef7e4686 100644
--- a/src/ruby/spec/generic/rpc_desc_spec.rb
+++ b/src/ruby/spec/generic/rpc_desc_spec.rb
@@ -35,8 +35,11 @@ describe GRPC::RpcDesc do
RpcDesc = GRPC::RpcDesc
Stream = RpcDesc::Stream
- OK = GRPC::StatusCodes::OK
- UNKNOWN = GRPC::StatusCodes::UNKNOWN
+ OK = GRPC::Core::StatusCodes::OK
+ INTERNAL = GRPC::Core::StatusCodes::INTERNAL
+ UNKNOWN = GRPC::Core::StatusCodes::UNKNOWN
+ CallError = GRPC::Core::CallError
+ EventError = GRPC::Core::EventError
before(:each) do
@request_response = RpcDesc.new('rr', Object.new, Object.new, 'encode',
@@ -47,7 +50,7 @@ describe GRPC::RpcDesc do
'encode', 'decode')
@bidi_streamer = RpcDesc.new('ss', Stream.new(Object.new),
Stream.new(Object.new), 'encode', 'decode')
- @bs_code = GRPC::StatusCodes::INTERNAL
+ @bs_code = INTERNAL
@no_reason = 'no reason given'
@ok_response = Object.new
end
@@ -74,7 +77,7 @@ describe GRPC::RpcDesc do
end
it 'absorbs EventError with no further action' do
- expect(@call).to receive(:remote_read).once.and_raise(GRPC::EventError)
+ expect(@call).to receive(:remote_read).once.and_raise(EventError)
blk = Proc.new do
@request_response.run_server_method(@call, method(:fake_reqresp))
end
@@ -82,7 +85,7 @@ describe GRPC::RpcDesc do
end
it 'absorbs CallError with no further action' do
- expect(@call).to receive(:remote_read).once.and_raise(GRPC::CallError)
+ expect(@call).to receive(:remote_read).once.and_raise(CallError)
blk = Proc.new do
@request_response.run_server_method(@call, method(:fake_reqresp))
end
@@ -118,7 +121,7 @@ describe GRPC::RpcDesc do
end
it 'absorbs EventError with no further action' do
- expect(@call).to receive(:remote_send).once.and_raise(GRPC::EventError)
+ expect(@call).to receive(:remote_send).once.and_raise(EventError)
blk = Proc.new do
@client_streamer.run_server_method(@call, method(:fake_clstream))
end
@@ -126,7 +129,7 @@ describe GRPC::RpcDesc do
end
it 'absorbs CallError with no further action' do
- expect(@call).to receive(:remote_send).once.and_raise(GRPC::CallError)
+ expect(@call).to receive(:remote_send).once.and_raise(CallError)
blk = Proc.new do
@client_streamer.run_server_method(@call, method(:fake_clstream))
end
@@ -163,7 +166,7 @@ describe GRPC::RpcDesc do
end
it 'absorbs EventError with no further action' do
- expect(@call).to receive(:remote_read).once.and_raise(GRPC::EventError)
+ expect(@call).to receive(:remote_read).once.and_raise(EventError)
blk = Proc.new do
@server_streamer.run_server_method(@call, method(:fake_svstream))
end
@@ -171,7 +174,7 @@ describe GRPC::RpcDesc do
end
it 'absorbs CallError with no further action' do
- expect(@call).to receive(:remote_read).once.and_raise(GRPC::CallError)
+ expect(@call).to receive(:remote_read).once.and_raise(CallError)
blk = Proc.new do
@server_streamer.run_server_method(@call, method(:fake_svstream))
end
@@ -377,4 +380,3 @@ describe GRPC::RpcDesc do
end
end
-
diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb
index 4e7379bc45..fc579a6c3f 100644
--- a/src/ruby/spec/generic/rpc_server_spec.rb
+++ b/src/ruby/spec/generic/rpc_server_spec.rb
@@ -35,8 +35,14 @@ require 'grpc/generic/service'
require 'xray/thread_dump_signal_handler'
require_relative '../port_picker'
+def load_test_certs
+ test_root = File.join(File.parent(File.dirname(__FILE__)), 'testdata')
+ files = ['ca.pem', 'server1.key', 'server1.pem']
+ files.map { |f| File.open(File.join(test_root, f)).read }
+end
+
class EchoMsg
- def marshal
+ def self.marshal(o)
''
end
@@ -86,302 +92,324 @@ end
SlowStub = SlowService.rpc_stub_class
-module GRPC
+describe GRPC::RpcServer do
- describe RpcServer do
+ RpcServer = GRPC::RpcServer
- before(:each) do
- @method = 'an_rpc_method'
- @pass = 0
- @fail = 1
- @noop = Proc.new { |x| x }
-
- @server_queue = CompletionQueue.new
- port = find_unused_tcp_port
- @host = "localhost:#{port}"
- @server = GRPC::Server.new(@server_queue, nil)
- @server.add_http2_port(@host)
- @ch = GRPC::Channel.new(@host, nil)
- end
+ before(:each) do
+ @method = 'an_rpc_method'
+ @pass = 0
+ @fail = 1
+ @noop = Proc.new { |x| x }
+
+ @server_queue = GRPC::Core::CompletionQueue.new
+ port = find_unused_tcp_port
+ @host = "localhost:#{port}"
+ @server = GRPC::Core::Server.new(@server_queue, nil)
+ @server.add_http2_port(@host)
+ @ch = GRPC::Core::Channel.new(@host, nil)
+ end
+
+ after(:each) do
+ @server.close
+ end
+
+ describe '#new' do
- after(:each) do
- @server.close
+ it 'can be created with just some args' do
+ opts = {:a_channel_arg => 'an_arg'}
+ blk = Proc.new do
+ RpcServer.new(**opts)
+ end
+ expect(&blk).not_to raise_error
end
- describe '#new' do
+ it 'can be created with a default deadline' do
+ opts = {:a_channel_arg => 'an_arg', :deadline => 5}
+ blk = Proc.new do
+ RpcServer.new(**opts)
+ end
+ expect(&blk).not_to raise_error
+ end
- it 'can be created with just some args' do
- opts = {:a_channel_arg => 'an_arg'}
- blk = Proc.new do
- RpcServer.new(**opts)
- end
- expect(&blk).not_to raise_error
+ it 'can be created with a completion queue override' do
+ opts = {
+ :a_channel_arg => 'an_arg',
+ :completion_queue_override => @server_queue
+ }
+ blk = Proc.new do
+ RpcServer.new(**opts)
end
+ expect(&blk).not_to raise_error
+ end
- it 'can be created with a default deadline' do
- opts = {:a_channel_arg => 'an_arg', :deadline => 5}
- blk = Proc.new do
- RpcServer.new(**opts)
- end
- expect(&blk).not_to raise_error
+ it 'cannot be created with a bad completion queue override' do
+ blk = Proc.new do
+ opts = {
+ :a_channel_arg => 'an_arg',
+ :completion_queue_override => Object.new
+ }
+ RpcServer.new(**opts)
end
+ expect(&blk).to raise_error
+ end
- it 'can be created with a completion queue override' do
+ it 'cannot be created with invalid ServerCredentials' do
+ blk = Proc.new do
opts = {
:a_channel_arg => 'an_arg',
- :completion_queue_override => @server_queue
+ :creds => Object.new
}
- blk = Proc.new do
- RpcServer.new(**opts)
- end
- expect(&blk).not_to raise_error
+ RpcServer.new(**opts)
end
+ expect(&blk).to raise_error
+ end
- it 'cannot be created with a bad completion queue override' do
- blk = Proc.new do
- opts = {
- :a_channel_arg => 'an_arg',
- :completion_queue_override => Object.new
- }
- RpcServer.new(**opts)
- end
- expect(&blk).to raise_error
+ it 'can be created with the creds as valid ServerCedentials' do
+ certs = load_test_certs
+ server_creds = GRPC::Core::ServerCredentials.new(nil, certs[1], certs[2])
+ blk = Proc.new do
+ opts = {
+ :a_channel_arg => 'an_arg',
+ :creds => server_creds
+ }
+ RpcServer.new(**opts)
end
+ expect(&blk).to_not raise_error
+ end
- it 'can be created with a server override' do
- opts = {:a_channel_arg => 'an_arg', :server_override => @server}
- blk = Proc.new do
- RpcServer.new(**opts)
- end
- expect(&blk).not_to raise_error
+ it 'can be created with a server override' do
+ opts = {:a_channel_arg => 'an_arg', :server_override => @server}
+ blk = Proc.new do
+ RpcServer.new(**opts)
end
+ expect(&blk).not_to raise_error
+ end
- it 'cannot be created with a bad server override' do
- blk = Proc.new do
- opts = {
- :a_channel_arg => 'an_arg',
- :server_override => Object.new
- }
- RpcServer.new(**opts)
- end
- expect(&blk).to raise_error
+ it 'cannot be created with a bad server override' do
+ blk = Proc.new do
+ opts = {
+ :a_channel_arg => 'an_arg',
+ :server_override => Object.new
+ }
+ RpcServer.new(**opts)
end
+ expect(&blk).to raise_error
+ end
+
+ end
+ describe '#stopped?' do
+
+ before(:each) do
+ opts = {:a_channel_arg => 'an_arg', :poll_period => 1}
+ @srv = RpcServer.new(**opts)
end
- describe '#stopped?' do
+ it 'starts out false' do
+ expect(@srv.stopped?).to be(false)
+ end
- before(:each) do
- opts = {:a_channel_arg => 'an_arg', :poll_period => 1}
- @srv = RpcServer.new(**opts)
- end
+ it 'stays false after a #stop is called before #run' do
+ @srv.stop
+ expect(@srv.stopped?).to be(false)
+ end
- it 'starts out false' do
- expect(@srv.stopped?).to be(false)
- end
+ it 'stays false after the server starts running' do
+ @srv.handle(EchoService)
+ t = Thread.new { @srv.run }
+ @srv.wait_till_running
+ expect(@srv.stopped?).to be(false)
+ @srv.stop
+ t.join
+ end
- it 'stays false after a #stop is called before #run' do
- @srv.stop
- expect(@srv.stopped?).to be(false)
- end
+ it 'is true after a running server is stopped' do
+ @srv.handle(EchoService)
+ t = Thread.new { @srv.run }
+ @srv.wait_till_running
+ @srv.stop
+ expect(@srv.stopped?).to be(true)
+ t.join
+ end
- it 'stays false after the server starts running' do
- @srv.handle(EchoService)
- t = Thread.new { @srv.run }
- @srv.wait_till_running
- expect(@srv.stopped?).to be(false)
- @srv.stop
- t.join
- end
+ end
- it 'is true after a running server is stopped' do
- @srv.handle(EchoService)
- t = Thread.new { @srv.run }
- @srv.wait_till_running
- @srv.stop
- expect(@srv.stopped?).to be(true)
- t.join
- end
+ describe '#running?' do
+
+ it 'starts out false' do
+ opts = {:a_channel_arg => 'an_arg', :server_override => @server}
+ r = RpcServer.new(**opts)
+ expect(r.running?).to be(false)
+ end
+ it 'is false after run is called with no services registered' do
+ opts = {
+ :a_channel_arg => 'an_arg',
+ :poll_period => 1,
+ :server_override => @server
+ }
+ r = RpcServer.new(**opts)
+ r.run()
+ expect(r.running?).to be(false)
end
- describe '#running?' do
+ it 'is true after run is called with a registered service' do
+ opts = {
+ :a_channel_arg => 'an_arg',
+ :poll_period => 1,
+ :server_override => @server
+ }
+ r = RpcServer.new(**opts)
+ r.handle(EchoService)
+ t = Thread.new { r.run }
+ r.wait_till_running
+ expect(r.running?).to be(true)
+ r.stop
+ t.join
+ end
- it 'starts out false' do
- opts = {:a_channel_arg => 'an_arg', :server_override => @server}
- r = RpcServer.new(**opts)
- expect(r.running?).to be(false)
- end
+ end
- it 'is false after run is called with no services registered' do
- opts = {
- :a_channel_arg => 'an_arg',
- :poll_period => 1,
- :server_override => @server
- }
- r = RpcServer.new(**opts)
- r.run()
- expect(r.running?).to be(false)
- end
+ describe '#handle' do
- it 'is true after run is called with a registered service' do
- opts = {
- :a_channel_arg => 'an_arg',
- :poll_period => 1,
- :server_override => @server
- }
- r = RpcServer.new(**opts)
- r.handle(EchoService)
- t = Thread.new { r.run }
- r.wait_till_running
- expect(r.running?).to be(true)
- r.stop
- t.join
- end
+ before(:each) do
+ @opts = {:a_channel_arg => 'an_arg', :poll_period => 1}
+ @srv = RpcServer.new(**@opts)
+ end
+ it 'raises if #run has already been called' do
+ @srv.handle(EchoService)
+ t = Thread.new { @srv.run }
+ @srv.wait_till_running
+ expect { @srv.handle(EchoService) }.to raise_error
+ @srv.stop
+ t.join
end
- describe '#handle' do
+ it 'raises if the server has been run and stopped' do
+ @srv.handle(EchoService)
+ t = Thread.new { @srv.run }
+ @srv.wait_till_running
+ @srv.stop
+ t.join
+ expect { @srv.handle(EchoService) }.to raise_error
+ end
- before(:each) do
- @opts = {:a_channel_arg => 'an_arg', :poll_period => 1}
- @srv = RpcServer.new(**@opts)
- end
+ it 'raises if the service does not include GenericService ' do
+ expect { @srv.handle(Object) }.to raise_error
+ end
+
+ it 'raises if the service does not declare any rpc methods' do
+ expect { @srv.handle(EmptyService) }.to raise_error
+ end
+
+ it 'raises if the service does not define its rpc methods' do
+ expect { @srv.handle(NoRpcImplementation) }.to raise_error
+ end
+
+ it 'raises if a handler method is already registered' do
+ @srv.handle(EchoService)
+ expect { r.handle(EchoService) }.to raise_error
+ end
+
+ end
+
+ describe '#run' do
+
+ before(:each) do
+ @client_opts = {
+ :channel_override => @ch
+ }
+ @marshal = EchoService.rpc_descs[:an_rpc].marshal_proc
+ @unmarshal = EchoService.rpc_descs[:an_rpc].unmarshal_proc(:output)
+ server_opts = {
+ :server_override => @server,
+ :completion_queue_override => @server_queue,
+ :poll_period => 1
+ }
+ @srv = RpcServer.new(**server_opts)
+ end
- it 'raises if #run has already been called' do
+ describe 'when running' do
+
+ it 'should return NOT_FOUND status for requests on unknown methods' do
@srv.handle(EchoService)
t = Thread.new { @srv.run }
@srv.wait_till_running
- expect { @srv.handle(EchoService) }.to raise_error
+ req = EchoMsg.new
+ blk = Proc.new do
+ cq = GRPC::Core::CompletionQueue.new
+ stub = GRPC::ClientStub.new(@host, cq, **@client_opts)
+ stub.request_response('/unknown', req, @marshal, @unmarshal)
+ end
+ expect(&blk).to raise_error BadStatus
@srv.stop
t.join
end
- it 'raises if the server has been run and stopped' do
+ it 'should obtain responses for multiple sequential requests' do
@srv.handle(EchoService)
t = Thread.new { @srv.run }
@srv.wait_till_running
+ req = EchoMsg.new
+ n = 5 # arbitrary
+ stub = EchoStub.new(@host, **@client_opts)
+ n.times { |x| expect(stub.an_rpc(req)).to be_a(EchoMsg) }
@srv.stop
t.join
- expect { @srv.handle(EchoService) }.to raise_error
- end
-
- it 'raises if the service does not include GenericService ' do
- expect { @srv.handle(Object) }.to raise_error
- end
-
- it 'raises if the service does not declare any rpc methods' do
- expect { @srv.handle(EmptyService) }.to raise_error
- end
-
- it 'raises if the service does not define its rpc methods' do
- expect { @srv.handle(NoRpcImplementation) }.to raise_error
end
- it 'raises if a handler method is already registered' do
+ it 'should obtain responses for multiple parallel requests' do
@srv.handle(EchoService)
- expect { r.handle(EchoService) }.to raise_error
+ t = Thread.new { @srv.run }
+ @srv.wait_till_running
+ req, q = EchoMsg.new, Queue.new
+ n = 5 # arbitrary
+ threads = []
+ n.times do |x|
+ cq = GRPC::Core::CompletionQueue.new
+ threads << Thread.new do
+ stub = EchoStub.new(@host, **@client_opts)
+ q << stub.an_rpc(req)
+ end
+ end
+ n.times { expect(q.pop).to be_a(EchoMsg) }
+ @srv.stop
+ threads.each { |t| t.join }
end
- end
-
- describe '#run' do
-
- before(:each) do
- @client_opts = {
- :channel_override => @ch
- }
- @marshal = EchoService.rpc_descs[:an_rpc].marshal_proc
- @unmarshal = EchoService.rpc_descs[:an_rpc].unmarshal_proc(:output)
- server_opts = {
+ it 'should return UNAVAILABLE status if there too many jobs' do
+ opts = {
+ :a_channel_arg => 'an_arg',
:server_override => @server,
:completion_queue_override => @server_queue,
- :poll_period => 1
+ :pool_size => 1,
+ :poll_period => 1,
+ :max_waiting_requests => 0
}
- @srv = RpcServer.new(**server_opts)
- end
-
- describe 'when running' do
-
- it 'should return NOT_FOUND status for requests on unknown methods' do
- @srv.handle(EchoService)
- t = Thread.new { @srv.run }
- @srv.wait_till_running
- req = EchoMsg.new
- blk = Proc.new do
- cq = CompletionQueue.new
- stub = ClientStub.new(@host, cq, **@client_opts)
- stub.request_response('/unknown', req, @marshal, @unmarshal)
- end
- expect(&blk).to raise_error BadStatus
- @srv.stop
- t.join
- end
-
- it 'should obtain responses for multiple sequential requests' do
- @srv.handle(EchoService)
- t = Thread.new { @srv.run }
- @srv.wait_till_running
- req = EchoMsg.new
- n = 5 # arbitrary
- stub = EchoStub.new(@host, **@client_opts)
- n.times { |x| expect(stub.an_rpc(req)).to be_a(EchoMsg) }
- @srv.stop
- t.join
- end
-
- it 'should obtain responses for multiple parallel requests' do
- @srv.handle(EchoService)
- t = Thread.new { @srv.run }
- @srv.wait_till_running
- req, q = EchoMsg.new, Queue.new
- n = 5 # arbitrary
- threads = []
- n.times do |x|
- cq = CompletionQueue.new
- threads << Thread.new do
- stub = EchoStub.new(@host, **@client_opts)
- q << stub.an_rpc(req)
- end
- end
- n.times { expect(q.pop).to be_a(EchoMsg) }
- @srv.stop
- threads.each { |t| t.join }
- end
-
- it 'should return UNAVAILABLE status if there too many jobs' do
- opts = {
- :a_channel_arg => 'an_arg',
- :server_override => @server,
- :completion_queue_override => @server_queue,
- :pool_size => 1,
- :poll_period => 1,
- :max_waiting_requests => 0
- }
- alt_srv = RpcServer.new(**opts)
- alt_srv.handle(SlowService)
- t = Thread.new { alt_srv.run }
- alt_srv.wait_till_running
- req = EchoMsg.new
- n = 5 # arbitrary, use as many to ensure the server pool is exceeded
- threads = []
- _1_failed_as_unavailable = false
- n.times do |x|
- threads << Thread.new do
- cq = CompletionQueue.new
- stub = SlowStub.new(@host, **@client_opts)
- begin
- stub.an_rpc(req)
- rescue BadStatus => e
- _1_failed_as_unavailable = e.code == StatusCodes::UNAVAILABLE
- end
+ alt_srv = RpcServer.new(**opts)
+ alt_srv.handle(SlowService)
+ t = Thread.new { alt_srv.run }
+ alt_srv.wait_till_running
+ req = EchoMsg.new
+ n = 5 # arbitrary, use as many to ensure the server pool is exceeded
+ threads = []
+ _1_failed_as_unavailable = false
+ n.times do |x|
+ threads << Thread.new do
+ cq = GRPC::Core::CompletionQueue.new
+ stub = SlowStub.new(@host, **@client_opts)
+ begin
+ stub.an_rpc(req)
+ rescue BadStatus => e
+ _1_failed_as_unavailable = e.code == StatusCodes::UNAVAILABLE
end
end
- threads.each { |t| t.join }
- alt_srv.stop
- expect(_1_failed_as_unavailable).to be(true)
end
-
+ threads.each { |t| t.join }
+ alt_srv.stop
+ expect(_1_failed_as_unavailable).to be(true)
end
end
diff --git a/src/ruby/spec/generic/service_spec.rb b/src/ruby/spec/generic/service_spec.rb
index 4c76881bcf..dc921d8934 100644
--- a/src/ruby/spec/generic/service_spec.rb
+++ b/src/ruby/spec/generic/service_spec.rb
@@ -33,7 +33,7 @@ require 'grpc/generic/service'
class GoodMsg
- def marshal
+ def self.marshal(o)
''
end
@@ -43,7 +43,7 @@ class GoodMsg
end
class EncodeDecodeMsg
- def encode
+ def self.encode(o)
''
end
@@ -53,7 +53,6 @@ class EncodeDecodeMsg
end
GenericService = GRPC::GenericService
-RpcDesc = GRPC::RpcDesc
Dsl = GenericService::Dsl
@@ -95,7 +94,7 @@ describe GenericService do
end
expect(c.rpc_descs).to include(:AnRpc)
- expect(c.rpc_descs[:AnRpc]).to be_a(RpcDesc)
+ expect(c.rpc_descs[:AnRpc]).to be_a(GRPC::RpcDesc)
end
it 'give subclasses access to #rpc_descs' do
@@ -106,7 +105,7 @@ describe GenericService do
c = Class.new(base) do
end
expect(c.rpc_descs).to include(:AnRpc)
- expect(c.rpc_descs[:AnRpc]).to be_a(RpcDesc)
+ expect(c.rpc_descs[:AnRpc]).to be_a(GRPC::RpcDesc)
end
end
@@ -189,7 +188,7 @@ describe GenericService do
blk = Proc.new do
Class.new do
include GenericService
- self.marshal_instance_method = :encode
+ self.marshal_class_method = :encode
self.unmarshal_class_method = :decode
rpc :AnRpc, EncodeDecodeMsg, EncodeDecodeMsg
end
diff --git a/src/ruby/spec/metadata_spec.rb b/src/ruby/spec/metadata_spec.rb
index 8465a40fab..d5dc8b2338 100644
--- a/src/ruby/spec/metadata_spec.rb
+++ b/src/ruby/spec/metadata_spec.rb
@@ -29,24 +29,23 @@
require 'grpc'
-describe GRPC::Metadata do
+describe GRPC::Core::Metadata do
describe '#new' do
it 'should create instances' do
- expect { GRPC::Metadata.new('a key', 'a value') }.to_not raise_error
- expect(GRPC::Metadata.new('a key', 'a value')).to be_a(GRPC::Metadata)
+ expect { GRPC::Core::Metadata.new('a key', 'a value') }.to_not raise_error
end
end
describe '#key' do
- md = GRPC::Metadata.new('a key', 'a value')
+ md = GRPC::Core::Metadata.new('a key', 'a value')
it 'should be the constructor value' do
expect(md.key).to eq('a key')
end
end
describe '#value' do
- md = GRPC::Metadata.new('a key', 'a value')
+ md = GRPC::Core::Metadata.new('a key', 'a value')
it 'should be the constuctor value' do
expect(md.value).to eq('a value')
end
@@ -54,12 +53,12 @@ describe GRPC::Metadata do
describe '#dup' do
it 'should create a copy that returns the correct key' do
- md = GRPC::Metadata.new('a key', 'a value')
+ md = GRPC::Core::Metadata.new('a key', 'a value')
expect(md.dup.key).to eq('a key')
end
it 'should create a copy that returns the correct value' do
- md = GRPC::Metadata.new('a key', 'a value')
+ md = GRPC::Core::Metadata.new('a key', 'a value')
expect(md.dup.value).to eq('a value')
end
end
diff --git a/src/ruby/spec/server_credentials_spec.rb b/src/ruby/spec/server_credentials_spec.rb
new file mode 100644
index 0000000000..bcc2cae4dd
--- /dev/null
+++ b/src/ruby/spec/server_credentials_spec.rb
@@ -0,0 +1,74 @@
+# 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.
+
+require 'grpc'
+
+def load_test_certs
+ test_root = File.join(File.dirname(__FILE__), 'testdata')
+ files = ['ca.pem', 'server1.pem', 'server1.key']
+ files.map { |f| File.open(File.join(test_root, f)).read }
+end
+
+
+describe GRPC::Core::ServerCredentials do
+
+ Creds = GRPC::Core::ServerCredentials
+
+ describe '#new' do
+
+ it 'can be constructed from a fake CA PEM, server PEM and a server key' do
+ expect { Creds.new('a', 'b', 'c') }.not_to raise_error
+ end
+
+ it 'can be constructed using the test certificates' do
+ certs = load_test_certs
+ expect { Creds.new(*certs) }.not_to raise_error
+ end
+
+ it 'cannot be constructed without a server cert chain' do
+ root_cert, server_key, _ = load_test_certs
+ blk = Proc.new { Creds.new(root_cert, server_key, nil) }
+ expect(&blk).to raise_error
+ end
+
+ it 'cannot be constructed without a server key' do
+ root_cert, server_key, _ = load_test_certs
+ blk = Proc.new { Creds.new(root_cert, _, cert_chain) }
+ expect(&blk).to raise_error
+ end
+
+ it 'can be constructed without a root_cret' do
+ _, server_key, cert_chain = load_test_certs
+ blk = Proc.new { Creds.new(_, server_key, cert_chain) }
+ expect(&blk).to_not raise_error
+ end
+
+ end
+
+end
diff --git a/src/ruby/spec/server_spec.rb b/src/ruby/spec/server_spec.rb
index 598b7cfa7b..28f520a2f6 100644
--- a/src/ruby/spec/server_spec.rb
+++ b/src/ruby/spec/server_spec.rb
@@ -30,72 +30,84 @@
require 'grpc'
require 'port_picker'
-module GRPC
+def load_test_certs
+ test_root = File.join(File.dirname(__FILE__), 'testdata')
+ files = ['ca.pem', 'server1.key', 'server1.pem']
+ files.map { |f| File.open(File.join(test_root, f)).read }
+end
- describe Server do
+Server = GRPC::Core::Server
- before(:each) do
- @cq = CompletionQueue.new
- end
+describe Server do
- describe '#start' do
+ def create_test_cert
+ GRPC::Core::ServerCredentials.new(*load_test_certs)
+ end
- it 'runs without failing' do
- blk = Proc.new do
- s = Server.new(@cq, nil).start
- end
- expect(&blk).to_not raise_error
- end
+ before(:each) do
+ @cq = GRPC::Core::CompletionQueue.new
+ end
- it 'fails if the server is closed' do
- s = Server.new(@cq, nil)
- s.close
- expect { s.start }.to raise_error(RuntimeError)
+ describe '#start' do
+
+ it 'runs without failing' do
+ blk = Proc.new do
+ s = Server.new(@cq, nil).start
end
+ expect(&blk).to_not raise_error
+ end
+ it 'fails if the server is closed' do
+ s = Server.new(@cq, nil)
+ s.close
+ expect { s.start }.to raise_error(RuntimeError)
end
- describe '#destroy' do
- it 'destroys a server ok' do
- s = start_a_server
- blk = Proc.new { s.destroy }
- expect(&blk).to_not raise_error
- end
+ end
- it 'can be called more than once without error' do
- s = start_a_server
- begin
- blk = Proc.new { s.destroy }
- expect(&blk).to_not raise_error
- blk.call
- expect(&blk).to_not raise_error
- ensure
- s.close
- end
- end
+ describe '#destroy' do
+ it 'destroys a server ok' do
+ s = start_a_server
+ blk = Proc.new { s.destroy }
+ expect(&blk).to_not raise_error
end
- describe '#close' do
- it 'closes a server ok' do
- s = start_a_server
- begin
- blk = Proc.new { s.close }
- expect(&blk).to_not raise_error
- ensure
- s.close
- end
+ it 'can be called more than once without error' do
+ s = start_a_server
+ begin
+ blk = Proc.new { s.destroy }
+ expect(&blk).to_not raise_error
+ blk.call
+ expect(&blk).to_not raise_error
+ ensure
+ s.close
end
+ end
+ end
- it 'can be called more than once without error' do
- s = start_a_server
+ describe '#close' do
+ it 'closes a server ok' do
+ s = start_a_server
+ begin
blk = Proc.new { s.close }
expect(&blk).to_not raise_error
- blk.call
- expect(&blk).to_not raise_error
+ ensure
+ s.close
end
end
- describe '#add_http_port' do
+ it 'can be called more than once without error' do
+ s = start_a_server
+ blk = Proc.new { s.close }
+ expect(&blk).to_not raise_error
+ blk.call
+ expect(&blk).to_not raise_error
+ end
+ end
+
+ describe '#add_http_port' do
+
+ describe 'for insecure servers' do
it 'runs without failing' do
blk = Proc.new do
@@ -114,72 +126,108 @@ module GRPC
end
- describe '#new' do
+ describe 'for secure servers' do
- it 'takes a completion queue with nil channel args' do
- expect { Server.new(@cq, nil) }.to_not raise_error
+ it 'runs without failing' do
+ blk = Proc.new do
+ s = Server.new(@cq, nil)
+ s.add_http2_port('localhost:0', true)
+ s.close
+ end
+ expect(&blk).to_not raise_error
end
- it 'does not take a hash with bad keys as channel args' do
- blk = construct_with_args(Object.new => 1)
- expect(&blk).to raise_error TypeError
- blk = construct_with_args(1 => 1)
- expect(&blk).to raise_error TypeError
+ it 'fails if the server is closed' do
+ s = Server.new(@cq, nil)
+ s.close
+ blk = Proc.new { s.add_http2_port('localhost:0', true) }
+ expect(&blk).to raise_error(RuntimeError)
end
- it 'does not take a hash with bad values as channel args' do
- blk = construct_with_args(:symbol => Object.new)
- expect(&blk).to raise_error TypeError
- blk = construct_with_args('1' => Hash.new)
- expect(&blk).to raise_error TypeError
- end
+ end
- it 'can take a hash with a symbol key as channel args' do
- blk = construct_with_args(:a_symbol => 1)
- expect(&blk).to_not raise_error
- end
+ end
- it 'can take a hash with a string key as channel args' do
- blk = construct_with_args('a_symbol' => 1)
- expect(&blk).to_not raise_error
- end
+ shared_examples '#new' do
- it 'can take a hash with a string value as channel args' do
- blk = construct_with_args(:a_symbol => '1')
- expect(&blk).to_not raise_error
- end
+ it 'takes a completion queue with nil channel args' do
+ expect { Server.new(@cq, nil, create_test_cert) }.to_not raise_error
+ end
- it 'can take a hash with a symbol value as channel args' do
- blk = construct_with_args(:a_symbol => :another_symbol)
- expect(&blk).to_not raise_error
- end
+ it 'does not take a hash with bad keys as channel args' do
+ blk = construct_with_args(Object.new => 1)
+ expect(&blk).to raise_error TypeError
+ blk = construct_with_args(1 => 1)
+ expect(&blk).to raise_error TypeError
+ end
- it 'can take a hash with a numeric value as channel args' do
- blk = construct_with_args(:a_symbol => 1)
- expect(&blk).to_not raise_error
- end
+ it 'does not take a hash with bad values as channel args' do
+ blk = construct_with_args(:symbol => Object.new)
+ expect(&blk).to raise_error TypeError
+ blk = construct_with_args('1' => Hash.new)
+ expect(&blk).to raise_error TypeError
+ end
- it 'can take a hash with many args as channel args' do
- args = Hash[127.times.collect { |x| [x.to_s, x] } ]
- blk = construct_with_args(args)
- expect(&blk).to_not raise_error
- end
+ it 'can take a hash with a symbol key as channel args' do
+ blk = construct_with_args(:a_symbol => 1)
+ expect(&blk).to_not raise_error
+ end
+
+ it 'can take a hash with a string key as channel args' do
+ blk = construct_with_args('a_symbol' => 1)
+ expect(&blk).to_not raise_error
+ end
+ it 'can take a hash with a string value as channel args' do
+ blk = construct_with_args(:a_symbol => '1')
+ expect(&blk).to_not raise_error
end
+ it 'can take a hash with a symbol value as channel args' do
+ blk = construct_with_args(:a_symbol => :another_symbol)
+ expect(&blk).to_not raise_error
+ end
+
+ it 'can take a hash with a numeric value as channel args' do
+ blk = construct_with_args(:a_symbol => 1)
+ expect(&blk).to_not raise_error
+ end
+
+ it 'can take a hash with many args as channel args' do
+ args = Hash[127.times.collect { |x| [x.to_s, x] } ]
+ blk = construct_with_args(args)
+ expect(&blk).to_not raise_error
+ end
+
+ end
+
+ describe '#new with an insecure channel' do
+
def construct_with_args(a)
Proc.new { Server.new(@cq, a) }
end
- def start_a_server
- port = find_unused_tcp_port
- host = "localhost:#{port}"
- s = Server.new(@cq, nil)
- s.add_http2_port(host)
- s.start
- s
+ it_behaves_like '#new'
+
+ end
+
+ describe '#new with a secure channel' do
+
+ def construct_with_args(a)
+ Proc.new { Server.new(@cq, a, create_test_cert) }
end
+ it_behaves_like '#new'
+
+ end
+
+ def start_a_server
+ port = find_unused_tcp_port
+ host = "localhost:#{port}"
+ s = Server.new(@cq, nil)
+ s.add_http2_port(host)
+ s.start
+ s
end
end
diff --git a/src/ruby/spec/status_spec.rb b/src/ruby/spec/status_spec.rb
index 83d4efc730..63dcefba04 100644
--- a/src/ruby/spec/status_spec.rb
+++ b/src/ruby/spec/status_spec.rb
@@ -29,133 +29,138 @@
require 'grpc'
-module GRPC
-
- describe StatusCodes do
-
- before(:each) do
- @known_types = {
- :OK => 0,
- :CANCELLED => 1,
- :UNKNOWN => 2,
- :INVALID_ARGUMENT => 3,
- :DEADLINE_EXCEEDED => 4,
- :NOT_FOUND => 5,
- :ALREADY_EXISTS => 6,
- :PERMISSION_DENIED => 7,
- :RESOURCE_EXHAUSTED => 8,
- :FAILED_PRECONDITION => 9,
- :ABORTED => 10,
- :OUT_OF_RANGE => 11,
- :UNIMPLEMENTED => 12,
- :INTERNAL => 13,
- :UNAVAILABLE => 14,
- :DATA_LOSS => 15,
- :UNAUTHENTICATED => 16
- }
- end
- it 'should have symbols for all the known status codes' do
- m = StatusCodes
- syms_and_codes = m.constants.collect { |c| [c, m.const_get(c)] }
- expect(Hash[syms_and_codes]).to eq(@known_types)
- end
+describe GRPC::Core::StatusCodes do
+
+ StatusCodes = GRPC::Core::StatusCodes
+
+ before(:each) do
+ @known_types = {
+ :OK => 0,
+ :CANCELLED => 1,
+ :UNKNOWN => 2,
+ :INVALID_ARGUMENT => 3,
+ :DEADLINE_EXCEEDED => 4,
+ :NOT_FOUND => 5,
+ :ALREADY_EXISTS => 6,
+ :PERMISSION_DENIED => 7,
+ :RESOURCE_EXHAUSTED => 8,
+ :FAILED_PRECONDITION => 9,
+ :ABORTED => 10,
+ :OUT_OF_RANGE => 11,
+ :UNIMPLEMENTED => 12,
+ :INTERNAL => 13,
+ :UNAVAILABLE => 14,
+ :DATA_LOSS => 15,
+ :UNAUTHENTICATED => 16
+ }
+ end
+ it 'should have symbols for all the known status codes' do
+ m = StatusCodes
+ syms_and_codes = m.constants.collect { |c| [c, m.const_get(c)] }
+ expect(Hash[syms_and_codes]).to eq(@known_types)
end
- describe Status do
+end
+
+
+describe GRPC::Core::Status do
+
+ Status = GRPC::Core::Status
- describe '#new' do
- it 'should create new instances' do
- expect { Status.new(142, 'test details') }.to_not raise_error
- end
+ describe '#new' do
+ it 'should create new instances' do
+ expect { Status.new(142, 'test details') }.to_not raise_error
end
+ end
- describe '#details' do
- it 'return the detail' do
- sts = Status.new(142, 'test details')
- expect(sts.details).to eq('test details')
- end
+ describe '#details' do
+ it 'return the detail' do
+ sts = Status.new(142, 'test details')
+ expect(sts.details).to eq('test details')
end
+ end
- describe '#code' do
- it 'should return the code' do
- sts = Status.new(142, 'test details')
- expect(sts.code).to eq(142)
- end
+ describe '#code' do
+ it 'should return the code' do
+ sts = Status.new(142, 'test details')
+ expect(sts.code).to eq(142)
end
+ end
- describe '#dup' do
- it 'should create a copy that returns the correct details' do
- sts = Status.new(142, 'test details')
- expect(sts.dup.code).to eq(142)
- end
+ describe '#dup' do
+ it 'should create a copy that returns the correct details' do
+ sts = Status.new(142, 'test details')
+ expect(sts.dup.code).to eq(142)
+ end
- it 'should create a copy that returns the correct code' do
- sts = Status.new(142, 'test details')
- expect(sts.dup.details).to eq('test details')
- end
+ it 'should create a copy that returns the correct code' do
+ sts = Status.new(142, 'test details')
+ expect(sts.dup.details).to eq('test details')
end
+ end
- end
+end
- describe BadStatus do
- describe '#new' do
- it 'should create new instances' do
- expect { BadStatus.new(142, 'test details') }.to_not raise_error
- end
- end
+describe GRPC::BadStatus do
+
+ BadStatus = GRPC::BadStatus
- describe '#details' do
- it 'return the detail' do
- err = BadStatus.new(142, 'test details')
- expect(err.details).to eq('test details')
- end
+ describe '#new' do
+ it 'should create new instances' do
+ expect { BadStatus.new(142, 'test details') }.to_not raise_error
end
+ end
- describe '#code' do
- it 'should return the code' do
- err = BadStatus.new(142, 'test details')
- expect(err.code).to eq(142)
- end
+ describe '#details' do
+ it 'return the detail' do
+ err = BadStatus.new(142, 'test details')
+ expect(err.details).to eq('test details')
end
+ end
- describe '#dup' do
- it 'should create a copy that returns the correct details' do
- err = BadStatus.new(142, 'test details')
- expect(err.dup.code).to eq(142)
- end
+ describe '#code' do
+ it 'should return the code' do
+ err = BadStatus.new(142, 'test details')
+ expect(err.code).to eq(142)
+ end
+ end
- it 'should create a copy that returns the correct code' do
- err = BadStatus.new(142, 'test details')
- expect(err.dup.details).to eq('test details')
- end
+ describe '#dup' do
+ it 'should create a copy that returns the correct details' do
+ err = BadStatus.new(142, 'test details')
+ expect(err.dup.code).to eq(142)
end
- describe '#to_status' do
- it 'should create a Status with the same code and details' do
- err = BadStatus.new(142, 'test details')
- sts = err.to_status
- expect(sts.code).to eq(142)
- expect(sts.details).to eq('test details')
- end
-
- it 'should create a copy that returns the correct code' do
- err = BadStatus.new(142, 'test details')
- expect(err.dup.details).to eq('test details')
- end
+ it 'should create a copy that returns the correct code' do
+ err = BadStatus.new(142, 'test details')
+ expect(err.dup.details).to eq('test details')
end
+ end
- describe 'as an exception' do
+ describe '#to_status' do
+ it 'should create a Status with the same code and details' do
+ err = BadStatus.new(142, 'test details')
+ sts = err.to_status
+ expect(sts.code).to eq(142)
+ expect(sts.details).to eq('test details')
+ end
- it 'can be raised' do
- blk = Proc.new { raise BadStatus.new(343, 'status 343') }
- expect(&blk).to raise_error(BadStatus)
- end
+ it 'should create a copy that returns the correct code' do
+ err = BadStatus.new(142, 'test details')
+ expect(err.dup.details).to eq('test details')
end
+ end
+
+ describe 'as an exception' do
+ it 'can be raised' do
+ blk = Proc.new { raise BadStatus.new(343, 'status 343') }
+ expect(&blk).to raise_error(BadStatus)
+ end
end
end
diff --git a/src/ruby/spec/testdata/README b/src/ruby/spec/testdata/README
new file mode 100755
index 0000000000..ed72661e97
--- /dev/null
+++ b/src/ruby/spec/testdata/README
@@ -0,0 +1,4 @@
+These are test keys *NOT* to be used in production.
+http://go/keyhunt requires this README
+
+CONFIRMEDTESTKEY
diff --git a/src/ruby/spec/testdata/ca.pem b/src/ruby/spec/testdata/ca.pem
new file mode 100755
index 0000000000..6c8511a73c
--- /dev/null
+++ b/src/ruby/spec/testdata/ca.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
+Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
+YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
+BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
+g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
+Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
+HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
+sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
+oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
+Dfcog5wrJytaQ6UA0wE=
+-----END CERTIFICATE-----
diff --git a/src/ruby/spec/testdata/server1.key b/src/ruby/spec/testdata/server1.key
new file mode 100755
index 0000000000..143a5b8765
--- /dev/null
+++ b/src/ruby/spec/testdata/server1.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD
+M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf
+3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY
+AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm
+V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY
+tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p
+dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q
+K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR
+81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff
+DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd
+aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2
+ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3
+XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe
+F98XJ7tIFfJq
+-----END PRIVATE KEY-----
diff --git a/src/ruby/spec/testdata/server1.pem b/src/ruby/spec/testdata/server1.pem
new file mode 100755
index 0000000000..8e582e571f
--- /dev/null
+++ b/src/ruby/spec/testdata/server1.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET
+MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5
+MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
+BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl
+c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs
+JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO
+RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30
+3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL
+BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6
+b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ
+KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS
+wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e
+aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s=
+-----END CERTIFICATE-----
diff --git a/src/ruby/spec/time_consts_spec.rb b/src/ruby/spec/time_consts_spec.rb
index 2bbcac07c5..27755075a9 100644
--- a/src/ruby/spec/time_consts_spec.rb
+++ b/src/ruby/spec/time_consts_spec.rb
@@ -29,67 +29,65 @@
require 'grpc'
-module GRPC
- describe TimeConsts do
+TimeConsts = GRPC::Core::TimeConsts
- before(:each) do
- @known_consts = [:ZERO, :INFINITE_FUTURE, :INFINITE_PAST].sort
- end
-
- it 'should have all the known types' do
- expect(TimeConsts.constants.collect.sort).to eq(@known_consts)
- end
-
- describe "#to_time" do
- it 'converts each constant to a Time' do
- m = TimeConsts
- m.constants.each do |c|
- expect(m.const_get(c).to_time).to be_a(Time)
- end
- end
- end
+describe TimeConsts do
+ before(:each) do
+ @known_consts = [:ZERO, :INFINITE_FUTURE, :INFINITE_PAST].sort
end
- describe '#from_relative_time' do
-
- it 'cannot handle arbitrary objects' do
- expect { TimeConsts.from_relative_time(Object.new) }.to raise_error
- end
+ it 'should have all the known types' do
+ expect(TimeConsts.constants.collect.sort).to eq(@known_consts)
+ end
- it 'preserves TimeConsts' do
+ describe '#to_time' do
+ it 'converts each constant to a Time' do
m = TimeConsts
m.constants.each do |c|
- const = m.const_get(c)
- expect(TimeConsts.from_relative_time(const)).to be(const)
+ expect(m.const_get(c).to_time).to be_a(Time)
end
end
+ end
- it 'converts 0 to TimeConsts::ZERO' do
- expect(TimeConsts.from_relative_time(0)).to eq(TimeConsts::ZERO)
- end
+end
- it 'converts nil to TimeConsts::ZERO' do
- expect(TimeConsts.from_relative_time(nil)).to eq(TimeConsts::ZERO)
- end
+describe '#from_relative_time' do
- it 'converts negative values to TimeConsts::INFINITE_FUTURE' do
- [-1, -3.2, -1e6].each do |t|
- y = TimeConsts.from_relative_time(t)
- expect(y).to eq(TimeConsts::INFINITE_FUTURE)
- end
+ it 'cannot handle arbitrary objects' do
+ expect { TimeConsts.from_relative_time(Object.new) }.to raise_error
+ end
+
+ it 'preserves TimeConsts' do
+ m = TimeConsts
+ m.constants.each do |c|
+ const = m.const_get(c)
+ expect(TimeConsts.from_relative_time(const)).to be(const)
end
+ end
- it 'converts a positive value to an absolute time' do
- epsilon = 1
- [1, 3.2, 1e6].each do |t|
- want = Time.now + t
- abs = TimeConsts.from_relative_time(t)
- expect(abs.to_f).to be_within(epsilon).of(want.to_f)
- end
+ it 'converts 0 to TimeConsts::ZERO' do
+ expect(TimeConsts.from_relative_time(0)).to eq(TimeConsts::ZERO)
+ end
+
+ it 'converts nil to TimeConsts::ZERO' do
+ expect(TimeConsts.from_relative_time(nil)).to eq(TimeConsts::ZERO)
+ end
+
+ it 'converts negative values to TimeConsts::INFINITE_FUTURE' do
+ [-1, -3.2, -1e6].each do |t|
+ y = TimeConsts.from_relative_time(t)
+ expect(y).to eq(TimeConsts::INFINITE_FUTURE)
end
+ end
+ it 'converts a positive value to an absolute time' do
+ epsilon = 1
+ [1, 3.2, 1e6].each do |t|
+ want = Time.now + t
+ abs = TimeConsts.from_relative_time(t)
+ expect(abs.to_f).to be_within(epsilon).of(want.to_f)
+ end
end
end
-