diff options
author | Jorge Canizales <jcanizales@google.com> | 2015-02-17 19:28:18 -0800 |
---|---|---|
committer | Jorge Canizales <jcanizales@google.com> | 2015-02-17 19:28:18 -0800 |
commit | db128617534ace98f7e7e2c4e77d44bd366aae52 (patch) | |
tree | 80baba6ac2f9b49a0c1fe32dd04277ed8fcf095a /src | |
parent | d1a8cc082acac3a01defa365b1ae98bc40c5ea3a (diff) | |
parent | 0a9ae3d23d48667d3fe915b233e4d5cab55be92f (diff) |
Merge branch 'master' of https://github.com/grpc/grpc into import-ios-library
Diffstat (limited to 'src')
120 files changed, 5678 insertions, 2923 deletions
diff --git a/src/core/channel/connected_channel.c b/src/core/channel/connected_channel.c index 61a6caf032..2d61d389e4 100644 --- a/src/core/channel/connected_channel.c +++ b/src/core/channel/connected_channel.c @@ -467,17 +467,11 @@ static void transport_goaway(void *user_data, grpc_transport *transport, /* 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; diff --git a/src/core/iomgr/fd_posix.c b/src/core/iomgr/fd_posix.c index 737ee016aa..cc57830551 100644 --- a/src/core/iomgr/fd_posix.c +++ b/src/core/iomgr/fd_posix.c @@ -104,14 +104,17 @@ static void destroy(grpc_fd *fd) { } static void ref_by(grpc_fd *fd, int n) { - gpr_atm_no_barrier_fetch_add(&fd->refst, n); + GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0); } static void unref_by(grpc_fd *fd, int n) { - if (gpr_atm_full_fetch_add(&fd->refst, -n) == n) { + gpr_atm old = gpr_atm_full_fetch_add(&fd->refst, -n); + if (old == n) { grpc_iomgr_add_callback(fd->on_done, fd->on_done_user_data); freelist_fd(fd); grpc_iomgr_unref(); + } else { + GPR_ASSERT(old > n); } } diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c index 53c9806fb9..1245d22dde 100644 --- a/src/core/iomgr/pollset_posix.c +++ b/src/core/iomgr/pollset_posix.c @@ -214,6 +214,7 @@ static void unary_poll_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) { * unary poller */ grpc_fd_unref(fds[0]); pollset->data.ptr = fd; + grpc_fd_ref(fd); } } diff --git a/src/core/iomgr/resolve_address.c b/src/core/iomgr/resolve_address.c index 575f884d91..e17bcdba0f 100644 --- a/src/core/iomgr/resolve_address.c +++ b/src/core/iomgr/resolve_address.c @@ -39,6 +39,7 @@ #include "src/core/iomgr/resolve_address.h" #include <sys/types.h> +#include <sys/un.h> #include <string.h> #include "src/core/iomgr/iomgr_internal.h" @@ -123,6 +124,19 @@ grpc_resolved_addresses *grpc_blocking_resolve_address( size_t i; grpc_resolved_addresses *addrs = NULL; const gpr_timespec start_time = gpr_now(); + struct sockaddr_un *un; + + if (name[0] == 'u' && name[1] == 'n' && name[2] == 'i' && name[3] == 'x' && + name[4] == ':' && name[5] != 0) { + addrs = gpr_malloc(sizeof(grpc_resolved_addresses)); + addrs->naddrs = 1; + addrs->addrs = gpr_malloc(sizeof(grpc_resolved_address)); + un = (struct sockaddr_un *)addrs->addrs->addr; + un->sun_family = AF_UNIX; + strcpy(un->sun_path, name + 5); + addrs->addrs->len = strlen(un->sun_path) + sizeof(un->sun_family); + return addrs; + } /* parse name, splitting it into host and port parts */ split_host_port(name, &host, &port); diff --git a/src/core/iomgr/sockaddr_utils.c b/src/core/iomgr/sockaddr_utils.c index 8dcfca74c6..f794241776 100644 --- a/src/core/iomgr/sockaddr_utils.c +++ b/src/core/iomgr/sockaddr_utils.c @@ -166,6 +166,8 @@ int grpc_sockaddr_get_port(const struct sockaddr *addr) { return ntohs(((struct sockaddr_in *)addr)->sin_port); case AF_INET6: return ntohs(((struct sockaddr_in6 *)addr)->sin6_port); + case AF_UNIX: + return 1; default: gpr_log(GPR_ERROR, "Unknown socket family %d in %s", addr->sa_family, __FUNCTION__); diff --git a/src/core/iomgr/socket_utils_linux.c b/src/core/iomgr/socket_utils_linux.c index 7ef58940c2..f3c22187d7 100644 --- a/src/core/iomgr/socket_utils_linux.c +++ b/src/core/iomgr/socket_utils_linux.c @@ -31,12 +31,9 @@ * */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif #include <grpc/support/port_platform.h> -#ifdef GPR_LINUX +#ifdef GPR_LINUX_SOCKETUTILS #include "src/core/iomgr/socket_utils_posix.h" diff --git a/src/core/iomgr/tcp_client_posix.c b/src/core/iomgr/tcp_client_posix.c index 851530ce68..6dc7997833 100644 --- a/src/core/iomgr/tcp_client_posix.c +++ b/src/core/iomgr/tcp_client_posix.c @@ -62,13 +62,13 @@ typedef struct { int refs; } async_connect; -static int prepare_socket(int fd) { +static int prepare_socket(const struct sockaddr *addr, int fd) { if (fd < 0) { goto error; } if (!grpc_set_socket_nonblocking(fd, 1) || !grpc_set_socket_cloexec(fd, 1) || - !grpc_set_socket_low_latency(fd, 1)) { + (addr->sa_family != AF_UNIX && !grpc_set_socket_low_latency(fd, 1))) { gpr_log(GPR_ERROR, "Unable to configure socket %d: %s", fd, strerror(errno)); goto error; @@ -200,7 +200,7 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep), addr = (struct sockaddr *)&addr4_copy; addr_len = sizeof(addr4_copy); } - if (!prepare_socket(fd)) { + if (!prepare_socket(addr, fd)) { cb(arg, NULL); return; } diff --git a/src/core/iomgr/tcp_server_posix.c b/src/core/iomgr/tcp_server_posix.c index 091f0aab1a..9e5076efc7 100644 --- a/src/core/iomgr/tcp_server_posix.c +++ b/src/core/iomgr/tcp_server_posix.c @@ -42,18 +42,21 @@ #include "src/core/iomgr/tcp_server.h" -#include <limits.h> +#include <errno.h> #include <fcntl.h> +#include <limits.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <stdio.h> -#include <sys/types.h> +#include <string.h> #include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/un.h> #include <unistd.h> -#include <string.h> -#include <errno.h> #include "src/core/iomgr/pollset_posix.h" +#include "src/core/iomgr/resolve_address.h" #include "src/core/iomgr/sockaddr_utils.h" #include "src/core/iomgr/socket_utils_posix.h" #include "src/core/iomgr/tcp_posix.h" @@ -73,8 +76,22 @@ typedef struct { int fd; grpc_fd *emfd; grpc_tcp_server *server; + union { + gpr_uint8 untyped[GRPC_MAX_SOCKADDR_SIZE]; + struct sockaddr sockaddr; + struct sockaddr_un un; + } addr; + int addr_len; } server_port; +static void unlink_if_unix_domain_socket(const struct sockaddr_un *un) { + struct stat st; + + if (stat(un->sun_path, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) { + unlink(un->sun_path); + } +} + /* the overall server */ struct grpc_tcp_server { grpc_tcp_server_cb cb; @@ -121,6 +138,9 @@ void grpc_tcp_server_destroy(grpc_tcp_server *s) { /* delete ALL the things */ for (i = 0; i < s->nports; i++) { server_port *sp = &s->ports[i]; + if (sp->addr.sockaddr.sa_family == AF_UNIX) { + unlink_if_unix_domain_socket(&sp->addr.un); + } grpc_fd_orphan(sp->emfd, NULL, NULL); } gpr_free(s->ports); @@ -170,8 +190,8 @@ static int prepare_socket(int fd, const struct sockaddr *addr, int addr_len) { } if (!grpc_set_socket_nonblocking(fd, 1) || !grpc_set_socket_cloexec(fd, 1) || - !grpc_set_socket_low_latency(fd, 1) || - !grpc_set_socket_reuse_addr(fd, 1)) { + (addr->sa_family != AF_UNIX && (!grpc_set_socket_low_latency(fd, 1) || + !grpc_set_socket_reuse_addr(fd, 1)))) { gpr_log(GPR_ERROR, "Unable to configure socket %d: %s", fd, strerror(errno)); goto error; @@ -265,6 +285,8 @@ static int add_socket_to_server(grpc_tcp_server *s, int fd, sp->server = s; sp->fd = fd; sp->emfd = grpc_fd_create(fd); + memcpy(sp->addr.untyped, addr, addr_len); + sp->addr_len = addr_len; GPR_ASSERT(sp->emfd); gpr_mu_unlock(&s->mu); } @@ -288,6 +310,10 @@ int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, socklen_t sockname_len; int port; + if (((struct sockaddr *)addr)->sa_family == AF_UNIX) { + unlink_if_unix_domain_socket(addr); + } + /* Check if this is a wildcard port, and if so, try to keep the port the same as some previously created listener. */ if (grpc_sockaddr_get_port(addr) == 0) { diff --git a/src/core/security/security_context.c b/src/core/security/security_context.c index adb0269792..1909617614 100644 --- a/src/core/security/security_context.c +++ b/src/core/security/security_context.c @@ -349,11 +349,13 @@ static grpc_security_status ssl_channel_check_peer(grpc_security_context *ctx, void *user_data) { grpc_ssl_channel_security_context *c = (grpc_ssl_channel_security_context *)ctx; - grpc_security_status status = ssl_check_peer(c->overridden_target_name != NULL - ? c->overridden_target_name - : c->target_name, - &peer); + grpc_security_status status; + tsi_peer_destruct(&c->peer); c->peer = peer; + status = ssl_check_peer(c->overridden_target_name != NULL + ? c->overridden_target_name + : c->target_name, + &peer); return status; } diff --git a/src/core/support/file_posix.c b/src/core/support/file_posix.c index cb48b3d52f..e1765666db 100644 --- a/src/core/support/file_posix.c +++ b/src/core/support/file_posix.c @@ -31,19 +31,6 @@ * */ -/* Posix code for gpr fdopen and mkstemp support. */ - -#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 200112L -#undef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200112L -#endif - -/* Don't know why I have to do this for mkstemp, looks like _POSIX_C_SOURCE - should be enough... */ -#ifndef _BSD_SOURCE -#define _BSD_SOURCE -#endif - #include <grpc/support/port_platform.h> #ifdef GPR_POSIX_FILE diff --git a/src/core/support/log_posix.c b/src/core/support/log_posix.c index 05f45de130..36479baeed 100644 --- a/src/core/support/log_posix.c +++ b/src/core/support/log_posix.c @@ -31,16 +31,6 @@ * */ -#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 200112L -#undef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200112L -#endif - -/* FIXME: "posix" files probably shouldn't depend on _GNU_SOURCE */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - #include <grpc/support/port_platform.h> #if defined(GPR_POSIX_LOG) diff --git a/src/core/support/string_posix.c b/src/core/support/string_posix.c index a6bb8058e6..b6f0cd4af0 100644 --- a/src/core/support/string_posix.c +++ b/src/core/support/string_posix.c @@ -31,13 +31,6 @@ * */ -/* Posix code for gpr snprintf support. */ - -#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 200112L -#undef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200112L -#endif - #include <grpc/support/port_platform.h> #ifdef GPR_POSIX_STRING diff --git a/src/core/support/sync_posix.c b/src/core/support/sync_posix.c index a28a4c6bf4..94fc1b0bec 100644 --- a/src/core/support/sync_posix.c +++ b/src/core/support/sync_posix.c @@ -31,13 +31,6 @@ * */ -/* Posix gpr synchroization support code. */ - -#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 199309L -#undef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 199309L -#endif - #include <grpc/support/port_platform.h> #ifdef GPR_POSIX_SYNC diff --git a/src/core/support/time_posix.c b/src/core/support/time_posix.c index 7f0f028183..4af537d974 100644 --- a/src/core/support/time_posix.c +++ b/src/core/support/time_posix.c @@ -31,14 +31,6 @@ * */ -/* Posix code for gpr time support. */ - -/* So we get nanosleep and clock_* */ -#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 199309L -#undef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 199309L -#endif - #include <grpc/support/port_platform.h> #ifdef GPR_POSIX_TIME @@ -70,7 +62,9 @@ gpr_timespec gpr_now(void) { } #else /* For some reason Apple's OSes haven't implemented clock_gettime. */ -/* TODO(klempner): Add special handling for Apple. */ + +#include <sys/time.h> + gpr_timespec gpr_now(void) { gpr_timespec now; struct timeval now_tv; diff --git a/src/core/surface/call.c b/src/core/surface/call.c index 0af2154842..58a2436937 100644 --- a/src/core/surface/call.c +++ b/src/core/surface/call.c @@ -514,32 +514,32 @@ static void finish_ioreq_op(grpc_call *call, grpc_ioreq_op op, } } -static void finish_send_op(grpc_call *call, grpc_ioreq_op op, +static void finish_send_op(grpc_call *call, grpc_ioreq_op op, write_state ws, grpc_op_error error) { lock(call); finish_ioreq_op(call, op, error); call->sending = 0; + call->write_state = ws; unlock(call); grpc_call_internal_unref(call, 0); } static void finish_write_step(void *pc, grpc_op_error error) { - finish_send_op(pc, GRPC_IOREQ_SEND_MESSAGE, error); + finish_send_op(pc, GRPC_IOREQ_SEND_MESSAGE, WRITE_STATE_STARTED, error); } static void finish_finish_step(void *pc, grpc_op_error error) { - finish_send_op(pc, GRPC_IOREQ_SEND_CLOSE, error); + finish_send_op(pc, GRPC_IOREQ_SEND_CLOSE, WRITE_STATE_WRITE_CLOSED, error); } static void finish_start_step(void *pc, grpc_op_error error) { - finish_send_op(pc, GRPC_IOREQ_SEND_INITIAL_METADATA, error); + finish_send_op(pc, GRPC_IOREQ_SEND_INITIAL_METADATA, WRITE_STATE_STARTED, error); } static send_action choose_send_action(grpc_call *call) { switch (call->write_state) { case WRITE_STATE_INITIAL: if (is_op_live(call, GRPC_IOREQ_SEND_INITIAL_METADATA)) { - call->write_state = WRITE_STATE_STARTED; if (is_op_live(call, GRPC_IOREQ_SEND_MESSAGE) || is_op_live(call, GRPC_IOREQ_SEND_CLOSE)) { return SEND_BUFFERED_INITIAL_METADATA; } else { @@ -555,7 +555,6 @@ static send_action choose_send_action(grpc_call *call) { return SEND_MESSAGE; } } else if (is_op_live(call, GRPC_IOREQ_SEND_CLOSE)) { - call->write_state = WRITE_STATE_WRITE_CLOSED; finish_ioreq_op(call, GRPC_IOREQ_SEND_TRAILING_METADATA, GRPC_OP_OK); finish_ioreq_op(call, GRPC_IOREQ_SEND_STATUS, GRPC_OP_OK); if (call->is_client) { @@ -992,6 +991,12 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, const grpc_op *op; grpc_ioreq *req; + if (nops == 0) { + grpc_cq_begin_op(call->cq, call, GRPC_OP_COMPLETE); + grpc_cq_end_op_complete(call->cq, tag, call, do_nothing, NULL, GRPC_OP_OK); + return GRPC_CALL_OK; + } + /* rewrite batch ops into ioreq ops */ for (in = 0, out = 0; in < nops; in++) { op = &ops[in]; diff --git a/src/core/surface/init.c b/src/core/surface/init.c index b5019eb03f..4d639fcbce 100644 --- a/src/core/surface/init.c +++ b/src/core/surface/init.c @@ -35,12 +35,32 @@ #include "src/core/statistics/census_interface.h" #include "src/core/iomgr/iomgr.h" +static gpr_once g_init = GPR_ONCE_INIT; +static gpr_mu g_init_mu; +static int g_initializations; + +static void do_init() { + gpr_mu_init(&g_init_mu); + g_initializations = 0; +} + void grpc_init(void) { - grpc_iomgr_init(); - census_init(); + gpr_once_init(&g_init, do_init); + + gpr_mu_lock(&g_init_mu); + if (++g_initializations == 1) { + grpc_iomgr_init(); + census_init(); + } + gpr_mu_unlock(&g_init_mu); } void grpc_shutdown(void) { - grpc_iomgr_shutdown(); - census_shutdown(); + gpr_mu_lock(&g_init_mu); + if (--g_initializations == 0) { + grpc_iomgr_shutdown(); + census_shutdown(); + } + gpr_mu_unlock(&g_init_mu); } + diff --git a/src/core/transport/chttp2_transport.c b/src/core/transport/chttp2_transport.c index ea579cf4a5..5921c3806e 100644 --- a/src/core/transport/chttp2_transport.c +++ b/src/core/transport/chttp2_transport.c @@ -184,11 +184,13 @@ struct transport { gpr_uint8 is_client; gpr_mu mu; + gpr_cv cv; /* basic state management - what are we doing at the moment? */ gpr_uint8 reading; gpr_uint8 writing; gpr_uint8 calling_back; + gpr_uint8 destroying; error_state error_state; /* stream indexing */ @@ -362,6 +364,7 @@ static void unref_transport(transport *t) { gpr_mu_unlock(&t->mu); gpr_mu_destroy(&t->mu); + gpr_cv_destroy(&t->cv); /* callback remaining pings: they're not allowed to call into the transpot, and maybe they hold resources that need to be freed */ @@ -397,6 +400,7 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup, /* one ref is for destroy, the other for when ep becomes NULL */ gpr_ref_init(&t->refs, 2); gpr_mu_init(&t->mu); + gpr_cv_init(&t->cv); t->metadata_context = mdctx; t->str_grpc_timeout = grpc_mdstr_from_string(t->metadata_context, "grpc-timeout"); @@ -405,6 +409,7 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup, t->error_state = ERROR_STATE_NONE; t->next_stream_id = is_client ? 1 : 2; t->last_incoming_stream_id = 0; + t->destroying = 0; t->is_client = is_client; t->outgoing_window = DEFAULT_WINDOW; t->incoming_window = DEFAULT_WINDOW; @@ -487,6 +492,7 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup, t->cb = sr.callbacks; t->cb_user_data = sr.user_data; t->calling_back = 0; + if (t->destroying) gpr_cv_signal(&t->cv); unlock(t); unref_transport(t); } @@ -495,6 +501,10 @@ static void destroy_transport(grpc_transport *gt) { transport *t = (transport *)gt; gpr_mu_lock(&t->mu); + t->destroying = 1; + while (t->calling_back) { + gpr_cv_wait(&t->cv, &t->mu, gpr_inf_future); + } t->cb = NULL; gpr_mu_unlock(&t->mu); @@ -684,10 +694,11 @@ static void unlock(transport *t) { int i; pending_goaway *goaways = NULL; grpc_endpoint *ep = t->ep; - grpc_stream_op_buffer nuke_now = t->nuke_later_sopb; - - if (nuke_now.nops) { - memset(&t->nuke_later_sopb, 0, sizeof(t->nuke_later_sopb)); + grpc_stream_op_buffer nuke_now; + + grpc_sopb_init(&nuke_now); + if (t->nuke_later_sopb.nops) { + grpc_sopb_swap(&nuke_now, &t->nuke_later_sopb); } /* see if we need to trigger a write - and if so, get the data ready */ @@ -753,13 +764,12 @@ static void unlock(transport *t) { if (perform_callbacks || call_closed || num_goaways) { lock(t); t->calling_back = 0; + if (t->destroying) gpr_cv_signal(&t->cv); unlock(t); unref_transport(t); } - if (nuke_now.nops) { - grpc_sopb_destroy(&nuke_now); - } + grpc_sopb_destroy(&nuke_now); gpr_free(goaways); } @@ -1698,13 +1708,10 @@ static grpc_stream_state compute_state(gpr_uint8 write_closed, static int prepare_callbacks(transport *t) { stream *s; - grpc_stream_op_buffer temp_sopb; int n = 0; while ((s = stream_list_remove_head(t, PENDING_CALLBACKS))) { int execute = 1; - temp_sopb = s->parser.incoming_sopb; - s->parser.incoming_sopb = s->callback_sopb; - s->callback_sopb = temp_sopb; + grpc_sopb_swap(&s->parser.incoming_sopb, &s->callback_sopb); s->callback_state = compute_state(s->sent_write_closed, s->read_closed); if (s->callback_state == GRPC_STREAM_CLOSED) { diff --git a/src/core/transport/stream_op.c b/src/core/transport/stream_op.c index 555543fc4b..03a338bd9b 100644 --- a/src/core/transport/stream_op.c +++ b/src/core/transport/stream_op.c @@ -38,23 +38,19 @@ #include <string.h> -/* Initial number of operations to allocate */ -#define INITIAL_SLOTS 8 /* Exponential growth function: Given x, return a larger x. - Currently we grow by 1.5 times upon reallocation. - Assumes INITIAL_SLOTS > 1 */ + Currently we grow by 1.5 times upon reallocation. */ #define GROW(x) (3 * (x) / 2) void grpc_sopb_init(grpc_stream_op_buffer *sopb) { - sopb->ops = gpr_malloc(sizeof(grpc_stream_op) * INITIAL_SLOTS); - GPR_ASSERT(sopb->ops); + sopb->ops = sopb->inlined_ops; sopb->nops = 0; - sopb->capacity = INITIAL_SLOTS; + sopb->capacity = GRPC_SOPB_INLINE_ELEMENTS; } void grpc_sopb_destroy(grpc_stream_op_buffer *sopb) { grpc_stream_ops_unref_owned_objects(sopb->ops, sopb->nops); - gpr_free(sopb->ops); + if (sopb->ops != sopb->inlined_ops) gpr_free(sopb->ops); } void grpc_sopb_reset(grpc_stream_op_buffer *sopb) { @@ -62,6 +58,19 @@ void grpc_sopb_reset(grpc_stream_op_buffer *sopb) { sopb->nops = 0; } +void grpc_sopb_swap(grpc_stream_op_buffer *a, grpc_stream_op_buffer *b) { + grpc_stream_op_buffer temp = *a; + *a = *b; + *b = temp; + + if (a->ops == b->inlined_ops) { + a->ops = a->inlined_ops; + } + if (b->ops == a->inlined_ops) { + b->ops = b->inlined_ops; + } +} + void grpc_stream_ops_unref_owned_objects(grpc_stream_op *ops, size_t nops) { size_t i; for (i = 0; i < nops; i++) { @@ -84,17 +93,21 @@ void grpc_stream_ops_unref_owned_objects(grpc_stream_op *ops, size_t nops) { } } -static void expand(grpc_stream_op_buffer *sopb) { - sopb->capacity = GROW(sopb->capacity); - sopb->ops = gpr_realloc(sopb->ops, sizeof(grpc_stream_op) * sopb->capacity); - GPR_ASSERT(sopb->ops); +static void expandto(grpc_stream_op_buffer *sopb, size_t new_capacity) { + sopb->capacity = new_capacity; + if (sopb->ops == sopb->inlined_ops) { + sopb->ops = gpr_malloc(sizeof(grpc_stream_op) * new_capacity); + memcpy(sopb->ops, sopb->inlined_ops, sopb->nops * sizeof(grpc_stream_op)); + } else { + sopb->ops = gpr_realloc(sopb->ops, sizeof(grpc_stream_op) * new_capacity); + } } static grpc_stream_op *add(grpc_stream_op_buffer *sopb) { grpc_stream_op *out; if (sopb->nops == sopb->capacity) { - expand(sopb); + expandto(sopb, GROW(sopb->capacity)); } out = sopb->ops + sopb->nops; sopb->nops++; @@ -152,12 +165,7 @@ void grpc_sopb_append(grpc_stream_op_buffer *sopb, grpc_stream_op *ops, size_t new_nops = orig_nops + nops; if (new_nops > sopb->capacity) { - size_t new_capacity = GROW(sopb->capacity); - if (new_capacity < new_nops) { - new_capacity = new_nops; - } - sopb->ops = gpr_realloc(sopb->ops, sizeof(grpc_stream_op) * new_capacity); - sopb->capacity = new_capacity; + expandto(sopb, GPR_MAX(GROW(sopb->capacity), new_nops)); } memcpy(sopb->ops + orig_nops, ops, sizeof(grpc_stream_op) * nops); diff --git a/src/core/transport/stream_op.h b/src/core/transport/stream_op.h index 20d609133f..d4d7515c4f 100644 --- a/src/core/transport/stream_op.h +++ b/src/core/transport/stream_op.h @@ -40,6 +40,9 @@ #include <grpc/support/time.h> #include "src/core/transport/metadata.h" +/* this many stream ops are inlined into a sopb before allocating */ +#define GRPC_SOPB_INLINE_ELEMENTS 16 + /* Operations that can be performed on a stream. Used by grpc_stream_op. */ typedef enum grpc_stream_op_code { @@ -96,6 +99,7 @@ typedef struct grpc_stream_op_buffer { grpc_stream_op *ops; size_t nops; size_t capacity; + grpc_stream_op inlined_ops[GRPC_SOPB_INLINE_ELEMENTS]; } grpc_stream_op_buffer; /* Initialize a stream op buffer */ @@ -104,6 +108,8 @@ void grpc_sopb_init(grpc_stream_op_buffer *sopb); void grpc_sopb_destroy(grpc_stream_op_buffer *sopb); /* Reset a sopb to no elements */ void grpc_sopb_reset(grpc_stream_op_buffer *sopb); +/* Swap two sopbs */ +void grpc_sopb_swap(grpc_stream_op_buffer *a, grpc_stream_op_buffer *b); void grpc_stream_ops_unref_owned_objects(grpc_stream_op *ops, size_t nops); diff --git a/src/csharp/GrpcApi/MathExamples.cs b/src/csharp/GrpcApi/MathExamples.cs index 43f0cedef6..07bcc9c9cd 100644 --- a/src/csharp/GrpcApi/MathExamples.cs +++ b/src/csharp/GrpcApi/MathExamples.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Threading.Tasks; using System.Collections.Generic; diff --git a/src/csharp/GrpcApi/MathGrpc.cs b/src/csharp/GrpcApi/MathGrpc.cs index 520fec437a..606e7f0239 100644 --- a/src/csharp/GrpcApi/MathGrpc.cs +++ b/src/csharp/GrpcApi/MathGrpc.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Threading; using System.Threading.Tasks; diff --git a/src/csharp/GrpcApi/MathServiceImpl.cs b/src/csharp/GrpcApi/MathServiceImpl.cs index 27abc4ce17..ffd794d6c6 100644 --- a/src/csharp/GrpcApi/MathServiceImpl.cs +++ b/src/csharp/GrpcApi/MathServiceImpl.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Threading; using System.Threading.Tasks; diff --git a/src/csharp/GrpcApi/proto/test.proto b/src/csharp/GrpcApi/proto/test.proto index 8380ebb31d..996f11aa6d 100644 --- a/src/csharp/GrpcApi/proto/test.proto +++ b/src/csharp/GrpcApi/proto/test.proto @@ -14,7 +14,7 @@ service TestService { rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); // One request followed by one response. - // The server returns the client payload as-is. + // TODO(Issue 527): Describe required server behavior. rpc UnaryCall(SimpleRequest) returns (SimpleResponse); // One request followed by a sequence of responses (streamed download). diff --git a/src/csharp/GrpcApiTests/MathClientServerTests.cs b/src/csharp/GrpcApiTests/MathClientServerTests.cs index aa78b698e8..bb3f75d4ac 100644 --- a/src/csharp/GrpcApiTests/MathClientServerTests.cs +++ b/src/csharp/GrpcApiTests/MathClientServerTests.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using NUnit.Framework; using Google.GRPC.Core; @@ -13,7 +46,7 @@ namespace math.Tests /// </summary> public class MathClientServerTest { - string serverAddr = "localhost:" + PortPicker.PickUnusedPort(); + string host = "localhost"; Server server; Channel channel; MathGrpc.IMathServiceClient client; @@ -21,11 +54,13 @@ namespace math.Tests [TestFixtureSetUp] public void Init() { + GrpcEnvironment.Initialize(); + server = new Server(); server.AddServiceDefinition(MathGrpc.BindService(new MathServiceImpl())); - server.AddPort(serverAddr); + int port = server.AddPort(host + ":0"); server.Start(); - channel = new Channel(serverAddr); + channel = new Channel(host + ":" + port); client = MathGrpc.NewStub(channel); } diff --git a/src/csharp/GrpcCore/Call.cs b/src/csharp/GrpcCore/Call.cs index 66e7004180..181210902f 100644 --- a/src/csharp/GrpcCore/Call.cs +++ b/src/csharp/GrpcCore/Call.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using Google.GRPC.Core.Internal; diff --git a/src/csharp/GrpcCore/Calls.cs b/src/csharp/GrpcCore/Calls.cs index c3e51cb478..101965600e 100644 --- a/src/csharp/GrpcCore/Calls.cs +++ b/src/csharp/GrpcCore/Calls.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Threading; using System.Threading.Tasks; diff --git a/src/csharp/GrpcCore/Channel.cs b/src/csharp/GrpcCore/Channel.cs index b0d8beeb7b..cd4f151f49 100644 --- a/src/csharp/GrpcCore/Channel.cs +++ b/src/csharp/GrpcCore/Channel.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Runtime.InteropServices; using System.Threading; @@ -8,13 +41,6 @@ namespace Google.GRPC.Core { public class Channel : IDisposable { - /// <summary> - /// Make sure GPRC environment is initialized before any channels get used. - /// </summary> - static Channel() { - GrpcEnvironment.EnsureInitialized(); - } - readonly ChannelSafeHandle handle; readonly String target; diff --git a/src/csharp/GrpcCore/ClientStreamingAsyncResult.cs b/src/csharp/GrpcCore/ClientStreamingAsyncResult.cs index 9e7312c1fa..507d0dd8ad 100644 --- a/src/csharp/GrpcCore/ClientStreamingAsyncResult.cs +++ b/src/csharp/GrpcCore/ClientStreamingAsyncResult.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Threading.Tasks; diff --git a/src/csharp/GrpcCore/GrpcCore.csproj b/src/csharp/GrpcCore/GrpcCore.csproj index 95df890917..34b9f6dfb8 100644 --- a/src/csharp/GrpcCore/GrpcCore.csproj +++ b/src/csharp/GrpcCore/GrpcCore.csproj @@ -61,7 +61,6 @@ <Compile Include="Marshaller.cs" /> <Compile Include="ServerServiceDefinition.cs" /> <Compile Include="Utils\RecordingObserver.cs" /> - <Compile Include="Utils\PortPicker.cs" /> <Compile Include="Utils\RecordingQueue.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> diff --git a/src/csharp/GrpcCore/GrpcEnvironment.cs b/src/csharp/GrpcCore/GrpcEnvironment.cs index 201320828b..ee1168621d 100644 --- a/src/csharp/GrpcCore/GrpcEnvironment.cs +++ b/src/csharp/GrpcCore/GrpcEnvironment.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using Google.GRPC.Core.Internal; using System.Runtime.InteropServices; @@ -5,36 +38,37 @@ using System.Runtime.InteropServices; namespace Google.GRPC.Core { /// <summary> - /// Encapsulates initialization and shutdown of GRPC C core library. - /// You should not need to initialize it manually, as static constructors - /// should load the library when needed. + /// Encapsulates initialization and shutdown of gRPC library. /// </summary> - public static class GrpcEnvironment + public class GrpcEnvironment { const int THREAD_POOL_SIZE = 1; - [DllImport("grpc.dll")] - static extern void grpc_init(); + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_init(); - [DllImport("grpc.dll")] - static extern void grpc_shutdown(); + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_shutdown(); static object staticLock = new object(); - static bool initCalled = false; - static bool shutdownCalled = false; - - static GrpcThreadPool threadPool = new GrpcThreadPool(THREAD_POOL_SIZE); + static volatile GrpcEnvironment instance; + + readonly GrpcThreadPool threadPool; + bool isClosed; /// <summary> - /// Makes sure GRPC environment is initialized. + /// Makes sure GRPC environment is initialized. Subsequent invocations don't have any + /// effect unless you call Shutdown first. + /// Although normal use cases assume you will call this just once in your application's + /// lifetime (and call Shutdown once you're done), for the sake of easier testing it's + /// allowed to initialize the environment again after it has been successfully shutdown. /// </summary> - public static void EnsureInitialized() { + public static void Initialize() { lock(staticLock) { - if (!initCalled) + if (instance == null) { - initCalled = true; - GrpcInit(); + instance = new GrpcEnvironment(); } } } @@ -47,45 +81,55 @@ namespace Google.GRPC.Core { lock(staticLock) { - if (initCalled && !shutdownCalled) + if (instance != null) { - shutdownCalled = true; - GrpcShutdown(); + instance.Close(); + instance = null; } } + } + internal static GrpcThreadPool ThreadPool + { + get + { + var inst = instance; + if (inst == null) + { + throw new InvalidOperationException("GRPC environment not initialized"); + } + return inst.threadPool; + } } /// <summary> - /// Initializes GRPC C Core library. + /// Creates gRPC environment. /// </summary> - private static void GrpcInit() + private GrpcEnvironment() { - grpc_init(); + grpcsharp_init(); + threadPool = new GrpcThreadPool(THREAD_POOL_SIZE); threadPool.Start(); // TODO: use proper logging here Console.WriteLine("GRPC initialized."); } /// <summary> - /// Shutdown GRPC C Core library. + /// Shuts down this environment. /// </summary> - private static void GrpcShutdown() + private void Close() { + if (isClosed) + { + throw new InvalidOperationException("Close has already been called"); + } threadPool.Stop(); - grpc_shutdown(); + grpcsharp_shutdown(); + isClosed = true; // TODO: use proper logging here Console.WriteLine("GRPC shutdown."); } - - internal static GrpcThreadPool ThreadPool - { - get - { - return threadPool; - } - } } } diff --git a/src/csharp/GrpcCore/Internal/AsyncCall.cs b/src/csharp/GrpcCore/Internal/AsyncCall.cs index c38363bb2b..a3b40e512c 100644 --- a/src/csharp/GrpcCore/Internal/AsyncCall.cs +++ b/src/csharp/GrpcCore/Internal/AsyncCall.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Runtime.InteropServices; using System.Diagnostics; diff --git a/src/csharp/GrpcCore/Internal/CallSafeHandle.cs b/src/csharp/GrpcCore/Internal/CallSafeHandle.cs index bbb830b355..d91d2ac6cb 100644 --- a/src/csharp/GrpcCore/Internal/CallSafeHandle.cs +++ b/src/csharp/GrpcCore/Internal/CallSafeHandle.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Runtime.InteropServices; using System.Diagnostics; @@ -15,67 +48,67 @@ namespace Google.GRPC.Core.Internal { const UInt32 GRPC_WRITE_BUFFER_HINT = 1; - [DllImport("grpc.dll")] - static extern CallSafeHandle grpc_channel_create_call_old(ChannelSafeHandle channel, string method, string host, Timespec deadline); + [DllImport("grpc_csharp_ext.dll")] + static extern CallSafeHandle grpcsharp_channel_create_call_old(ChannelSafeHandle channel, string method, string host, Timespec deadline); - [DllImport("grpc.dll")] - static extern GRPCCallError grpc_call_add_metadata(CallSafeHandle call, IntPtr metadata, UInt32 flags); + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_add_metadata(CallSafeHandle call, IntPtr metadata, UInt32 flags); - [DllImport("grpc.dll")] - static extern GRPCCallError grpc_call_invoke_old(CallSafeHandle call, CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, UInt32 flags); + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_invoke_old(CallSafeHandle call, CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, UInt32 flags); - [DllImport("grpc.dll", EntryPoint = "grpc_call_invoke_old")] - static extern GRPCCallError grpc_call_invoke_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle cq, + [DllImport("grpc_csharp_ext.dll", EntryPoint = "grpcsharp_call_invoke_old")] + static extern GRPCCallError grpcsharp_call_invoke_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle cq, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate metadataReadCallback, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback, UInt32 flags); - [DllImport("grpc.dll")] - static extern GRPCCallError grpc_call_server_accept_old(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, IntPtr finishedTag); + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_server_accept_old(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, IntPtr finishedTag); - [DllImport("grpc.dll", EntryPoint = "grpc_call_server_accept_old")] - static extern GRPCCallError grpc_call_server_accept_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback); + [DllImport("grpc_csharp_ext.dll", EntryPoint = "grpcsharp_call_server_accept_old")] + static extern GRPCCallError grpcsharp_call_server_accept_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback); - [DllImport("grpc.dll")] - static extern GRPCCallError grpc_call_server_end_initial_metadata_old(CallSafeHandle call, UInt32 flags); + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_server_end_initial_metadata_old(CallSafeHandle call, UInt32 flags); - [DllImport("grpc.dll")] - static extern GRPCCallError grpc_call_cancel(CallSafeHandle call); + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_cancel(CallSafeHandle call); - [DllImport("grpc.dll")] - static extern GRPCCallError grpc_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description); + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description); - [DllImport("grpc.dll")] - static extern GRPCCallError grpc_call_start_write_status_old(CallSafeHandle call, StatusCode statusCode, string statusMessage, IntPtr tag); + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_start_write_status_old(CallSafeHandle call, StatusCode statusCode, string statusMessage, IntPtr tag); - [DllImport("grpc.dll", EntryPoint = "grpc_call_start_write_status_old")] - static extern GRPCCallError grpc_call_start_write_status_old_CALLBACK(CallSafeHandle call, StatusCode statusCode, string statusMessage, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); + [DllImport("grpc_csharp_ext.dll", EntryPoint = "grpcsharp_call_start_write_status_old")] + static extern GRPCCallError grpcsharp_call_start_write_status_old_CALLBACK(CallSafeHandle call, StatusCode statusCode, string statusMessage, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); - [DllImport("grpc.dll")] - static extern GRPCCallError grpc_call_writes_done_old(CallSafeHandle call, IntPtr tag); + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_writes_done_old(CallSafeHandle call, IntPtr tag); - [DllImport("grpc.dll", EntryPoint = "grpc_call_writes_done_old")] - static extern GRPCCallError grpc_call_writes_done_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); + [DllImport("grpc_csharp_ext.dll", EntryPoint = "grpcsharp_call_writes_done_old")] + static extern GRPCCallError grpcsharp_call_writes_done_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); - [DllImport("grpc.dll")] - static extern GRPCCallError grpc_call_start_read_old(CallSafeHandle call, IntPtr tag); + [DllImport("grpc_csharp_ext.dll")] + static extern GRPCCallError grpcsharp_call_start_read_old(CallSafeHandle call, IntPtr tag); - [DllImport("grpc.dll", EntryPoint = "grpc_call_start_read_old")] - static extern GRPCCallError grpc_call_start_read_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); + [DllImport("grpc_csharp_ext.dll", EntryPoint = "grpcsharp_call_start_read_old")] + static extern GRPCCallError grpcsharp_call_start_read_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); [DllImport("grpc_csharp_ext.dll")] - static extern void grpc_call_start_write_from_copied_buffer(CallSafeHandle call, + static extern void grpcsharp_call_start_write_from_copied_buffer(CallSafeHandle call, byte[] buffer, UIntPtr length, IntPtr tag, UInt32 flags); - [DllImport("grpc_csharp_ext.dll", EntryPoint = "grpc_call_start_write_from_copied_buffer")] - static extern void grpc_call_start_write_from_copied_buffer_CALLBACK(CallSafeHandle call, + [DllImport("grpc_csharp_ext.dll", EntryPoint = "grpcsharp_call_start_write_from_copied_buffer")] + static extern void grpcsharp_call_start_write_from_copied_buffer_CALLBACK(CallSafeHandle call, byte[] buffer, UIntPtr length, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback, UInt32 flags); - [DllImport("grpc.dll")] - static extern void grpc_call_destroy(IntPtr call); + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_call_destroy(IntPtr call); private CallSafeHandle() { @@ -86,87 +119,87 @@ namespace Google.GRPC.Core.Internal /// </summary> public static CallSafeHandle Create(ChannelSafeHandle channel, string method, string host, Timespec deadline) { - return grpc_channel_create_call_old(channel, method, host, deadline); + return grpcsharp_channel_create_call_old(channel, method, host, deadline); } public void Invoke(CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, bool buffered) { - AssertCallOk(grpc_call_invoke_old(this, cq, metadataReadTag, finishedTag, GetFlags(buffered))); + AssertCallOk(grpcsharp_call_invoke_old(this, cq, metadataReadTag, finishedTag, GetFlags(buffered))); } public void Invoke(CompletionQueueSafeHandle cq, bool buffered, EventCallbackDelegate metadataReadCallback, EventCallbackDelegate finishedCallback) { - AssertCallOk(grpc_call_invoke_old_CALLBACK(this, cq, metadataReadCallback, finishedCallback, GetFlags(buffered))); + AssertCallOk(grpcsharp_call_invoke_old_CALLBACK(this, cq, metadataReadCallback, finishedCallback, GetFlags(buffered))); } public void ServerAccept(CompletionQueueSafeHandle cq, IntPtr finishedTag) { - AssertCallOk(grpc_call_server_accept_old(this, cq, finishedTag)); + AssertCallOk(grpcsharp_call_server_accept_old(this, cq, finishedTag)); } public void ServerAccept(CompletionQueueSafeHandle cq, EventCallbackDelegate callback) { - AssertCallOk(grpc_call_server_accept_old_CALLBACK(this, cq, callback)); + AssertCallOk(grpcsharp_call_server_accept_old_CALLBACK(this, cq, callback)); } public void ServerEndInitialMetadata(UInt32 flags) { - AssertCallOk(grpc_call_server_end_initial_metadata_old(this, flags)); + AssertCallOk(grpcsharp_call_server_end_initial_metadata_old(this, flags)); } public void StartWrite(byte[] payload, IntPtr tag, bool buffered) { - grpc_call_start_write_from_copied_buffer(this, payload, new UIntPtr((ulong) payload.Length), tag, GetFlags(buffered)); + grpcsharp_call_start_write_from_copied_buffer(this, payload, new UIntPtr((ulong) payload.Length), tag, GetFlags(buffered)); } public void StartWrite(byte[] payload, bool buffered, EventCallbackDelegate callback) { - grpc_call_start_write_from_copied_buffer_CALLBACK(this, payload, new UIntPtr((ulong) payload.Length), callback, GetFlags(buffered)); + grpcsharp_call_start_write_from_copied_buffer_CALLBACK(this, payload, new UIntPtr((ulong) payload.Length), callback, GetFlags(buffered)); } public void StartWriteStatus(Status status, IntPtr tag) { - AssertCallOk(grpc_call_start_write_status_old(this, status.StatusCode, status.Detail, tag)); + AssertCallOk(grpcsharp_call_start_write_status_old(this, status.StatusCode, status.Detail, tag)); } public void StartWriteStatus(Status status, EventCallbackDelegate callback) { - AssertCallOk(grpc_call_start_write_status_old_CALLBACK(this, status.StatusCode, status.Detail, callback)); + AssertCallOk(grpcsharp_call_start_write_status_old_CALLBACK(this, status.StatusCode, status.Detail, callback)); } public void WritesDone(IntPtr tag) { - AssertCallOk(grpc_call_writes_done_old(this, tag)); + AssertCallOk(grpcsharp_call_writes_done_old(this, tag)); } public void WritesDone(EventCallbackDelegate callback) { - AssertCallOk(grpc_call_writes_done_old_CALLBACK(this, callback)); + AssertCallOk(grpcsharp_call_writes_done_old_CALLBACK(this, callback)); } public void StartRead(IntPtr tag) { - AssertCallOk(grpc_call_start_read_old(this, tag)); + AssertCallOk(grpcsharp_call_start_read_old(this, tag)); } public void StartRead(EventCallbackDelegate callback) { - AssertCallOk(grpc_call_start_read_old_CALLBACK(this, callback)); + AssertCallOk(grpcsharp_call_start_read_old_CALLBACK(this, callback)); } public void Cancel() { - AssertCallOk(grpc_call_cancel(this)); + AssertCallOk(grpcsharp_call_cancel(this)); } public void CancelWithStatus(Status status) { - AssertCallOk(grpc_call_cancel_with_status(this, status.StatusCode, status.Detail)); + AssertCallOk(grpcsharp_call_cancel_with_status(this, status.StatusCode, status.Detail)); } protected override bool ReleaseHandle() { - grpc_call_destroy(handle); + grpcsharp_call_destroy(handle); return true; } diff --git a/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs b/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs index 0f38d63f98..f6af64c967 100644 --- a/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs +++ b/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Runtime.InteropServices; using System.Threading; @@ -10,11 +43,11 @@ namespace Google.GRPC.Core.Internal /// </summary> internal class ChannelSafeHandle : SafeHandleZeroIsInvalid { - [DllImport("grpc.dll")] - static extern ChannelSafeHandle grpc_channel_create(string target, IntPtr channelArgs); + [DllImport("grpc_csharp_ext.dll")] + static extern ChannelSafeHandle grpcsharp_channel_create(string target, IntPtr channelArgs); - [DllImport("grpc.dll")] - static extern void grpc_channel_destroy(IntPtr channel); + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_channel_destroy(IntPtr channel); private ChannelSafeHandle() { @@ -22,12 +55,12 @@ namespace Google.GRPC.Core.Internal public static ChannelSafeHandle Create(string target, IntPtr channelArgs) { - return grpc_channel_create(target, channelArgs); + return grpcsharp_channel_create(target, channelArgs); } protected override bool ReleaseHandle() { - grpc_channel_destroy(handle); + grpcsharp_channel_destroy(handle); return true; } } diff --git a/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs b/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs index f098de6820..fc2b1d5421 100644 --- a/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs +++ b/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -9,23 +42,23 @@ namespace Google.GRPC.Core.Internal /// </summary> internal class CompletionQueueSafeHandle : SafeHandleZeroIsInvalid { - [DllImport("grpc.dll")] - static extern CompletionQueueSafeHandle grpc_completion_queue_create(); + [DllImport("grpc_csharp_ext.dll")] + static extern CompletionQueueSafeHandle grpcsharp_completion_queue_create(); - [DllImport("grpc.dll")] - static extern EventSafeHandle grpc_completion_queue_pluck(CompletionQueueSafeHandle cq, IntPtr tag, Timespec deadline); + [DllImport("grpc_csharp_ext.dll")] + static extern EventSafeHandle grpcsharp_completion_queue_pluck(CompletionQueueSafeHandle cq, IntPtr tag, Timespec deadline); - [DllImport("grpc.dll")] - static extern EventSafeHandle grpc_completion_queue_next(CompletionQueueSafeHandle cq, Timespec deadline); + [DllImport("grpc_csharp_ext.dll")] + static extern EventSafeHandle grpcsharp_completion_queue_next(CompletionQueueSafeHandle cq, Timespec deadline); - [DllImport("grpc.dll")] - static extern void grpc_completion_queue_shutdown(CompletionQueueSafeHandle cq); + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_completion_queue_shutdown(CompletionQueueSafeHandle cq); [DllImport("grpc_csharp_ext.dll")] - static extern GRPCCompletionType grpc_completion_queue_next_with_callback(CompletionQueueSafeHandle cq); + static extern GRPCCompletionType grpcsharp_completion_queue_next_with_callback(CompletionQueueSafeHandle cq); - [DllImport("grpc.dll")] - static extern void grpc_completion_queue_destroy(IntPtr cq); + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_completion_queue_destroy(IntPtr cq); private CompletionQueueSafeHandle() { @@ -33,32 +66,32 @@ namespace Google.GRPC.Core.Internal public static CompletionQueueSafeHandle Create() { - return grpc_completion_queue_create(); + return grpcsharp_completion_queue_create(); } public EventSafeHandle Next(Timespec deadline) { - return grpc_completion_queue_next(this, deadline); + return grpcsharp_completion_queue_next(this, deadline); } public GRPCCompletionType NextWithCallback() { - return grpc_completion_queue_next_with_callback(this); + return grpcsharp_completion_queue_next_with_callback(this); } public EventSafeHandle Pluck(IntPtr tag, Timespec deadline) { - return grpc_completion_queue_pluck(this, tag, deadline); + return grpcsharp_completion_queue_pluck(this, tag, deadline); } public void Shutdown() { - grpc_completion_queue_shutdown(this); + grpcsharp_completion_queue_shutdown(this); } protected override bool ReleaseHandle() { - grpc_completion_queue_destroy(handle); + grpcsharp_completion_queue_destroy(handle); return true; } } diff --git a/src/csharp/GrpcCore/Internal/Enums.cs b/src/csharp/GrpcCore/Internal/Enums.cs index 1151e94899..c2eb1327b3 100644 --- a/src/csharp/GrpcCore/Internal/Enums.cs +++ b/src/csharp/GrpcCore/Internal/Enums.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Runtime.InteropServices; diff --git a/src/csharp/GrpcCore/Internal/Event.cs b/src/csharp/GrpcCore/Internal/Event.cs index 5853ddd570..9229472ccd 100644 --- a/src/csharp/GrpcCore/Internal/Event.cs +++ b/src/csharp/GrpcCore/Internal/Event.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Runtime.InteropServices; using Google.GRPC.Core; @@ -9,84 +42,84 @@ namespace Google.GRPC.Core.Internal /// </summary> internal class EventSafeHandle : SafeHandleZeroIsInvalid { - [DllImport("grpc.dll")] - static extern void grpc_event_finish(IntPtr ev); + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_event_finish(IntPtr ev); [DllImport("grpc_csharp_ext.dll")] - static extern GRPCCompletionType grpc_event_type(EventSafeHandle ev); + static extern GRPCCompletionType grpcsharp_event_type(EventSafeHandle ev); [DllImport("grpc_csharp_ext.dll")] - static extern CallSafeHandle grpc_event_call(EventSafeHandle ev); + static extern CallSafeHandle grpcsharp_event_call(EventSafeHandle ev); [DllImport("grpc_csharp_ext.dll")] - static extern GRPCOpError grpc_event_write_accepted(EventSafeHandle ev); + static extern GRPCOpError grpcsharp_event_write_accepted(EventSafeHandle ev); [DllImport("grpc_csharp_ext.dll")] - static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandle ev); + static extern GRPCOpError grpcsharp_event_finish_accepted(EventSafeHandle ev); [DllImport("grpc_csharp_ext.dll")] - static extern StatusCode grpc_event_finished_status(EventSafeHandle ev); + static extern StatusCode grpcsharp_event_finished_status(EventSafeHandle ev); [DllImport("grpc_csharp_ext.dll")] - static extern IntPtr grpc_event_finished_details(EventSafeHandle ev); // returns const char* + static extern IntPtr grpcsharp_event_finished_details(EventSafeHandle ev); // returns const char* [DllImport("grpc_csharp_ext.dll")] - static extern IntPtr grpc_event_read_length(EventSafeHandle ev); + static extern IntPtr grpcsharp_event_read_length(EventSafeHandle ev); [DllImport("grpc_csharp_ext.dll")] - static extern void grpc_event_read_copy_to_buffer(EventSafeHandle ev, byte[] buffer, UIntPtr bufferLen); + static extern void grpcsharp_event_read_copy_to_buffer(EventSafeHandle ev, byte[] buffer, UIntPtr bufferLen); [DllImport("grpc_csharp_ext.dll")] - static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandle ev); // returns const char* + static extern IntPtr grpcsharp_event_server_rpc_new_method(EventSafeHandle ev); // returns const char* public GRPCCompletionType GetCompletionType() { - return grpc_event_type(this); + return grpcsharp_event_type(this); } public GRPCOpError GetWriteAccepted() { - return grpc_event_write_accepted(this); + return grpcsharp_event_write_accepted(this); } public GRPCOpError GetFinishAccepted() { - return grpc_event_finish_accepted(this); + return grpcsharp_event_finish_accepted(this); } public Status GetFinished() { // TODO: can the native method return string directly? - string details = Marshal.PtrToStringAnsi(grpc_event_finished_details(this)); - return new Status(grpc_event_finished_status(this), details); + string details = Marshal.PtrToStringAnsi(grpcsharp_event_finished_details(this)); + return new Status(grpcsharp_event_finished_status(this), details); } public byte[] GetReadData() { - IntPtr len = grpc_event_read_length(this); + IntPtr len = grpcsharp_event_read_length(this); if (len == new IntPtr(-1)) { return null; } byte[] data = new byte[(int) len]; - grpc_event_read_copy_to_buffer(this, data, new UIntPtr((ulong)data.Length)); + grpcsharp_event_read_copy_to_buffer(this, data, new UIntPtr((ulong)data.Length)); return data; } public CallSafeHandle GetCall() { - return grpc_event_call(this); + return grpcsharp_event_call(this); } public string GetServerRpcNewMethod() { // TODO: can the native method return string directly? - return Marshal.PtrToStringAnsi(grpc_event_server_rpc_new_method(this)); + return Marshal.PtrToStringAnsi(grpcsharp_event_server_rpc_new_method(this)); } //TODO: client_metadata_read event type protected override bool ReleaseHandle() { - grpc_event_finish(handle); + grpcsharp_event_finish(handle); return true; } } @@ -98,35 +131,35 @@ namespace Google.GRPC.Core.Internal /// </summary> internal class EventSafeHandleNotOwned : SafeHandleZeroIsInvalid { - [DllImport("grpc.dll")] - static extern void grpc_event_finish(IntPtr ev); + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_event_finish(IntPtr ev); [DllImport("grpc_csharp_ext.dll")] - static extern GRPCCompletionType grpc_event_type(EventSafeHandleNotOwned ev); + static extern GRPCCompletionType grpcsharp_event_type(EventSafeHandleNotOwned ev); [DllImport("grpc_csharp_ext.dll")] - static extern CallSafeHandle grpc_event_call(EventSafeHandleNotOwned ev); + static extern CallSafeHandle grpcsharp_event_call(EventSafeHandleNotOwned ev); [DllImport("grpc_csharp_ext.dll")] - static extern GRPCOpError grpc_event_write_accepted(EventSafeHandleNotOwned ev); + static extern GRPCOpError grpcsharp_event_write_accepted(EventSafeHandleNotOwned ev); [DllImport("grpc_csharp_ext.dll")] - static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandleNotOwned ev); + static extern GRPCOpError grpcsharp_event_finish_accepted(EventSafeHandleNotOwned ev); [DllImport("grpc_csharp_ext.dll")] - static extern StatusCode grpc_event_finished_status(EventSafeHandleNotOwned ev); + static extern StatusCode grpcsharp_event_finished_status(EventSafeHandleNotOwned ev); [DllImport("grpc_csharp_ext.dll")] - static extern IntPtr grpc_event_finished_details(EventSafeHandleNotOwned ev); // returns const char* + static extern IntPtr grpcsharp_event_finished_details(EventSafeHandleNotOwned ev); // returns const char* [DllImport("grpc_csharp_ext.dll")] - static extern IntPtr grpc_event_read_length(EventSafeHandleNotOwned ev); + static extern IntPtr grpcsharp_event_read_length(EventSafeHandleNotOwned ev); [DllImport("grpc_csharp_ext.dll")] - static extern void grpc_event_read_copy_to_buffer(EventSafeHandleNotOwned ev, byte[] buffer, UIntPtr bufferLen); + static extern void grpcsharp_event_read_copy_to_buffer(EventSafeHandleNotOwned ev, byte[] buffer, UIntPtr bufferLen); [DllImport("grpc_csharp_ext.dll")] - static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandleNotOwned ev); // returns const char* + static extern IntPtr grpcsharp_event_server_rpc_new_method(EventSafeHandleNotOwned ev); // returns const char* public EventSafeHandleNotOwned() : base(false) { @@ -139,52 +172,52 @@ namespace Google.GRPC.Core.Internal public GRPCCompletionType GetCompletionType() { - return grpc_event_type(this); + return grpcsharp_event_type(this); } public GRPCOpError GetWriteAccepted() { - return grpc_event_write_accepted(this); + return grpcsharp_event_write_accepted(this); } public GRPCOpError GetFinishAccepted() { - return grpc_event_finish_accepted(this); + return grpcsharp_event_finish_accepted(this); } public Status GetFinished() { // TODO: can the native method return string directly? - string details = Marshal.PtrToStringAnsi(grpc_event_finished_details(this)); - return new Status(grpc_event_finished_status(this), details); + string details = Marshal.PtrToStringAnsi(grpcsharp_event_finished_details(this)); + return new Status(grpcsharp_event_finished_status(this), details); } public byte[] GetReadData() { - IntPtr len = grpc_event_read_length(this); + IntPtr len = grpcsharp_event_read_length(this); if (len == new IntPtr(-1)) { return null; } byte[] data = new byte[(int) len]; - grpc_event_read_copy_to_buffer(this, data, new UIntPtr((ulong)data.Length)); + grpcsharp_event_read_copy_to_buffer(this, data, new UIntPtr((ulong)data.Length)); return data; } public CallSafeHandle GetCall() { - return grpc_event_call(this); + return grpcsharp_event_call(this); } public string GetServerRpcNewMethod() { // TODO: can the native method return string directly? - return Marshal.PtrToStringAnsi(grpc_event_server_rpc_new_method(this)); + return Marshal.PtrToStringAnsi(grpcsharp_event_server_rpc_new_method(this)); } //TODO: client_metadata_read event type protected override bool ReleaseHandle() { - grpc_event_finish(handle); + grpcsharp_event_finish(handle); return true; } } diff --git a/src/csharp/GrpcCore/Internal/GrpcThreadPool.cs b/src/csharp/GrpcCore/Internal/GrpcThreadPool.cs index 1139e54a1d..b768decc8c 100644 --- a/src/csharp/GrpcCore/Internal/GrpcThreadPool.cs +++ b/src/csharp/GrpcCore/Internal/GrpcThreadPool.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using Google.GRPC.Core.Internal; using System.Runtime.InteropServices; diff --git a/src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs b/src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs index 5a1252b881..c7d8a0a3c3 100644 --- a/src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs +++ b/src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Runtime.InteropServices; diff --git a/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs b/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs index d363b34f0b..2ae0ad237d 100644 --- a/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs +++ b/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Runtime.InteropServices; using System.Diagnostics; @@ -10,31 +43,31 @@ namespace Google.GRPC.Core.Internal /// </summary> internal sealed class ServerSafeHandle : SafeHandleZeroIsInvalid { - [DllImport("grpc.dll", EntryPoint = "grpc_server_request_call_old")] - static extern GRPCCallError grpc_server_request_call_old_CALLBACK(ServerSafeHandle server, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); + [DllImport("grpc_csharp_ext.dll", EntryPoint = "grpcsharp_server_request_call_old")] + static extern GRPCCallError grpcsharp_server_request_call_old_CALLBACK(ServerSafeHandle server, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); - [DllImport("grpc.dll")] - static extern ServerSafeHandle grpc_server_create(CompletionQueueSafeHandle cq, IntPtr args); + [DllImport("grpc_csharp_ext.dll")] + static extern ServerSafeHandle grpcsharp_server_create(CompletionQueueSafeHandle cq, IntPtr args); // TODO: check int representation size - [DllImport("grpc.dll")] - static extern int grpc_server_add_http2_port(ServerSafeHandle server, string addr); + [DllImport("grpc_csharp_ext.dll")] + static extern int grpcsharp_server_add_http2_port(ServerSafeHandle server, string addr); // TODO: check int representation size - [DllImport("grpc.dll")] - static extern int grpc_server_add_secure_http2_port(ServerSafeHandle server, string addr); + [DllImport("grpc_csharp_ext.dll")] + static extern int grpcsharp_server_add_secure_http2_port(ServerSafeHandle server, string addr); - [DllImport("grpc.dll")] - static extern void grpc_server_start(ServerSafeHandle server); + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_server_start(ServerSafeHandle server); - [DllImport("grpc.dll")] - static extern void grpc_server_shutdown(ServerSafeHandle server); + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_server_shutdown(ServerSafeHandle server); - [DllImport("grpc.dll", EntryPoint = "grpc_server_shutdown_and_notify")] - static extern void grpc_server_shutdown_and_notify_CALLBACK(ServerSafeHandle server, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); + [DllImport("grpc_csharp_ext.dll", EntryPoint = "grpcsharp_server_shutdown_and_notify")] + static extern void grpcsharp_server_shutdown_and_notify_CALLBACK(ServerSafeHandle server, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); - [DllImport("grpc.dll")] - static extern void grpc_server_destroy(IntPtr server); + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_server_destroy(IntPtr server); private ServerSafeHandle() { @@ -43,38 +76,38 @@ namespace Google.GRPC.Core.Internal public static ServerSafeHandle NewServer(CompletionQueueSafeHandle cq, IntPtr args) { // TODO: also grpc_secure_server_create... - return grpc_server_create(cq, args); + return grpcsharp_server_create(cq, args); } public int AddPort(string addr) { // TODO: also grpc_server_add_secure_http2_port... - return grpc_server_add_http2_port(this, addr); + return grpcsharp_server_add_http2_port(this, addr); } public void Start() { - grpc_server_start(this); + grpcsharp_server_start(this); } public void Shutdown() { - grpc_server_shutdown(this); + grpcsharp_server_shutdown(this); } public void ShutdownAndNotify(EventCallbackDelegate callback) { - grpc_server_shutdown_and_notify_CALLBACK(this, callback); + grpcsharp_server_shutdown_and_notify_CALLBACK(this, callback); } public GRPCCallError RequestCall(EventCallbackDelegate callback) { - return grpc_server_request_call_old_CALLBACK(this, callback); + return grpcsharp_server_request_call_old_CALLBACK(this, callback); } protected override bool ReleaseHandle() { - grpc_server_destroy(handle); + grpcsharp_server_destroy(handle); return true; } } diff --git a/src/csharp/GrpcCore/Internal/ServerWritingObserver.cs b/src/csharp/GrpcCore/Internal/ServerWritingObserver.cs index 2b46e9c53d..1120791842 100644 --- a/src/csharp/GrpcCore/Internal/ServerWritingObserver.cs +++ b/src/csharp/GrpcCore/Internal/ServerWritingObserver.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using Google.GRPC.Core.Internal; diff --git a/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs b/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs index c5de979351..49410961cb 100644 --- a/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs +++ b/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using Google.GRPC.Core.Internal; diff --git a/src/csharp/GrpcCore/Internal/Timespec.cs b/src/csharp/GrpcCore/Internal/Timespec.cs index 5a197e121c..651003dae8 100644 --- a/src/csharp/GrpcCore/Internal/Timespec.cs +++ b/src/csharp/GrpcCore/Internal/Timespec.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Runtime.InteropServices; using System.Threading; @@ -13,13 +46,20 @@ namespace Google.GRPC.Core.Internal const int nanosPerSecond = 1000 * 1000 * 1000; const int nanosPerTick = 100; - [DllImport("gpr.dll")] - static extern Timespec gpr_now(); + [DllImport("grpc_csharp_ext.dll")] + static extern Timespec gprsharp_now(); - // TODO: this only works on 64bit linux, can we autoselect the right size of ints? - // perhaps using IntPtr would work. - public System.Int64 tv_sec; - public System.Int64 tv_nsec; + [DllImport("grpc_csharp_ext.dll")] + static extern Timespec gprsharp_inf_future(); + + [DllImport("grpc_csharp_ext.dll")] + static extern int gprsharp_sizeof_timespec(); + + // TODO: revisit this. + // NOTE: on linux 64bit sizeof(gpr_timespec) = 16, on windows 32bit sizeof(gpr_timespec) = 8 + // so IntPtr seems to have the right size to work on both. + public System.IntPtr tv_sec; + public System.IntPtr tv_nsec; /// <summary> /// Timespec a long time in the future. @@ -28,8 +68,7 @@ namespace Google.GRPC.Core.Internal { get { - // TODO: set correct value based on the length of the struct - return new Timespec { tv_sec = Int32.MaxValue, tv_nsec = 0 }; + return gprsharp_inf_future(); } } @@ -37,7 +76,15 @@ namespace Google.GRPC.Core.Internal { get { - return gpr_now(); + return gprsharp_now(); + } + } + + internal static int NativeSize + { + get + { + return gprsharp_sizeof_timespec(); } } @@ -54,12 +101,12 @@ namespace Google.GRPC.Core.Internal } public Timespec Add(TimeSpan timeSpan) { - long nanos = tv_nsec + (timeSpan.Ticks % TimeSpan.TicksPerSecond) * nanosPerTick; + long nanos = tv_nsec.ToInt64() + (timeSpan.Ticks % TimeSpan.TicksPerSecond) * nanosPerTick; long overflow_sec = (nanos > nanosPerSecond) ? 1 : 0; Timespec result; - result.tv_nsec = nanos % nanosPerSecond; - result.tv_sec = tv_sec + (timeSpan.Ticks / TimeSpan.TicksPerSecond) + overflow_sec; + result.tv_nsec = new IntPtr(nanos % nanosPerSecond); + result.tv_sec = new IntPtr(tv_sec.ToInt64() + (timeSpan.Ticks / TimeSpan.TicksPerSecond) + overflow_sec); return result; } } diff --git a/src/csharp/GrpcCore/Marshaller.cs b/src/csharp/GrpcCore/Marshaller.cs index 242524063c..21bd26650b 100644 --- a/src/csharp/GrpcCore/Marshaller.cs +++ b/src/csharp/GrpcCore/Marshaller.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; namespace Google.GRPC.Core diff --git a/src/csharp/GrpcCore/Method.cs b/src/csharp/GrpcCore/Method.cs index 9067ae8c94..a8c647035d 100644 --- a/src/csharp/GrpcCore/Method.cs +++ b/src/csharp/GrpcCore/Method.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; namespace Google.GRPC.Core diff --git a/src/csharp/GrpcCore/RpcException.cs b/src/csharp/GrpcCore/RpcException.cs index 8811c3a7c7..5d1ca3bcdf 100644 --- a/src/csharp/GrpcCore/RpcException.cs +++ b/src/csharp/GrpcCore/RpcException.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; namespace Google.GRPC.Core diff --git a/src/csharp/GrpcCore/Server.cs b/src/csharp/GrpcCore/Server.cs index d3bc81e574..62ffa70b71 100644 --- a/src/csharp/GrpcCore/Server.cs +++ b/src/csharp/GrpcCore/Server.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Runtime.InteropServices; using System.Diagnostics; @@ -26,10 +59,6 @@ namespace Google.GRPC.Core readonly TaskCompletionSource<object> shutdownTcs = new TaskCompletionSource<object>(); - static Server() { - GrpcEnvironment.EnsureInitialized(); - } - public Server() { // TODO: what is the tag for server shutdown? diff --git a/src/csharp/GrpcCore/ServerCallHandler.cs b/src/csharp/GrpcCore/ServerCallHandler.cs index 67103791b4..12d0c93634 100644 --- a/src/csharp/GrpcCore/ServerCallHandler.cs +++ b/src/csharp/GrpcCore/ServerCallHandler.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using Google.GRPC.Core.Internal; diff --git a/src/csharp/GrpcCore/ServerCalls.cs b/src/csharp/GrpcCore/ServerCalls.cs index 86c4718932..b95a0d97b4 100644 --- a/src/csharp/GrpcCore/ServerCalls.cs +++ b/src/csharp/GrpcCore/ServerCalls.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; namespace Google.GRPC.Core diff --git a/src/csharp/GrpcCore/ServerServiceDefinition.cs b/src/csharp/GrpcCore/ServerServiceDefinition.cs index 7f1cc6284e..f0b4daf071 100644 --- a/src/csharp/GrpcCore/ServerServiceDefinition.cs +++ b/src/csharp/GrpcCore/ServerServiceDefinition.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Collections.Generic; diff --git a/src/csharp/GrpcCore/Status.cs b/src/csharp/GrpcCore/Status.cs index f1212f8d67..dce1e24d7e 100644 --- a/src/csharp/GrpcCore/Status.cs +++ b/src/csharp/GrpcCore/Status.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Runtime.InteropServices; diff --git a/src/csharp/GrpcCore/StatusCode.cs b/src/csharp/GrpcCore/StatusCode.cs index 80fc8bd581..eccaae72f6 100644 --- a/src/csharp/GrpcCore/StatusCode.cs +++ b/src/csharp/GrpcCore/StatusCode.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; namespace Google.GRPC.Core diff --git a/src/csharp/GrpcCore/Utils/PortPicker.cs b/src/csharp/GrpcCore/Utils/PortPicker.cs deleted file mode 100644 index 7c83bf3886..0000000000 --- a/src/csharp/GrpcCore/Utils/PortPicker.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Net; -using System.Net.Sockets; - -namespace Google.GRPC.Core.Utils -{ - public class PortPicker - { - static Random random = new Random(); - - // TODO: cleanup this code a bit - public static int PickUnusedPort() - { - int port; - do - { - port = random.Next(2000, 50000); - - } while(!IsPortAvailable(port)); - return port; - } - - // TODO: cleanup this code a bit - public static bool IsPortAvailable(int port) - { - bool available = true; - - TcpListener server = null; - try - { - IPAddress ipAddress = Dns.GetHostEntry("localhost").AddressList[0]; - server = new TcpListener(ipAddress, port); - server.Start(); - } - catch (Exception ex) - { - available = false; - } - finally - { - if (server != null) - { - server.Stop(); - } - } - return available; - } - } -} - diff --git a/src/csharp/GrpcCore/Utils/RecordingObserver.cs b/src/csharp/GrpcCore/Utils/RecordingObserver.cs index ca11cc4aa2..0cadfc0e4a 100644 --- a/src/csharp/GrpcCore/Utils/RecordingObserver.cs +++ b/src/csharp/GrpcCore/Utils/RecordingObserver.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Threading.Tasks; using System.Collections.Generic; diff --git a/src/csharp/GrpcCore/Utils/RecordingQueue.cs b/src/csharp/GrpcCore/Utils/RecordingQueue.cs index 0726f00af7..d73fc0fc78 100644 --- a/src/csharp/GrpcCore/Utils/RecordingQueue.cs +++ b/src/csharp/GrpcCore/Utils/RecordingQueue.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Threading.Tasks; using System.Collections.Generic; @@ -5,6 +38,7 @@ using System.Collections.Concurrent; namespace Google.GRPC.Core.Utils { + // TODO: replace this by something that implements IAsyncEnumerator. /// <summary> /// Observer that allows us to await incoming messages one-by-one. /// The implementation is not ideal and class will be probably replaced diff --git a/src/csharp/GrpcCoreTests/ClientServerTest.cs b/src/csharp/GrpcCoreTests/ClientServerTest.cs index c700ffbe7b..1472db6e07 100644 --- a/src/csharp/GrpcCoreTests/ClientServerTest.cs +++ b/src/csharp/GrpcCoreTests/ClientServerTest.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using NUnit.Framework; using Google.GRPC.Core; @@ -10,7 +43,7 @@ namespace Google.GRPC.Core.Tests { public class ClientServerTest { - string serverAddr = "localhost:" + PortPicker.PickUnusedPort(); + string host = "localhost"; Method<string, string> unaryEchoStringMethod = new Method<string, string>( MethodType.Unary, @@ -21,15 +54,17 @@ namespace Google.GRPC.Core.Tests [Test] public void EmptyCall() { + GrpcEnvironment.Initialize(); + Server server = new Server(); server.AddServiceDefinition( ServerServiceDefinition.CreateBuilder("someService") .AddMethod(unaryEchoStringMethod, HandleUnaryEchoString).Build()); - server.AddPort(serverAddr); + int port = server.AddPort(host + ":0"); server.Start(); - using (Channel channel = new Channel(serverAddr)) + using (Channel channel = new Channel(host + ":" + port)) { var call = new Call<string, string>(unaryEchoStringMethod, channel); diff --git a/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs b/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs index 136878d76e..1bc6cce401 100644 --- a/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs +++ b/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using NUnit.Framework; using Google.GRPC.Core; @@ -9,10 +42,30 @@ namespace Google.GRPC.Core.Tests { [Test] public void InitializeAndShutdownGrpcEnvironment() { - GrpcEnvironment.EnsureInitialized(); - Thread.Sleep(500); + GrpcEnvironment.Initialize(); Assert.IsNotNull(GrpcEnvironment.ThreadPool.CompletionQueue); GrpcEnvironment.Shutdown(); } + + [Test] + public void SubsequentInvocations() { + GrpcEnvironment.Initialize(); + GrpcEnvironment.Initialize(); + GrpcEnvironment.Shutdown(); + GrpcEnvironment.Shutdown(); + } + + [Test] + public void InitializeAfterShutdown() { + GrpcEnvironment.Initialize(); + var tp1 = GrpcEnvironment.ThreadPool; + GrpcEnvironment.Shutdown(); + + GrpcEnvironment.Initialize(); + var tp2 = GrpcEnvironment.ThreadPool; + GrpcEnvironment.Shutdown(); + + Assert.IsFalse(Object.ReferenceEquals(tp1, tp2)); + } } } diff --git a/src/csharp/GrpcCoreTests/ServerTest.cs b/src/csharp/GrpcCoreTests/ServerTest.cs index 6e13bc735f..1c70a3d6c4 100644 --- a/src/csharp/GrpcCoreTests/ServerTest.cs +++ b/src/csharp/GrpcCoreTests/ServerTest.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using NUnit.Framework; using Google.GRPC.Core.Internal; @@ -9,10 +42,12 @@ namespace Google.GRPC.Core.Tests public class ServerTest { [Test] - public void StartAndShutdownServer() { + public void StartAndShutdownServer() + { + GrpcEnvironment.Initialize(); Server server = new Server(); - server.AddPort("localhost:" + PortPicker.PickUnusedPort()); + int port = server.AddPort("localhost:0"); server.Start(); server.ShutdownAsync().Wait(); diff --git a/src/csharp/GrpcCoreTests/TimespecTest.cs b/src/csharp/GrpcCoreTests/TimespecTest.cs index 484bad7ca1..2b03513cb7 100644 --- a/src/csharp/GrpcCoreTests/TimespecTest.cs +++ b/src/csharp/GrpcCoreTests/TimespecTest.cs @@ -1,5 +1,39 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using NUnit.Framework; +using System.Runtime.InteropServices; using Google.GRPC.Core.Internal; namespace Google.GRPC.Core.Internal.Tests @@ -13,30 +47,42 @@ namespace Google.GRPC.Core.Internal.Tests } [Test] + public void InfFuture() + { + var timespec = Timespec.InfFuture; + } + + [Test] + public void TimespecSizeIsNativeSize() + { + Assert.AreEqual(Timespec.NativeSize, Marshal.SizeOf(typeof(Timespec))); + } + + [Test] public void Add() { - var t = new Timespec { tv_sec = 12345, tv_nsec = 123456789 }; + var t = new Timespec { tv_sec = new IntPtr(12345), tv_nsec = new IntPtr(123456789) }; var result = t.Add(TimeSpan.FromTicks(TimeSpan.TicksPerSecond * 10)); - Assert.AreEqual(result.tv_sec, 12355); - Assert.AreEqual(result.tv_nsec, 123456789); + Assert.AreEqual(result.tv_sec, new IntPtr(12355)); + Assert.AreEqual(result.tv_nsec, new IntPtr(123456789)); } [Test] public void Add_Nanos() { - var t = new Timespec { tv_sec = 12345, tv_nsec = 123456789 }; + var t = new Timespec { tv_sec = new IntPtr(12345), tv_nsec = new IntPtr(123456789) }; var result = t.Add(TimeSpan.FromTicks(10)); - Assert.AreEqual(result.tv_sec, 12345); - Assert.AreEqual(result.tv_nsec, 123456789 + 1000); + Assert.AreEqual(result.tv_sec, new IntPtr(12345)); + Assert.AreEqual(result.tv_nsec, new IntPtr(123456789 + 1000)); } [Test] public void Add_NanosOverflow() { - var t = new Timespec { tv_sec = 12345, tv_nsec = 999999999 }; + var t = new Timespec { tv_sec = new IntPtr(12345), tv_nsec = new IntPtr(999999999) }; var result = t.Add(TimeSpan.FromTicks(TimeSpan.TicksPerSecond * 10 + 10)); - Assert.AreEqual(result.tv_sec, 12356); - Assert.AreEqual(result.tv_nsec, 999); + Assert.AreEqual(result.tv_sec, new IntPtr(12356)); + Assert.AreEqual(result.tv_nsec, new IntPtr(999)); } } } diff --git a/src/csharp/InteropClient/Client.cs b/src/csharp/InteropClient/Client.cs index 9f5815050e..fcc6a572e4 100644 --- a/src/csharp/InteropClient/Client.cs +++ b/src/csharp/InteropClient/Client.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Collections.Generic; using NUnit.Framework; @@ -60,6 +93,8 @@ namespace Google.GRPC.Interop private void Run() { + GrpcEnvironment.Initialize(); + string addr = string.Format("{0}:{1}", options.serverHost, options.serverPort); using (Channel channel = new Channel(addr)) { diff --git a/src/csharp/MathClient/MathClient.cs b/src/csharp/MathClient/MathClient.cs index 45222abb79..a54c8e3809 100644 --- a/src/csharp/MathClient/MathClient.cs +++ b/src/csharp/MathClient/MathClient.cs @@ -1,3 +1,36 @@ +#region Copyright notice and license + +// Copyright 2015, 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. + +#endregion + using System; using System.Runtime.InteropServices; using Google.GRPC.Core; @@ -9,6 +42,8 @@ namespace math { public static void Main (string[] args) { + GrpcEnvironment.Initialize(); + using (Channel channel = new Channel("127.0.0.1:23456")) { MathGrpc.IMathServiceClient stub = new MathGrpc.MathServiceClientStub(channel); diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index 74d11c655b..c7949af44e 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -1,9 +1,56 @@ +/* + * + * Copyright 2015, 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/support/port_platform.h> #include <grpc/grpc.h> #include <grpc/support/log.h> #include <grpc/support/slice.h> #include <string.h> +#ifdef GPR_WIN32 +#define GPR_EXPORT __declspec(dllexport) +#define GPR_CALLTYPE __stdcall +#endif + +#ifndef GPR_EXPORT +#define GPR_EXPORT +#endif + +#ifndef GPR_CALLTYPE +#define GPR_CALLTYPE +#endif + grpc_byte_buffer *string_to_byte_buffer(const char *buffer, size_t len) { gpr_slice slice = gpr_slice_from_copied_buffer(buffer, len); grpc_byte_buffer *bb = grpc_byte_buffer_create(&slice, 1); @@ -11,40 +58,119 @@ grpc_byte_buffer *string_to_byte_buffer(const char *buffer, size_t len) { return bb; } -void grpc_call_start_write_from_copied_buffer(grpc_call *call, - const char *buffer, size_t len, - void *tag, gpr_uint32 flags) { - grpc_byte_buffer *byte_buffer = string_to_byte_buffer(buffer, len); - GPR_ASSERT(grpc_call_start_write_old(call, byte_buffer, tag, flags) == - GRPC_CALL_OK); - grpc_byte_buffer_destroy(byte_buffer); +/* Init & shutdown */ + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_init(void) { grpc_init(); } + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_shutdown(void) { grpc_shutdown(); } + +/* Completion queue */ + +GPR_EXPORT grpc_completion_queue *GPR_CALLTYPE +grpcsharp_completion_queue_create(void) { + return grpc_completion_queue_create(); +} + +GPR_EXPORT grpc_event *GPR_CALLTYPE +grpcsharp_completion_queue_next(grpc_completion_queue *cq, + gpr_timespec deadline) { + return grpc_completion_queue_next(cq, deadline); +} + +GPR_EXPORT grpc_event *GPR_CALLTYPE +grpcsharp_completion_queue_pluck(grpc_completion_queue *cq, void *tag, + gpr_timespec deadline) { + return grpc_completion_queue_pluck(cq, tag, deadline); +} + +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_completion_queue_shutdown(grpc_completion_queue *cq) { + grpc_completion_queue_shutdown(cq); +} + +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_completion_queue_destroy(grpc_completion_queue *cq) { + grpc_completion_queue_destroy(cq); +} + +GPR_EXPORT grpc_completion_type GPR_CALLTYPE +grpcsharp_completion_queue_next_with_callback(grpc_completion_queue *cq) { + grpc_event *ev; + grpc_completion_type t; + void(GPR_CALLTYPE * callback)(grpc_event *); + + ev = grpc_completion_queue_next(cq, gpr_inf_future); + t = ev->type; + if (ev->tag) { + /* call the callback in ev->tag */ + /* C forbids to cast object pointers to function pointers, so + * we cast to intptr first. + */ + callback = (void(GPR_CALLTYPE *)(grpc_event *))(gpr_intptr)ev->tag; + (*callback)(ev); + } + grpc_event_finish(ev); + + /* return completion type to allow some handling for events that have no + * tag - such as GRPC_QUEUE_SHUTDOWN + */ + return t; +} + +/* Channel */ + +GPR_EXPORT grpc_channel *GPR_CALLTYPE +grpcsharp_channel_create(const char *target, const grpc_channel_args *args) { + return grpc_channel_create(target, args); +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_channel_destroy(grpc_channel *channel) { + grpc_channel_destroy(channel); } -grpc_completion_type grpc_event_type(const grpc_event *event) { +GPR_EXPORT grpc_call *GPR_CALLTYPE +grpcsharp_channel_create_call_old(grpc_channel *channel, const char *method, + const char *host, gpr_timespec deadline) { + return grpc_channel_create_call_old(channel, method, host, deadline); +} + +/* Event */ + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_event_finish(grpc_event *event) { + grpc_event_finish(event); +} + +GPR_EXPORT grpc_completion_type GPR_CALLTYPE +grpcsharp_event_type(const grpc_event *event) { return event->type; } -grpc_op_error grpc_event_write_accepted(const grpc_event *event) { +GPR_EXPORT grpc_op_error GPR_CALLTYPE +grpcsharp_event_write_accepted(const grpc_event *event) { GPR_ASSERT(event->type == GRPC_WRITE_ACCEPTED); return event->data.invoke_accepted; } -grpc_op_error grpc_event_finish_accepted(const grpc_event *event) { +GPR_EXPORT grpc_op_error GPR_CALLTYPE +grpcsharp_event_finish_accepted(const grpc_event *event) { GPR_ASSERT(event->type == GRPC_FINISH_ACCEPTED); return event->data.finish_accepted; } -grpc_status_code grpc_event_finished_status(const grpc_event *event) { +GPR_EXPORT grpc_status_code GPR_CALLTYPE +grpcsharp_event_finished_status(const grpc_event *event) { GPR_ASSERT(event->type == GRPC_FINISHED); return event->data.finished.status; } -const char *grpc_event_finished_details(const grpc_event *event) { +GPR_EXPORT const char *GPR_CALLTYPE +grpcsharp_event_finished_details(const grpc_event *event) { GPR_ASSERT(event->type == GRPC_FINISHED); return event->data.finished.details; } -gpr_intptr grpc_event_read_length(const grpc_event *event) { +GPR_EXPORT gpr_intptr GPR_CALLTYPE +grpcsharp_event_read_length(const grpc_event *event) { GPR_ASSERT(event->type == GRPC_READ); if (!event->data.read) { return -1; @@ -56,7 +182,8 @@ gpr_intptr grpc_event_read_length(const grpc_event *event) { * Copies data from read event to a buffer. Fatal error occurs if * buffer is too small. */ -void grpc_event_read_copy_to_buffer(const grpc_event *event, char *buffer, +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_event_read_copy_to_buffer(const grpc_event *event, char *buffer, size_t buffer_len) { grpc_byte_buffer_reader *reader; gpr_slice slice; @@ -77,37 +204,142 @@ void grpc_event_read_copy_to_buffer(const grpc_event *event, char *buffer, grpc_byte_buffer_reader_destroy(reader); } -grpc_call *grpc_event_call(const grpc_event *event) { +GPR_EXPORT grpc_call *GPR_CALLTYPE +grpcsharp_event_call(const grpc_event *event) { /* we only allow this for newly incoming server calls. */ GPR_ASSERT(event->type == GRPC_SERVER_RPC_NEW); return event->call; } -const char *grpc_event_server_rpc_new_method(const grpc_event *event) { +GPR_EXPORT const char *GPR_CALLTYPE +grpcsharp_event_server_rpc_new_method(const grpc_event *event) { GPR_ASSERT(event->type == GRPC_SERVER_RPC_NEW); return event->data.server_rpc_new.method; } -grpc_completion_type grpc_completion_queue_next_with_callback( - grpc_completion_queue *cq) { - grpc_event *ev; - grpc_completion_type t; - void (*callback)(grpc_event *); +/* Timespec */ - ev = grpc_completion_queue_next(cq, gpr_inf_future); - t = ev->type; - if (ev->tag) { - /* call the callback in ev->tag */ - /* C forbids to cast object pointers to function pointers, so - * we cast to intptr first. - */ - callback = (void (*)(grpc_event *))(gpr_intptr)ev->tag; - (*callback)(ev); - } - grpc_event_finish(ev); +GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_now(void) { return gpr_now(); } - /* return completion type to allow some handling for events that have no - * tag - such as GRPC_QUEUE_SHUTDOWN - */ - return t; +GPR_EXPORT gpr_timespec GPR_CALLTYPE gprsharp_inf_future(void) { + return gpr_inf_future; +} + +GPR_EXPORT gpr_int32 GPR_CALLTYPE gprsharp_sizeof_timespec(void) { + return sizeof(gpr_timespec); +} + +/* Call */ + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_add_metadata_old(grpc_call *call, grpc_metadata *metadata, + gpr_uint32 flags) { + return grpc_call_add_metadata_old(call, metadata, flags); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_invoke_old(grpc_call *call, grpc_completion_queue *cq, + void *metadata_read_tag, void *finished_tag, + gpr_uint32 flags) { + return grpc_call_invoke_old(call, cq, metadata_read_tag, finished_tag, flags); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_server_accept_old(grpc_call *call, grpc_completion_queue *cq, + void *finished_tag) { + return grpc_call_server_accept_old(call, cq, finished_tag); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_server_end_initial_metadata_old(grpc_call *call, + gpr_uint32 flags) { + return grpc_call_server_end_initial_metadata_old(call, flags); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE grpcsharp_call_cancel(grpc_call *call) { + return grpc_call_cancel(call); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_cancel_with_status(grpc_call *call, grpc_status_code status, + const char *description) { + return grpc_call_cancel_with_status(call, status, description); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_start_write_old(grpc_call *call, grpc_byte_buffer *byte_buffer, + void *tag, gpr_uint32 flags) { + return grpc_call_start_write_old(call, byte_buffer, tag, flags); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_start_write_status_old(grpc_call *call, + grpc_status_code status_code, + const char *status_message, void *tag) { + return grpc_call_start_write_status_old(call, status_code, status_message, + tag); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_writes_done_old(grpc_call *call, void *tag) { + return grpc_call_writes_done_old(call, tag); +} + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_call_start_read_old(grpc_call *call, void *tag) { + return grpc_call_start_read_old(call, tag); +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_call_destroy(grpc_call *call) { + grpc_call_destroy(call); +} + +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_call_start_write_from_copied_buffer(grpc_call *call, + const char *buffer, size_t len, + void *tag, gpr_uint32 flags) { + grpc_byte_buffer *byte_buffer = string_to_byte_buffer(buffer, len); + GPR_ASSERT(grpc_call_start_write_old(call, byte_buffer, tag, flags) == + GRPC_CALL_OK); + grpc_byte_buffer_destroy(byte_buffer); +} + +/* Server */ + +GPR_EXPORT grpc_call_error GPR_CALLTYPE +grpcsharp_server_request_call_old(grpc_server *server, void *tag_new) { + return grpc_server_request_call_old(server, tag_new); +} + +GPR_EXPORT grpc_server *GPR_CALLTYPE +grpcsharp_server_create(grpc_completion_queue *cq, + const grpc_channel_args *args) { + return grpc_server_create(cq, args); +} + +GPR_EXPORT int GPR_CALLTYPE +grpcsharp_server_add_http2_port(grpc_server *server, const char *addr) { + return grpc_server_add_http2_port(server, addr); +} + +GPR_EXPORT int GPR_CALLTYPE +grpcsharp_server_add_secure_http2_port(grpc_server *server, const char *addr) { + return grpc_server_add_secure_http2_port(server, addr); +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_start(grpc_server *server) { + grpc_server_start(server); +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_shutdown(grpc_server *server) { + grpc_server_shutdown(server); +} + +GPR_EXPORT void GPR_CALLTYPE +grpcsharp_server_shutdown_and_notify(grpc_server *server, void *tag) { + grpc_server_shutdown_and_notify(server, tag); +} + +GPR_EXPORT void GPR_CALLTYPE grpcsharp_server_destroy(grpc_server *server) { + grpc_server_destroy(server); } diff --git a/src/node/binding.gyp b/src/node/binding.gyp index cf2a6acb04..fb4c779f8e 100644 --- a/src/node/binding.gyp +++ b/src/node/binding.gyp @@ -9,14 +9,15 @@ 'include_dirs': [ "<!(nodejs -e \"require('nan')\")" ], - 'cxxflags': [ + 'cflags': [ + '-std=c++11', '-Wall', '-pthread', '-pedantic', '-g', '-zdefs' - '-Werror', - ], + '-Werror' + ], 'ldflags': [ '-g' ], @@ -33,11 +34,9 @@ "ext/channel.cc", "ext/completion_queue_async_worker.cc", "ext/credentials.cc", - "ext/event.cc", "ext/node_grpc.cc", "ext/server.cc", "ext/server_credentials.cc", - "ext/tag.cc", "ext/timeval.cc" ], 'conditions' : [ diff --git a/src/node/ext/call.cc b/src/node/ext/call.cc index 23aead07b2..9ed389f3bc 100644 --- a/src/node/ext/call.cc +++ b/src/node/ext/call.cc @@ -1,6 +1,6 @@ /* * - * Copyright 2014, Google Inc. + * Copyright 2015, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,17 +31,25 @@ * */ +#include <memory> +#include <vector> +#include <map> + #include <node.h> #include "grpc/support/log.h" #include "grpc/grpc.h" +#include "grpc/support/alloc.h" #include "grpc/support/time.h" #include "byte_buffer.h" #include "call.h" #include "channel.h" #include "completion_queue_async_worker.h" #include "timeval.h" -#include "tag.h" + +using std::unique_ptr; +using std::shared_ptr; +using std::vector; namespace grpc { namespace node { @@ -49,6 +57,7 @@ namespace node { using ::node::Buffer; using v8::Arguments; using v8::Array; +using v8::Boolean; using v8::Exception; using v8::External; using v8::Function; @@ -68,37 +77,372 @@ using v8::Value; Persistent<Function> Call::constructor; Persistent<FunctionTemplate> Call::fun_tpl; -Call::Call(grpc_call *call) : wrapped_call(call) {} -Call::~Call() { grpc_call_destroy(wrapped_call); } +bool CreateMetadataArray(Handle<Object> metadata, grpc_metadata_array *array, + shared_ptr<Resources> resources) { + NanScope(); + grpc_metadata_array_init(array); + Handle<Array> keys(metadata->GetOwnPropertyNames()); + for (unsigned int i = 0; i < keys->Length(); i++) { + Handle<String> current_key(keys->Get(i)->ToString()); + if (!metadata->Get(current_key)->IsArray()) { + return false; + } + array->capacity += Local<Array>::Cast(metadata->Get(current_key))->Length(); + } + array->metadata = reinterpret_cast<grpc_metadata*>( + gpr_malloc(array->capacity * sizeof(grpc_metadata))); + for (unsigned int i = 0; i < keys->Length(); i++) { + Handle<String> current_key(keys->Get(i)->ToString()); + NanUtf8String *utf8_key = new NanUtf8String(current_key); + resources->strings.push_back(unique_ptr<NanUtf8String>(utf8_key)); + Handle<Array> values = Local<Array>::Cast(metadata->Get(current_key)); + for (unsigned int j = 0; j < values->Length(); j++) { + Handle<Value> value = values->Get(j); + grpc_metadata *current = &array->metadata[array->count]; + current->key = **utf8_key; + if (Buffer::HasInstance(value)) { + current->value = Buffer::Data(value); + current->value_length = Buffer::Length(value); + Persistent<Value> handle; + NanAssignPersistent(handle, value); + resources->handles.push_back(unique_ptr<PersistentHolder>( + new PersistentHolder(handle))); + } else if (value->IsString()) { + Handle<String> string_value = value->ToString(); + NanUtf8String *utf8_value = new NanUtf8String(string_value); + resources->strings.push_back(unique_ptr<NanUtf8String>(utf8_value)); + current->value = **utf8_value; + current->value_length = string_value->Length(); + } else { + return false; + } + array->count += 1; + } + } + return true; +} + +Handle<Value> ParseMetadata(const grpc_metadata_array *metadata_array) { + NanEscapableScope(); + grpc_metadata *metadata_elements = metadata_array->metadata; + size_t length = metadata_array->count; + std::map<const char*, size_t> size_map; + std::map<const char*, size_t> index_map; + + for (unsigned int i = 0; i < length; i++) { + const char *key = metadata_elements[i].key; + if (size_map.count(key)) { + size_map[key] += 1; + } + index_map[key] = 0; + } + Handle<Object> metadata_object = NanNew<Object>(); + for (unsigned int i = 0; i < length; i++) { + grpc_metadata* elem = &metadata_elements[i]; + Handle<String> key_string = String::New(elem->key); + Handle<Array> array; + if (metadata_object->Has(key_string)) { + array = Handle<Array>::Cast(metadata_object->Get(key_string)); + } else { + array = NanNew<Array>(size_map[elem->key]); + metadata_object->Set(key_string, array); + } + array->Set(index_map[elem->key], + MakeFastBuffer( + NanNewBufferHandle(elem->value, elem->value_length))); + index_map[elem->key] += 1; + } + return NanEscapeScope(metadata_object); +} + +Handle<Value> Op::GetOpType() const { + NanEscapableScope(); + return NanEscapeScope(NanNew<String>(GetTypeString())); +} + +class SendMetadataOp : public Op { + public: + Handle<Value> GetNodeValue() const { + NanEscapableScope(); + return NanEscapeScope(NanTrue()); + } + bool ParseOp(Handle<Value> value, grpc_op *out, + shared_ptr<Resources> resources) { + if (!value->IsObject()) { + return false; + } + grpc_metadata_array array; + if (!CreateMetadataArray(value->ToObject(), &array, resources)) { + return false; + } + out->data.send_initial_metadata.count = array.count; + out->data.send_initial_metadata.metadata = array.metadata; + return true; + } + protected: + std::string GetTypeString() const { + return "send metadata"; + } +}; + +class SendMessageOp : public Op { + public: + Handle<Value> GetNodeValue() const { + NanEscapableScope(); + return NanEscapeScope(NanTrue()); + } + bool ParseOp(Handle<Value> value, grpc_op *out, + shared_ptr<Resources> resources) { + if (!Buffer::HasInstance(value)) { + return false; + } + out->data.send_message = BufferToByteBuffer(value); + Persistent<Value> handle; + NanAssignPersistent(handle, value); + resources->handles.push_back(unique_ptr<PersistentHolder>( + new PersistentHolder(handle))); + return true; + } + protected: + std::string GetTypeString() const { + return "send message"; + } +}; + +class SendClientCloseOp : public Op { + public: + Handle<Value> GetNodeValue() const { + NanEscapableScope(); + return NanEscapeScope(NanTrue()); + } + bool ParseOp(Handle<Value> value, grpc_op *out, + shared_ptr<Resources> resources) { + return true; + } + protected: + std::string GetTypeString() const { + return "client close"; + } +}; + +class SendServerStatusOp : public Op { + public: + Handle<Value> GetNodeValue() const { + NanEscapableScope(); + return NanEscapeScope(NanTrue()); + } + bool ParseOp(Handle<Value> value, grpc_op *out, + shared_ptr<Resources> resources) { + if (!value->IsObject()) { + return false; + } + Handle<Object> server_status = value->ToObject(); + if (!server_status->Get(NanNew("metadata"))->IsObject()) { + return false; + } + if (!server_status->Get(NanNew("code"))->IsUint32()) { + return false; + } + if (!server_status->Get(NanNew("details"))->IsString()) { + return false; + } + grpc_metadata_array array; + if (!CreateMetadataArray(server_status->Get(NanNew("metadata"))-> + ToObject(), + &array, resources)) { + return false; + } + out->data.send_status_from_server.trailing_metadata_count = array.count; + out->data.send_status_from_server.trailing_metadata = array.metadata; + out->data.send_status_from_server.status = + static_cast<grpc_status_code>( + server_status->Get(NanNew("code"))->Uint32Value()); + NanUtf8String *str = new NanUtf8String( + server_status->Get(NanNew("details"))); + resources->strings.push_back(unique_ptr<NanUtf8String>(str)); + out->data.send_status_from_server.status_details = **str; + return true; + } + protected: + std::string GetTypeString() const { + return "send status"; + } +}; + +class GetMetadataOp : public Op { + public: + GetMetadataOp() { + grpc_metadata_array_init(&recv_metadata); + } + + ~GetMetadataOp() { + grpc_metadata_array_destroy(&recv_metadata); + } + + Handle<Value> GetNodeValue() const { + NanEscapableScope(); + return NanEscapeScope(ParseMetadata(&recv_metadata)); + } + + bool ParseOp(Handle<Value> value, grpc_op *out, + shared_ptr<Resources> resources) { + out->data.recv_initial_metadata = &recv_metadata; + return true; + } + + protected: + std::string GetTypeString() const { + return "metadata"; + } + + private: + grpc_metadata_array recv_metadata; +}; + +class ReadMessageOp : public Op { + public: + ReadMessageOp() { + recv_message = NULL; + } + ~ReadMessageOp() { + if (recv_message != NULL) { + gpr_free(recv_message); + } + } + Handle<Value> GetNodeValue() const { + NanEscapableScope(); + return NanEscapeScope(ByteBufferToBuffer(recv_message)); + } + + bool ParseOp(Handle<Value> value, grpc_op *out, + shared_ptr<Resources> resources) { + out->data.recv_message = &recv_message; + return true; + } + + protected: + std::string GetTypeString() const { + return "read"; + } + + private: + grpc_byte_buffer *recv_message; +}; + +class ClientStatusOp : public Op { + public: + ClientStatusOp() { + grpc_metadata_array_init(&metadata_array); + status_details = NULL; + details_capacity = 0; + } + + ~ClientStatusOp() { + grpc_metadata_array_destroy(&metadata_array); + gpr_free(status_details); + } + + bool ParseOp(Handle<Value> value, grpc_op *out, + shared_ptr<Resources> resources) { + out->data.recv_status_on_client.trailing_metadata = &metadata_array; + out->data.recv_status_on_client.status = &status; + out->data.recv_status_on_client.status_details = &status_details; + out->data.recv_status_on_client.status_details_capacity = &details_capacity; + return true; + } + + Handle<Value> GetNodeValue() const { + NanEscapableScope(); + Handle<Object> status_obj = NanNew<Object>(); + status_obj->Set(NanNew("code"), NanNew<Number>(status)); + if (status_details != NULL) { + status_obj->Set(NanNew("details"), String::New(status_details)); + } + status_obj->Set(NanNew("metadata"), ParseMetadata(&metadata_array)); + return NanEscapeScope(status_obj); + } + protected: + std::string GetTypeString() const { + return "status"; + } + private: + grpc_metadata_array metadata_array; + grpc_status_code status; + char *status_details; + size_t details_capacity; +}; + +class ServerCloseResponseOp : public Op { + public: + Handle<Value> GetNodeValue() const { + NanEscapableScope(); + return NanEscapeScope(NanNew<Boolean>(cancelled)); + } + + bool ParseOp(Handle<Value> value, grpc_op *out, + shared_ptr<Resources> resources) { + out->data.recv_close_on_server.cancelled = &cancelled; + return true; + } + + protected: + std::string GetTypeString() const { + return "cancelled"; + } + + private: + int cancelled; +}; + +tag::tag(NanCallback *callback, OpVec *ops, + shared_ptr<Resources> resources) : + callback(callback), ops(ops), resources(resources){ +} + +tag::~tag() { + delete callback; + delete ops; +} + +Handle<Value> GetTagNodeValue(void *tag) { + NanEscapableScope(); + struct tag *tag_struct = reinterpret_cast<struct tag *>(tag); + Handle<Object> tag_obj = NanNew<Object>(); + for (vector<unique_ptr<Op> >::iterator it = tag_struct->ops->begin(); + it != tag_struct->ops->end(); ++it) { + Op *op_ptr = it->get(); + tag_obj->Set(op_ptr->GetOpType(), op_ptr->GetNodeValue()); + } + return NanEscapeScope(tag_obj); +} + +NanCallback *GetTagCallback(void *tag) { + struct tag *tag_struct = reinterpret_cast<struct tag *>(tag); + return tag_struct->callback; +} + +void DestroyTag(void *tag) { + struct tag *tag_struct = reinterpret_cast<struct tag *>(tag); + delete tag_struct; +} + +Call::Call(grpc_call *call) : wrapped_call(call) { +} + +Call::~Call() { + grpc_call_destroy(wrapped_call); +} void Call::Init(Handle<Object> exports) { NanScope(); Local<FunctionTemplate> tpl = FunctionTemplate::New(New); tpl->SetClassName(NanNew("Call")); tpl->InstanceTemplate()->SetInternalFieldCount(1); - NanSetPrototypeTemplate(tpl, "addMetadata", - FunctionTemplate::New(AddMetadata)->GetFunction()); - NanSetPrototypeTemplate(tpl, "invoke", - FunctionTemplate::New(Invoke)->GetFunction()); - NanSetPrototypeTemplate(tpl, "serverAccept", - FunctionTemplate::New(ServerAccept)->GetFunction()); - NanSetPrototypeTemplate( - tpl, "serverEndInitialMetadata", - FunctionTemplate::New(ServerEndInitialMetadata)->GetFunction()); + NanSetPrototypeTemplate(tpl, "startBatch", + FunctionTemplate::New(StartBatch)->GetFunction()); NanSetPrototypeTemplate(tpl, "cancel", FunctionTemplate::New(Cancel)->GetFunction()); - NanSetPrototypeTemplate(tpl, "startWrite", - FunctionTemplate::New(StartWrite)->GetFunction()); - NanSetPrototypeTemplate( - tpl, "startWriteStatus", - FunctionTemplate::New(StartWriteStatus)->GetFunction()); - NanSetPrototypeTemplate(tpl, "writesDone", - FunctionTemplate::New(WritesDone)->GetFunction()); - NanSetPrototypeTemplate(tpl, "startReadMetadata", - FunctionTemplate::New(WritesDone)->GetFunction()); - NanSetPrototypeTemplate(tpl, "startRead", - FunctionTemplate::New(StartRead)->GetFunction()); NanAssignPersistent(fun_tpl, tpl); NanAssignPersistent(constructor, tpl->GetFunction()); constructor->Set(NanNew("WRITE_BUFFER_HINT"), @@ -152,9 +496,9 @@ NAN_METHOD(Call::New) { NanUtf8String method(args[1]); double deadline = args[2]->NumberValue(); grpc_channel *wrapped_channel = channel->GetWrappedChannel(); - grpc_call *wrapped_call = grpc_channel_create_call_old( - wrapped_channel, *method, channel->GetHost(), - MillisecondsToTimespec(deadline)); + grpc_call *wrapped_call = grpc_channel_create_call( + wrapped_channel, CompletionQueueAsyncWorker::GetQueue(), *method, + channel->GetHost(), MillisecondsToTimespec(deadline)); call = new Call(wrapped_call); args.This()->SetHiddenValue(String::NewSymbol("channel_"), channel_object); @@ -168,119 +512,74 @@ NAN_METHOD(Call::New) { } } -NAN_METHOD(Call::AddMetadata) { +NAN_METHOD(Call::StartBatch) { NanScope(); if (!HasInstance(args.This())) { - return NanThrowTypeError("addMetadata can only be called on Call objects"); + return NanThrowTypeError("startBatch can only be called on Call objects"); } - Call *call = ObjectWrap::Unwrap<Call>(args.This()); if (!args[0]->IsObject()) { - return NanThrowTypeError("addMetadata's first argument must be an object"); - } - Handle<Object> metadata = args[0]->ToObject(); - Handle<Array> keys(metadata->GetOwnPropertyNames()); - for (unsigned int i = 0; i < keys->Length(); i++) { - Handle<String> current_key(keys->Get(i)->ToString()); - if (!metadata->Get(current_key)->IsArray()) { - return NanThrowTypeError( - "addMetadata's first argument's values must be arrays"); - } - NanUtf8String utf8_key(current_key); - Handle<Array> values = Local<Array>::Cast(metadata->Get(current_key)); - for (unsigned int j = 0; j < values->Length(); j++) { - Handle<Value> value = values->Get(j); - grpc_metadata metadata; - grpc_call_error error; - metadata.key = *utf8_key; - if (Buffer::HasInstance(value)) { - metadata.value = Buffer::Data(value); - metadata.value_length = Buffer::Length(value); - error = grpc_call_add_metadata_old(call->wrapped_call, &metadata, 0); - } else if (value->IsString()) { - Handle<String> string_value = value->ToString(); - NanUtf8String utf8_value(string_value); - metadata.value = *utf8_value; - metadata.value_length = string_value->Length(); - gpr_log(GPR_DEBUG, "adding metadata: %s, %s, %d", metadata.key, - metadata.value, metadata.value_length); - error = grpc_call_add_metadata_old(call->wrapped_call, &metadata, 0); - } else { - return NanThrowTypeError( - "addMetadata values must be strings or buffers"); - } - if (error != GRPC_CALL_OK) { - return NanThrowError("addMetadata failed", error); - } - } - } - NanReturnUndefined(); -} - -NAN_METHOD(Call::Invoke) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("invoke can only be called on Call objects"); - } - if (!args[0]->IsFunction()) { - return NanThrowTypeError("invoke's first argument must be a function"); + return NanThrowError("startBatch's first argument must be an object"); } if (!args[1]->IsFunction()) { - return NanThrowTypeError("invoke's second argument must be a function"); - } - if (!args[2]->IsUint32()) { - return NanThrowTypeError("invoke's third argument must be integer flags"); - } - Call *call = ObjectWrap::Unwrap<Call>(args.This()); - unsigned int flags = args[3]->Uint32Value(); - grpc_call_error error = grpc_call_invoke_old( - call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(), - CreateTag(args[0], args.This()), CreateTag(args[1], args.This()), flags); - if (error == GRPC_CALL_OK) { - CompletionQueueAsyncWorker::Next(); - CompletionQueueAsyncWorker::Next(); - } else { - return NanThrowError("invoke failed", error); - } - NanReturnUndefined(); -} - -NAN_METHOD(Call::ServerAccept) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("accept can only be called on Call objects"); - } - if (!args[0]->IsFunction()) { - return NanThrowTypeError("accept's first argument must be a function"); + return NanThrowError("startBatch's second argument must be a callback"); } + Handle<Function> callback_func = args[1].As<Function>(); Call *call = ObjectWrap::Unwrap<Call>(args.This()); - grpc_call_error error = grpc_call_server_accept_old( - call->wrapped_call, CompletionQueueAsyncWorker::GetQueue(), - CreateTag(args[0], args.This())); - if (error == GRPC_CALL_OK) { - CompletionQueueAsyncWorker::Next(); - } else { - return NanThrowError("serverAccept failed", error); - } - NanReturnUndefined(); -} - -NAN_METHOD(Call::ServerEndInitialMetadata) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError( - "serverEndInitialMetadata can only be called on Call objects"); - } - if (!args[0]->IsUint32()) { - return NanThrowTypeError( - "serverEndInitialMetadata's second argument must be integer flags"); + shared_ptr<Resources> resources(new Resources); + Handle<Object> obj = args[0]->ToObject(); + Handle<Array> keys = obj->GetOwnPropertyNames(); + size_t nops = keys->Length(); + vector<grpc_op> ops(nops); + unique_ptr<OpVec> op_vector(new OpVec()); + for (unsigned int i = 0; i < nops; i++) { + unique_ptr<Op> op; + if (!keys->Get(i)->IsUint32()) { + return NanThrowError( + "startBatch's first argument's keys must be integers"); + } + uint32_t type = keys->Get(i)->Uint32Value(); + ops[i].op = static_cast<grpc_op_type>(type); + switch (type) { + case GRPC_OP_SEND_INITIAL_METADATA: + op.reset(new SendMetadataOp()); + break; + case GRPC_OP_SEND_MESSAGE: + op.reset(new SendMessageOp()); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + op.reset(new SendClientCloseOp()); + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + op.reset(new SendServerStatusOp()); + break; + case GRPC_OP_RECV_INITIAL_METADATA: + op.reset(new GetMetadataOp()); + break; + case GRPC_OP_RECV_MESSAGE: + op.reset(new ReadMessageOp()); + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + op.reset(new ClientStatusOp()); + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + op.reset(new ServerCloseResponseOp()); + break; + default: + return NanThrowError("Argument object had an unrecognized key"); + } + if (!op->ParseOp(obj->Get(type), &ops[i], resources)) { + return NanThrowTypeError("Incorrectly typed arguments to startBatch"); + } + op_vector->push_back(std::move(op)); } - Call *call = ObjectWrap::Unwrap<Call>(args.This()); - unsigned int flags = args[1]->Uint32Value(); - grpc_call_error error = - grpc_call_server_end_initial_metadata_old(call->wrapped_call, flags); + NanCallback *callback = new NanCallback(callback_func); + grpc_call_error error = grpc_call_start_batch( + call->wrapped_call, &ops[0], nops, new struct tag( + callback, op_vector.release(), resources)); if (error != GRPC_CALL_OK) { - return NanThrowError("serverEndInitialMetadata failed", error); + return NanThrowError("startBatch failed", error); } + CompletionQueueAsyncWorker::Next(); NanReturnUndefined(); } @@ -297,102 +596,5 @@ NAN_METHOD(Call::Cancel) { NanReturnUndefined(); } -NAN_METHOD(Call::StartWrite) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("startWrite can only be called on Call objects"); - } - if (!Buffer::HasInstance(args[0])) { - return NanThrowTypeError("startWrite's first argument must be a Buffer"); - } - if (!args[1]->IsFunction()) { - return NanThrowTypeError("startWrite's second argument must be a function"); - } - if (!args[2]->IsUint32()) { - return NanThrowTypeError( - "startWrite's third argument must be integer flags"); - } - Call *call = ObjectWrap::Unwrap<Call>(args.This()); - grpc_byte_buffer *buffer = BufferToByteBuffer(args[0]); - unsigned int flags = args[2]->Uint32Value(); - grpc_call_error error = grpc_call_start_write_old( - call->wrapped_call, buffer, CreateTag(args[1], args.This()), flags); - if (error == GRPC_CALL_OK) { - CompletionQueueAsyncWorker::Next(); - } else { - return NanThrowError("startWrite failed", error); - } - NanReturnUndefined(); -} - -NAN_METHOD(Call::StartWriteStatus) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError( - "startWriteStatus can only be called on Call objects"); - } - if (!args[0]->IsUint32()) { - return NanThrowTypeError( - "startWriteStatus's first argument must be a status code"); - } - if (!args[1]->IsString()) { - return NanThrowTypeError( - "startWriteStatus's second argument must be a string"); - } - if (!args[2]->IsFunction()) { - return NanThrowTypeError( - "startWriteStatus's third argument must be a function"); - } - Call *call = ObjectWrap::Unwrap<Call>(args.This()); - NanUtf8String details(args[1]); - grpc_call_error error = grpc_call_start_write_status_old( - call->wrapped_call, (grpc_status_code)args[0]->Uint32Value(), *details, - CreateTag(args[2], args.This())); - if (error == GRPC_CALL_OK) { - CompletionQueueAsyncWorker::Next(); - } else { - return NanThrowError("startWriteStatus failed", error); - } - NanReturnUndefined(); -} - -NAN_METHOD(Call::WritesDone) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("writesDone can only be called on Call objects"); - } - if (!args[0]->IsFunction()) { - return NanThrowTypeError("writesDone's first argument must be a function"); - } - Call *call = ObjectWrap::Unwrap<Call>(args.This()); - grpc_call_error error = grpc_call_writes_done_old( - call->wrapped_call, CreateTag(args[0], args.This())); - if (error == GRPC_CALL_OK) { - CompletionQueueAsyncWorker::Next(); - } else { - return NanThrowError("writesDone failed", error); - } - NanReturnUndefined(); -} - -NAN_METHOD(Call::StartRead) { - NanScope(); - if (!HasInstance(args.This())) { - return NanThrowTypeError("startRead can only be called on Call objects"); - } - if (!args[0]->IsFunction()) { - return NanThrowTypeError("startRead's first argument must be a function"); - } - Call *call = ObjectWrap::Unwrap<Call>(args.This()); - grpc_call_error error = grpc_call_start_read_old( - call->wrapped_call, CreateTag(args[0], args.This())); - if (error == GRPC_CALL_OK) { - CompletionQueueAsyncWorker::Next(); - } else { - return NanThrowError("startRead failed", error); - } - NanReturnUndefined(); -} - } // namespace node } // namespace grpc diff --git a/src/node/ext/call.h b/src/node/ext/call.h index 1924a1bf42..dbdb8e2ff6 100644 --- a/src/node/ext/call.h +++ b/src/node/ext/call.h @@ -34,15 +34,71 @@ #ifndef NET_GRPC_NODE_CALL_H_ #define NET_GRPC_NODE_CALL_H_ +#include <memory> +#include <vector> + #include <node.h> #include <nan.h> #include "grpc/grpc.h" #include "channel.h" + namespace grpc { namespace node { +using std::unique_ptr; +using std::shared_ptr; + +v8::Handle<v8::Value> ParseMetadata(const grpc_metadata_array *metadata_array); + +class PersistentHolder { + public: + explicit PersistentHolder(v8::Persistent<v8::Value> persist) : + persist(persist) { + } + + ~PersistentHolder() { + NanDisposePersistent(persist); + } + + private: + v8::Persistent<v8::Value> persist; +}; + +struct Resources { + std::vector<unique_ptr<NanUtf8String> > strings; + std::vector<unique_ptr<PersistentHolder> > handles; +}; + +class Op { + public: + virtual v8::Handle<v8::Value> GetNodeValue() const = 0; + virtual bool ParseOp(v8::Handle<v8::Value> value, grpc_op *out, + shared_ptr<Resources> resources) = 0; + v8::Handle<v8::Value> GetOpType() const; + + protected: + virtual std::string GetTypeString() const = 0; +}; + +typedef std::vector<unique_ptr<Op>> OpVec; + +struct tag { + tag(NanCallback *callback, OpVec *ops, + shared_ptr<Resources> resources); + ~tag(); + NanCallback *callback; + OpVec *ops; + shared_ptr<Resources> resources; +}; + +v8::Handle<v8::Value> GetTagNodeValue(void *tag); + +NanCallback *GetTagCallback(void *tag); + +void DestroyTag(void *tag); + /* Wrapper class for grpc_call structs. */ class Call : public ::node::ObjectWrap { public: @@ -60,15 +116,8 @@ class Call : public ::node::ObjectWrap { Call &operator=(const Call &); static NAN_METHOD(New); - static NAN_METHOD(AddMetadata); - static NAN_METHOD(Invoke); - static NAN_METHOD(ServerAccept); - static NAN_METHOD(ServerEndInitialMetadata); + static NAN_METHOD(StartBatch); static NAN_METHOD(Cancel); - static NAN_METHOD(StartWrite); - static NAN_METHOD(StartWriteStatus); - static NAN_METHOD(WritesDone); - static NAN_METHOD(StartRead); static v8::Persistent<v8::Function> constructor; // Used for typechecking instances of this javascript class static v8::Persistent<v8::FunctionTemplate> fun_tpl; diff --git a/src/node/ext/completion_queue_async_worker.cc b/src/node/ext/completion_queue_async_worker.cc index 8de7db66d5..a1f390f64b 100644 --- a/src/node/ext/completion_queue_async_worker.cc +++ b/src/node/ext/completion_queue_async_worker.cc @@ -35,10 +35,10 @@ #include <nan.h> #include "grpc/grpc.h" +#include "grpc/support/log.h" #include "grpc/support/time.h" #include "completion_queue_async_worker.h" -#include "event.h" -#include "tag.h" +#include "call.h" namespace grpc { namespace node { @@ -58,6 +58,9 @@ CompletionQueueAsyncWorker::~CompletionQueueAsyncWorker() {} void CompletionQueueAsyncWorker::Execute() { result = grpc_completion_queue_next(queue, gpr_inf_future); + if (result->data.op_complete != GRPC_OP_OK) { + SetErrorMessage("The batch encountered an error"); + } } grpc_completion_queue *CompletionQueueAsyncWorker::GetQueue() { return queue; } @@ -75,14 +78,26 @@ void CompletionQueueAsyncWorker::Init(Handle<Object> exports) { void CompletionQueueAsyncWorker::HandleOKCallback() { NanScope(); - NanCallback event_callback(GetTagHandle(result->tag).As<Function>()); - Handle<Value> argv[] = {CreateEventObject(result)}; + NanCallback *callback = GetTagCallback(result->tag); + Handle<Value> argv[] = {NanNull(), GetTagNodeValue(result->tag)}; + + callback->Call(2, argv); DestroyTag(result->tag); grpc_event_finish(result); result = NULL; +} + +void CompletionQueueAsyncWorker::HandleErrorCallback() { + NanScope(); + NanCallback *callback = GetTagCallback(result->tag); + Handle<Value> argv[] = {NanError(ErrorMessage())}; - event_callback.Call(1, argv); + callback->Call(1, argv); + + DestroyTag(result->tag); + grpc_event_finish(result); + result = NULL; } } // namespace node diff --git a/src/node/ext/completion_queue_async_worker.h b/src/node/ext/completion_queue_async_worker.h index 2c928b7024..c04a303283 100644 --- a/src/node/ext/completion_queue_async_worker.h +++ b/src/node/ext/completion_queue_async_worker.h @@ -67,6 +67,8 @@ class CompletionQueueAsyncWorker : public NanAsyncWorker { completion_queue_next */ void HandleOKCallback(); + void HandleErrorCallback(); + private: grpc_event *result; diff --git a/src/node/ext/credentials.cc b/src/node/ext/credentials.cc index c8859ed941..b79c3e3019 100644 --- a/src/node/ext/credentials.cc +++ b/src/node/ext/credentials.cc @@ -63,7 +63,6 @@ Credentials::Credentials(grpc_credentials *credentials) : wrapped_credentials(credentials) {} Credentials::~Credentials() { - gpr_log(GPR_DEBUG, "Destroying credentials object"); grpc_credentials_release(wrapped_credentials); } diff --git a/src/node/ext/event.cc b/src/node/ext/event.cc deleted file mode 100644 index d59b68fb40..0000000000 --- a/src/node/ext/event.cc +++ /dev/null @@ -1,173 +0,0 @@ -/* - * - * Copyright 2015, 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 <map> - -#include <node.h> -#include <nan.h> -#include "grpc/grpc.h" -#include "byte_buffer.h" -#include "call.h" -#include "event.h" -#include "tag.h" -#include "timeval.h" - -namespace grpc { -namespace node { - -using ::node::Buffer; -using v8::Array; -using v8::Date; -using v8::Handle; -using v8::HandleScope; -using v8::Number; -using v8::Object; -using v8::Persistent; -using v8::String; -using v8::Value; - -Handle<Value> ParseMetadata(grpc_metadata *metadata_elements, size_t length) { - NanEscapableScope(); - std::map<const char*, size_t> size_map; - std::map<const char*, size_t> index_map; - - for (unsigned int i = 0; i < length; i++) { - const char *key = metadata_elements[i].key; - if (size_map.count(key)) { - size_map[key] += 1; - } - index_map[key] = 0; - } - Handle<Object> metadata_object = NanNew<Object>(); - for (unsigned int i = 0; i < length; i++) { - grpc_metadata* elem = &metadata_elements[i]; - Handle<String> key_string = String::New(elem->key); - Handle<Array> array; - if (metadata_object->Has(key_string)) { - array = Handle<Array>::Cast(metadata_object->Get(key_string)); - } else { - array = NanNew<Array>(size_map[elem->key]); - metadata_object->Set(key_string, array); - } - array->Set(index_map[elem->key], - MakeFastBuffer( - NanNewBufferHandle(elem->value, elem->value_length))); - index_map[elem->key] += 1; - } - return NanEscapeScope(metadata_object); -} - -Handle<Value> GetEventData(grpc_event *event) { - NanEscapableScope(); - size_t count; - grpc_metadata *items; - Handle<Array> metadata; - Handle<Object> status; - Handle<Object> rpc_new; - switch (event->type) { - case GRPC_READ: - return NanEscapeScope(ByteBufferToBuffer(event->data.read)); - case GRPC_WRITE_ACCEPTED: - return NanEscapeScope(NanNew<Number>(event->data.write_accepted)); - case GRPC_FINISH_ACCEPTED: - return NanEscapeScope(NanNew<Number>(event->data.finish_accepted)); - case GRPC_CLIENT_METADATA_READ: - count = event->data.client_metadata_read.count; - items = event->data.client_metadata_read.elements; - return NanEscapeScope(ParseMetadata(items, count)); - case GRPC_FINISHED: - status = NanNew<Object>(); - status->Set(NanNew("code"), NanNew<Number>(event->data.finished.status)); - if (event->data.finished.details != NULL) { - status->Set(NanNew("details"), - String::New(event->data.finished.details)); - } - count = event->data.finished.metadata_count; - items = event->data.finished.metadata_elements; - status->Set(NanNew("metadata"), ParseMetadata(items, count)); - return NanEscapeScope(status); - case GRPC_SERVER_RPC_NEW: - rpc_new = NanNew<Object>(); - if (event->data.server_rpc_new.method == NULL) { - return NanEscapeScope(NanNull()); - } - rpc_new->Set( - NanNew("method"), - NanNew(event->data.server_rpc_new.method)); - rpc_new->Set( - NanNew("host"), - NanNew(event->data.server_rpc_new.host)); - rpc_new->Set(NanNew("absolute_deadline"), - NanNew<Date>(TimespecToMilliseconds( - event->data.server_rpc_new.deadline))); - count = event->data.server_rpc_new.metadata_count; - items = event->data.server_rpc_new.metadata_elements; - metadata = NanNew<Array>(static_cast<int>(count)); - for (unsigned int i = 0; i < count; i++) { - Handle<Object> item_obj = Object::New(); - item_obj->Set(NanNew("key"), - NanNew(items[i].key)); - item_obj->Set( - NanNew("value"), - NanNew(items[i].value, static_cast<int>(items[i].value_length))); - metadata->Set(i, item_obj); - } - rpc_new->Set(NanNew("metadata"), ParseMetadata(items, count)); - return NanEscapeScope(rpc_new); - default: - return NanEscapeScope(NanNull()); - } -} - -Handle<Value> CreateEventObject(grpc_event *event) { - NanEscapableScope(); - if (event == NULL) { - return NanEscapeScope(NanNull()); - } - Handle<Object> event_obj = NanNew<Object>(); - Handle<Value> call; - if (TagHasCall(event->tag)) { - call = TagGetCall(event->tag); - } else { - call = Call::WrapStruct(event->call); - } - event_obj->Set(NanNew<String, const char *>("call"), call); - event_obj->Set(NanNew<String, const char *>("type"), - NanNew<Number>(event->type)); - event_obj->Set(NanNew<String, const char *>("data"), GetEventData(event)); - - return NanEscapeScope(event_obj); -} - -} // namespace node -} // namespace grpc diff --git a/src/node/ext/event.h b/src/node/ext/event.h deleted file mode 100644 index e06d8f0168..0000000000 --- a/src/node/ext/event.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * 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 NET_GRPC_NODE_EVENT_H_ -#define NET_GRPC_NODE_EVENT_H_ - -#include <node.h> -#include "grpc/grpc.h" - -namespace grpc { -namespace node { - -v8::Handle<v8::Value> CreateEventObject(grpc_event *event); - -} // namespace node -} // namespace grpc - -#endif // NET_GRPC_NODE_EVENT_H_ diff --git a/src/node/ext/node_grpc.cc b/src/node/ext/node_grpc.cc index bc1dfaf899..9b0fe82976 100644 --- a/src/node/ext/node_grpc.cc +++ b/src/node/ext/node_grpc.cc @@ -130,35 +130,34 @@ void InitCallErrorConstants(Handle<Object> exports) { call_error->Set(NanNew("INVALID_FLAGS"), INVALID_FLAGS); } -void InitOpErrorConstants(Handle<Object> exports) { +void InitOpTypeConstants(Handle<Object> exports) { NanScope(); - Handle<Object> op_error = Object::New(); - exports->Set(NanNew("opError"), op_error); - Handle<Value> OK(NanNew<Uint32, uint32_t>(GRPC_OP_OK)); - op_error->Set(NanNew("OK"), OK); - Handle<Value> ERROR(NanNew<Uint32, uint32_t>(GRPC_OP_ERROR)); - op_error->Set(NanNew("ERROR"), ERROR); -} - -void InitCompletionTypeConstants(Handle<Object> exports) { - NanScope(); - Handle<Object> completion_type = Object::New(); - exports->Set(NanNew("completionType"), completion_type); - Handle<Value> QUEUE_SHUTDOWN(NanNew<Uint32, uint32_t>(GRPC_QUEUE_SHUTDOWN)); - completion_type->Set(NanNew("QUEUE_SHUTDOWN"), QUEUE_SHUTDOWN); - Handle<Value> READ(NanNew<Uint32, uint32_t>(GRPC_READ)); - completion_type->Set(NanNew("READ"), READ); - Handle<Value> WRITE_ACCEPTED(NanNew<Uint32, uint32_t>(GRPC_WRITE_ACCEPTED)); - completion_type->Set(NanNew("WRITE_ACCEPTED"), WRITE_ACCEPTED); - Handle<Value> FINISH_ACCEPTED(NanNew<Uint32, uint32_t>(GRPC_FINISH_ACCEPTED)); - completion_type->Set(NanNew("FINISH_ACCEPTED"), FINISH_ACCEPTED); - Handle<Value> CLIENT_METADATA_READ( - NanNew<Uint32, uint32_t>(GRPC_CLIENT_METADATA_READ)); - completion_type->Set(NanNew("CLIENT_METADATA_READ"), CLIENT_METADATA_READ); - Handle<Value> FINISHED(NanNew<Uint32, uint32_t>(GRPC_FINISHED)); - completion_type->Set(NanNew("FINISHED"), FINISHED); - Handle<Value> SERVER_RPC_NEW(NanNew<Uint32, uint32_t>(GRPC_SERVER_RPC_NEW)); - completion_type->Set(NanNew("SERVER_RPC_NEW"), SERVER_RPC_NEW); + Handle<Object> op_type = Object::New(); + exports->Set(NanNew("opType"), op_type); + Handle<Value> SEND_INITIAL_METADATA( + NanNew<Uint32, uint32_t>(GRPC_OP_SEND_INITIAL_METADATA)); + op_type->Set(NanNew("SEND_INITIAL_METADATA"), SEND_INITIAL_METADATA); + Handle<Value> SEND_MESSAGE( + NanNew<Uint32, uint32_t>(GRPC_OP_SEND_MESSAGE)); + op_type->Set(NanNew("SEND_MESSAGE"), SEND_MESSAGE); + Handle<Value> SEND_CLOSE_FROM_CLIENT( + NanNew<Uint32, uint32_t>(GRPC_OP_SEND_CLOSE_FROM_CLIENT)); + op_type->Set(NanNew("SEND_CLOSE_FROM_CLIENT"), SEND_CLOSE_FROM_CLIENT); + Handle<Value> SEND_STATUS_FROM_SERVER( + NanNew<Uint32, uint32_t>(GRPC_OP_SEND_STATUS_FROM_SERVER)); + op_type->Set(NanNew("SEND_STATUS_FROM_SERVER"), SEND_STATUS_FROM_SERVER); + Handle<Value> RECV_INITIAL_METADATA( + NanNew<Uint32, uint32_t>(GRPC_OP_RECV_INITIAL_METADATA)); + op_type->Set(NanNew("RECV_INITIAL_METADATA"), RECV_INITIAL_METADATA); + Handle<Value> RECV_MESSAGE( + NanNew<Uint32, uint32_t>(GRPC_OP_RECV_MESSAGE)); + op_type->Set(NanNew("RECV_MESSAGE"), RECV_MESSAGE); + Handle<Value> RECV_STATUS_ON_CLIENT( + NanNew<Uint32, uint32_t>(GRPC_OP_RECV_STATUS_ON_CLIENT)); + op_type->Set(NanNew("RECV_STATUS_ON_CLIENT"), RECV_STATUS_ON_CLIENT); + Handle<Value> RECV_CLOSE_ON_SERVER( + NanNew<Uint32, uint32_t>(GRPC_OP_RECV_CLOSE_ON_SERVER)); + op_type->Set(NanNew("RECV_CLOSE_ON_SERVER"), RECV_CLOSE_ON_SERVER); } void init(Handle<Object> exports) { @@ -166,8 +165,7 @@ void init(Handle<Object> exports) { grpc_init(); InitStatusConstants(exports); InitCallErrorConstants(exports); - InitOpErrorConstants(exports); - InitCompletionTypeConstants(exports); + InitOpTypeConstants(exports); grpc::node::Call::Init(exports); grpc::node::Channel::Init(exports); diff --git a/src/node/ext/server.cc b/src/node/ext/server.cc index 6b8ccef9b1..ee3e1087ce 100644 --- a/src/node/ext/server.cc +++ b/src/node/ext/server.cc @@ -31,6 +31,8 @@ * */ +#include <memory> + #include "server.h" #include <node.h> @@ -41,17 +43,20 @@ #include <vector> #include "grpc/grpc.h" #include "grpc/grpc_security.h" +#include "grpc/support/log.h" #include "call.h" #include "completion_queue_async_worker.h" -#include "tag.h" #include "server_credentials.h" +#include "timeval.h" namespace grpc { namespace node { +using std::unique_ptr; using v8::Arguments; using v8::Array; using v8::Boolean; +using v8::Date; using v8::Exception; using v8::Function; using v8::FunctionTemplate; @@ -67,6 +72,49 @@ using v8::Value; Persistent<Function> Server::constructor; Persistent<FunctionTemplate> Server::fun_tpl; +class NewCallOp : public Op { + public: + NewCallOp() { + call = NULL; + grpc_call_details_init(&details); + grpc_metadata_array_init(&request_metadata); + } + + ~NewCallOp() { + grpc_call_details_destroy(&details); + grpc_metadata_array_destroy(&request_metadata); + } + + Handle<Value> GetNodeValue() const { + NanEscapableScope(); + if (call == NULL) { + return NanEscapeScope(NanNull()); + } + Handle<Object> obj = NanNew<Object>(); + obj->Set(NanNew("call"), Call::WrapStruct(call)); + obj->Set(NanNew("method"), NanNew(details.method)); + obj->Set(NanNew("host"), NanNew(details.host)); + obj->Set(NanNew("deadline"), + NanNew<Date>(TimespecToMilliseconds(details.deadline))); + obj->Set(NanNew("metadata"), ParseMetadata(&request_metadata)); + return NanEscapeScope(obj); + } + + bool ParseOp(Handle<Value> value, grpc_op *out, + shared_ptr<Resources> resources) { + return true; + } + + grpc_call *call; + grpc_call_details details; + grpc_metadata_array request_metadata; + + protected: + std::string GetTypeString() const { + return "new call"; + } +}; + Server::Server(grpc_server *server) : wrapped_server(server) {} Server::~Server() { grpc_server_destroy(wrapped_server); } @@ -175,13 +223,18 @@ NAN_METHOD(Server::RequestCall) { return NanThrowTypeError("requestCall can only be called on a Server"); } Server *server = ObjectWrap::Unwrap<Server>(args.This()); - grpc_call_error error = grpc_server_request_call_old( - server->wrapped_server, CreateTag(args[0], NanNull())); - if (error == GRPC_CALL_OK) { - CompletionQueueAsyncWorker::Next(); - } else { + NewCallOp *op = new NewCallOp(); + unique_ptr<OpVec> ops(new OpVec()); + ops->push_back(unique_ptr<Op>(op)); + grpc_call_error error = grpc_server_request_call( + server->wrapped_server, &op->call, &op->details, &op->request_metadata, + CompletionQueueAsyncWorker::GetQueue(), + new struct tag(new NanCallback(args[0].As<Function>()), ops.release(), + shared_ptr<Resources>(nullptr))); + if (error != GRPC_CALL_OK) { return NanThrowError("requestCall failed", error); } + CompletionQueueAsyncWorker::Next(); NanReturnUndefined(); } diff --git a/src/node/ext/server_credentials.cc b/src/node/ext/server_credentials.cc index 393f3a6305..3add43c48c 100644 --- a/src/node/ext/server_credentials.cc +++ b/src/node/ext/server_credentials.cc @@ -63,7 +63,6 @@ ServerCredentials::ServerCredentials(grpc_server_credentials *credentials) : wrapped_credentials(credentials) {} ServerCredentials::~ServerCredentials() { - gpr_log(GPR_DEBUG, "Destroying server credentials object"); grpc_server_credentials_release(wrapped_credentials); } diff --git a/src/node/ext/tag.cc b/src/node/ext/tag.cc deleted file mode 100644 index dc8e523e12..0000000000 --- a/src/node/ext/tag.cc +++ /dev/null @@ -1,101 +0,0 @@ -/* - * - * 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 <stdlib.h> -#include <node.h> -#include <nan.h> -#include "tag.h" - -namespace grpc { -namespace node { - -using v8::Handle; -using v8::HandleScope; -using v8::Persistent; -using v8::Value; - -struct tag { - tag(Persistent<Value> *tag, Persistent<Value> *call) - : persist_tag(tag), persist_call(call) {} - - ~tag() { - persist_tag->Dispose(); - if (persist_call != NULL) { - persist_call->Dispose(); - } - } - Persistent<Value> *persist_tag; - Persistent<Value> *persist_call; -}; - -void *CreateTag(Handle<Value> tag, Handle<Value> call) { - NanScope(); - Persistent<Value> *persist_tag = new Persistent<Value>(); - NanAssignPersistent(*persist_tag, tag); - Persistent<Value> *persist_call; - if (call->IsNull() || call->IsUndefined()) { - persist_call = NULL; - } else { - persist_call = new Persistent<Value>(); - NanAssignPersistent(*persist_call, call); - } - struct tag *tag_struct = new struct tag(persist_tag, persist_call); - return reinterpret_cast<void *>(tag_struct); -} - -Handle<Value> GetTagHandle(void *tag) { - NanEscapableScope(); - struct tag *tag_struct = reinterpret_cast<struct tag *>(tag); - Handle<Value> tag_value = NanNew<Value>(*tag_struct->persist_tag); - return NanEscapeScope(tag_value); -} - -bool TagHasCall(void *tag) { - struct tag *tag_struct = reinterpret_cast<struct tag *>(tag); - return tag_struct->persist_call != NULL; -} - -Handle<Value> TagGetCall(void *tag) { - NanEscapableScope(); - struct tag *tag_struct = reinterpret_cast<struct tag *>(tag); - if (tag_struct->persist_call == NULL) { - return NanEscapeScope(NanNull()); - } - Handle<Value> call_value = NanNew<Value>(*tag_struct->persist_call); - return NanEscapeScope(call_value); -} - -void DestroyTag(void *tag) { delete reinterpret_cast<struct tag *>(tag); } - -} // namespace node -} // namespace grpc diff --git a/src/node/ext/tag.h b/src/node/ext/tag.h deleted file mode 100644 index bdb09252d9..0000000000 --- a/src/node/ext/tag.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * - * 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 NET_GRPC_NODE_TAG_H_ -#define NET_GRPC_NODE_TAG_H_ - -#include <node.h> - -namespace grpc { -namespace node { - -/* Create a void* tag that can be passed to various grpc_call functions from - a javascript value and the javascript wrapper for the call. The call can be - null. */ -void *CreateTag(v8::Handle<v8::Value> tag, v8::Handle<v8::Value> call); -/* Return the javascript value stored in the tag */ -v8::Handle<v8::Value> GetTagHandle(void *tag); -/* Returns true if the call was set (non-null) when the tag was created */ -bool TagHasCall(void *tag); -/* Returns the javascript wrapper for the call associated with this tag */ -v8::Handle<v8::Value> TagGetCall(void *call); -/* Destroy the tag and all resources it is holding. It is illegal to call any - of these other functions on a tag after it has been destroyed. */ -void DestroyTag(void *tag); - -} // namespace node -} // namespace grpc - -#endif // NET_GRPC_NODE_TAG_H_ diff --git a/src/node/index.js b/src/node/index.js index 0627e7f557..baef4d03c6 100644 --- a/src/node/index.js +++ b/src/node/index.js @@ -35,9 +35,9 @@ var _ = require('underscore'); var ProtoBuf = require('protobufjs'); -var surface_client = require('./src/surface_client.js'); +var client = require('./src/client.js'); -var surface_server = require('./src/surface_server.js'); +var server = require('./src/server.js'); var grpc = require('bindings')('grpc'); @@ -54,7 +54,7 @@ function loadObject(value) { }); return result; } else if (value.className === 'Service') { - return surface_client.makeClientConstructor(value); + return client.makeClientConstructor(value); } else if (value.className === 'Message' || value.className === 'Enum') { return value.build(); } else { @@ -84,9 +84,9 @@ exports.loadObject = loadObject; exports.load = load; /** - * See docs for surface_server.makeServerConstructor + * See docs for server.makeServerConstructor */ -exports.buildServer = surface_server.makeServerConstructor; +exports.buildServer = server.makeServerConstructor; /** * Status name to code number mapping diff --git a/src/node/interop/interop_client.js b/src/node/interop/interop_client.js index ce18f77fe7..8737af6cde 100644 --- a/src/node/interop/interop_client.js +++ b/src/node/interop/interop_client.js @@ -145,8 +145,8 @@ function serverStreaming(client, done) { resp_index += 1; }); call.on('status', function(status) { - assert.strictEqual(resp_index, 4); assert.strictEqual(status.code, grpc.status.OK); + assert.strictEqual(resp_index, 4); if (done) { done(); } diff --git a/src/node/interop/test.proto b/src/node/interop/test.proto index 8380ebb31d..996f11aa6d 100644 --- a/src/node/interop/test.proto +++ b/src/node/interop/test.proto @@ -14,7 +14,7 @@ service TestService { rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); // One request followed by one response. - // The server returns the client payload as-is. + // TODO(Issue 527): Describe required server behavior. rpc UnaryCall(SimpleRequest) returns (SimpleResponse); // One request followed by a sequence of responses (streamed download). diff --git a/src/node/package.json b/src/node/package.json index 028dc20555..8f81014c1e 100644 --- a/src/node/package.json +++ b/src/node/package.json @@ -1,6 +1,6 @@ { "name": "grpc", - "version": "0.1.0", + "version": "0.2.0", "description": "gRPC Library for Node", "scripts": { "test": "./node_modules/mocha/bin/mocha" diff --git a/src/node/src/client.js b/src/node/src/client.js index 3a1c9eef84..81fa65eb26 100644 --- a/src/node/src/client.js +++ b/src/node/src/client.js @@ -1,6 +1,6 @@ /* * - * Copyright 2014, Google Inc. + * Copyright 2015, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,185 +31,452 @@ * */ +var _ = require('underscore'); + +var capitalize = require('underscore.string/capitalize'); +var decapitalize = require('underscore.string/decapitalize'); + var grpc = require('bindings')('grpc.node'); -var common = require('./common'); +var common = require('./common.js'); + +var EventEmitter = require('events').EventEmitter; -var Duplex = require('stream').Duplex; +var stream = require('stream'); + +var Readable = stream.Readable; +var Writable = stream.Writable; +var Duplex = stream.Duplex; var util = require('util'); -util.inherits(GrpcClientStream, Duplex); +util.inherits(ClientWritableStream, Writable); /** - * Class for representing a gRPC client side stream as a Node stream. Extends - * from stream.Duplex. + * A stream that the client can write to. Used for calls that are streaming from + * the client side. * @constructor - * @param {grpc.Call} call Call object to proxy - * @param {function(*):Buffer=} serialize Serialization function for requests - * @param {function(Buffer):*=} deserialize Deserialization function for - * responses + * @param {grpc.Call} call The call object to send data with + * @param {function(*):Buffer=} serialize Serialization function for writes. */ -function GrpcClientStream(call, serialize, deserialize) { - Duplex.call(this, {objectMode: true}); - if (!serialize) { - serialize = function(value) { - return value; - }; - } - if (!deserialize) { - deserialize = function(value) { - return value; - }; - } - var self = this; - var finished = false; - // Indicates that a read is currently pending - var reading = false; - // Indicates that a write is currently pending - var writing = false; - this._call = call; +function ClientWritableStream(call, serialize) { + Writable.call(this, {objectMode: true}); + this.call = call; + this.serialize = common.wrapIgnoreNull(serialize); + this.on('finish', function() { + var batch = {}; + batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + call.startBatch(batch, function() {}); + }); +} - /** - * Serialize a request value to a buffer. Always maps null to null. Otherwise - * uses the provided serialize function - * @param {*} value The value to serialize - * @return {Buffer} The serialized value - */ - this.serialize = function(value) { - if (value === null || value === undefined) { - return null; +/** + * Attempt to write the given chunk. Calls the callback when done. This is an + * implementation of a method needed for implementing stream.Writable. + * @param {Buffer} chunk The chunk to write + * @param {string} encoding Ignored + * @param {function(Error=)} callback Called when the write is complete + */ +function _write(chunk, encoding, callback) { + var batch = {}; + batch[grpc.opType.SEND_MESSAGE] = this.serialize(chunk); + this.call.startBatch(batch, function(err, event) { + if (err) { + throw err; } - return serialize(value); - }; + callback(); + }); +}; - /** - * Deserialize a response buffer to a value. Always maps null to null. - * Otherwise uses the provided deserialize function. - * @param {Buffer} buffer The buffer to deserialize - * @return {*} The deserialized value - */ - this.deserialize = function(buffer) { - if (buffer === null) { - return null; - } - return deserialize(buffer); - }; +ClientWritableStream.prototype._write = _write; + +util.inherits(ClientReadableStream, Readable); + +/** + * A stream that the client can read from. Used for calls that are streaming + * from the server side. + * @constructor + * @param {grpc.Call} call The call object to read data with + * @param {function(Buffer):*=} deserialize Deserialization function for reads + */ +function ClientReadableStream(call, deserialize) { + Readable.call(this, {objectMode: true}); + this.call = call; + this.finished = false; + this.reading = false; + this.deserialize = common.wrapIgnoreNull(deserialize); +} + +/** + * Read the next object from the stream. + * @param {*} size Ignored because we use objectMode=true + */ +function _read(size) { + var self = this; /** * Callback to be called when a READ event is received. Pushes the data onto * the read queue and starts reading again if applicable * @param {grpc.Event} event READ event object */ - function readCallback(event) { - if (finished) { + function readCallback(err, event) { + if (err) { + throw err; + } + if (self.finished) { self.push(null); return; } - var data = event.data; + var data = event.read; if (self.push(self.deserialize(data)) && data != null) { - self._call.startRead(readCallback); + var read_batch = {}; + read_batch[grpc.opType.RECV_MESSAGE] = true; + self.call.startBatch(read_batch, readCallback); } else { - reading = false; + self.reading = false; + } + } + if (self.finished) { + self.push(null); + } else { + if (!self.reading) { + self.reading = true; + var read_batch = {}; + read_batch[grpc.opType.RECV_MESSAGE] = true; + self.call.startBatch(read_batch, readCallback); } } - call.invoke(function(event) { - self.emit('metadata', event.data); - }, function(event) { - finished = true; - self.emit('status', event.data); - }, 0); +}; + +ClientReadableStream.prototype._read = _read; + +util.inherits(ClientDuplexStream, Duplex); + +/** + * A stream that the client can read from or write to. Used for calls with + * duplex streaming. + * @constructor + * @param {grpc.Call} call Call object to proxy + * @param {function(*):Buffer=} serialize Serialization function for requests + * @param {function(Buffer):*=} deserialize Deserialization function for + * responses + */ +function ClientDuplexStream(call, serialize, deserialize) { + Duplex.call(this, {objectMode: true}); + this.serialize = common.wrapIgnoreNull(serialize); + this.deserialize = common.wrapIgnoreNull(deserialize); + var self = this; + var finished = false; + // Indicates that a read is currently pending + var reading = false; + this.call = call; this.on('finish', function() { - call.writesDone(function() {}); + var batch = {}; + batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + call.startBatch(batch, function() {}); }); - /** - * Start reading if there is not already a pending read. Reading will - * continue until self.push returns false (indicating reads should slow - * down) or the read data is null (indicating that there is no more data). - */ - this.startReading = function() { - if (finished) { - self.push(null); - } else { - if (!reading) { - reading = true; - self._call.startRead(readCallback); - } - } - }; } +ClientDuplexStream.prototype._read = _read; +ClientDuplexStream.prototype._write = _write; + /** - * Start reading. This is an implementation of a method needed for implementing - * stream.Readable. - * @param {number} size Ignored + * Cancel the ongoing call */ -GrpcClientStream.prototype._read = function(size) { - this.startReading(); -}; +function cancel() { + this.call.cancel(); +} + +ClientReadableStream.prototype.cancel = cancel; +ClientWritableStream.prototype.cancel = cancel; +ClientDuplexStream.prototype.cancel = cancel; /** - * Attempt to write the given chunk. Calls the callback when done. This is an - * implementation of a method needed for implementing stream.Writable. - * @param {Buffer} chunk The chunk to write - * @param {string} encoding Ignored - * @param {function(Error=)} callback Ignored + * Get a function that can make unary requests to the specified method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeUnaryRequest */ -GrpcClientStream.prototype._write = function(chunk, encoding, callback) { - var self = this; - self._call.startWrite(self.serialize(chunk), function(event) { - callback(); - }, 0); -}; +function makeUnaryRequestFunction(method, serialize, deserialize) { + /** + * Make a unary request with this method on the given channel with the given + * argument, callback, etc. + * @this {Client} Client object. Must have a channel member. + * @param {*} argument The argument to the call. Should be serializable with + * serialize + * @param {function(?Error, value=)} callback The callback to for when the + * response is received + * @param {array=} metadata Array of metadata key/value pairs to add to the + * call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ + function makeUnaryRequest(argument, callback, metadata, deadline) { + if (deadline === undefined) { + deadline = Infinity; + } + var emitter = new EventEmitter(); + var call = new grpc.Call(this.channel, method, deadline); + if (metadata === null || metadata === undefined) { + metadata = {}; + } + emitter.cancel = function cancel() { + call.cancel(); + }; + var client_batch = {}; + client_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + client_batch[grpc.opType.SEND_MESSAGE] = serialize(argument); + client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + client_batch[grpc.opType.RECV_MESSAGE] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + if (err) { + callback(err); + return; + } + if (response.status.code != grpc.status.OK) { + callback(response.status); + return; + } + emitter.emit('status', response.status); + emitter.emit('metadata', response.metadata); + callback(null, deserialize(response.read)); + }); + return emitter; + } + return makeUnaryRequest; +} /** - * Cancel the ongoing call. If the call has not already finished, it will finish - * with status CANCELLED. + * Get a function that can make client stream requests to the specified method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeClientStreamRequest */ -GrpcClientStream.prototype.cancel = function() { - this._call.cancel(); -}; +function makeClientStreamRequestFunction(method, serialize, deserialize) { + /** + * Make a client stream request with this method on the given channel with the + * given callback, etc. + * @this {Client} Client object. Must have a channel member. + * @param {function(?Error, value=)} callback The callback to for when the + * response is received + * @param {array=} metadata Array of metadata key/value pairs to add to the + * call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ + function makeClientStreamRequest(callback, metadata, deadline) { + if (deadline === undefined) { + deadline = Infinity; + } + var call = new grpc.Call(this.channel, method, deadline); + if (metadata === null || metadata === undefined) { + metadata = {}; + } + var stream = new ClientWritableStream(call, serialize); + var metadata_batch = {}; + metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + metadata_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + call.startBatch(metadata_batch, function(err, response) { + if (err) { + callback(err); + return; + } + stream.emit('metadata', response.metadata); + }); + var client_batch = {}; + client_batch[grpc.opType.RECV_MESSAGE] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + if (err) { + callback(err); + return; + } + if (response.status.code != grpc.status.OK) { + callback(response.status); + return; + } + stream.emit('status', response.status); + callback(null, deserialize(response.read)); + }); + return stream; + } + return makeClientStreamRequest; +} /** - * Make a request on the channel to the given method with the given arguments - * @param {grpc.Channel} channel The channel on which to make the request - * @param {string} method The method to request - * @param {function(*):Buffer} serialize Serialization function for requests - * @param {function(Buffer):*} deserialize Deserialization function for - * responses - * @param {array=} metadata Array of metadata key/value pairs to add to the call - * @param {(number|Date)=} deadline The deadline for processing this request. - * Defaults to infinite future. - * @return {stream=} The stream of responses + * Get a function that can make server stream requests to the specified method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeServerStreamRequest */ -function makeRequest(channel, - method, - serialize, - deserialize, - metadata, - deadline) { - if (deadline === undefined) { - deadline = Infinity; +function makeServerStreamRequestFunction(method, serialize, deserialize) { + /** + * Make a server stream request with this method on the given channel with the + * given argument, etc. + * @this {SurfaceClient} Client object. Must have a channel member. + * @param {*} argument The argument to the call. Should be serializable with + * serialize + * @param {array=} metadata Array of metadata key/value pairs to add to the + * call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ + function makeServerStreamRequest(argument, metadata, deadline) { + if (deadline === undefined) { + deadline = Infinity; + } + var call = new grpc.Call(this.channel, method, deadline); + if (metadata === null || metadata === undefined) { + metadata = {}; + } + var stream = new ClientReadableStream(call, deserialize); + var start_batch = {}; + start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + start_batch[grpc.opType.SEND_MESSAGE] = serialize(argument); + start_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + call.startBatch(start_batch, function(err, response) { + if (err) { + throw err; + } + stream.emit('metadata', response.metadata); + }); + var status_batch = {}; + status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(status_batch, function(err, response) { + if (err) { + throw err; + } + stream.emit('status', response.status); + }); + return stream; } - var call = new grpc.Call(channel, method, deadline); - if (metadata) { - call.addMetadata(metadata); + return makeServerStreamRequest; +} + +/** + * Get a function that can make bidirectional stream requests to the specified + * method. + * @param {string} method The name of the method to request + * @param {function(*):Buffer} serialize The serialization function for inputs + * @param {function(Buffer)} deserialize The deserialization function for + * outputs + * @return {Function} makeBidiStreamRequest + */ +function makeBidiStreamRequestFunction(method, serialize, deserialize) { + /** + * Make a bidirectional stream request with this method on the given channel. + * @this {SurfaceClient} Client object. Must have a channel member. + * @param {array=} metadata Array of metadata key/value pairs to add to the + * call + * @param {(number|Date)=} deadline The deadline for processing this request. + * Defaults to infinite future + * @return {EventEmitter} An event emitter for stream related events + */ + function makeBidiStreamRequest(metadata, deadline) { + if (deadline === undefined) { + deadline = Infinity; + } + var call = new grpc.Call(this.channel, method, deadline); + if (metadata === null || metadata === undefined) { + metadata = {}; + } + var stream = new ClientDuplexStream(call, serialize, deserialize); + var start_batch = {}; + start_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + start_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + call.startBatch(start_batch, function(err, response) { + if (err) { + throw err; + } + stream.emit('metadata', response.metadata); + }); + var status_batch = {}; + status_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(status_batch, function(err, response) { + if (err) { + throw err; + } + stream.emit('status', response.status); + }); + return stream; } - return new GrpcClientStream(call, serialize, deserialize); + return makeBidiStreamRequest; } + /** - * See documentation for makeRequest above + * Map with short names for each of the requester maker functions. Used in + * makeClientConstructor */ -exports.makeRequest = makeRequest; +var requester_makers = { + unary: makeUnaryRequestFunction, + server_stream: makeServerStreamRequestFunction, + client_stream: makeClientStreamRequestFunction, + bidi: makeBidiStreamRequestFunction +}; /** - * Represents a client side gRPC channel associated with a single host. + * Creates a constructor for clients for the given service + * @param {ProtoBuf.Reflect.Service} service The service to generate a client + * for + * @return {function(string, Object)} New client constructor */ -exports.Channel = grpc.Channel; +function makeClientConstructor(service) { + var prefix = '/' + common.fullyQualifiedName(service) + '/'; + /** + * Create a client with the given methods + * @constructor + * @param {string} address The address of the server to connect to + * @param {Object} options Options to pass to the underlying channel + */ + function Client(address, options) { + this.channel = new grpc.Channel(address, options); + } + + _.each(service.children, function(method) { + var method_type; + if (method.requestStream) { + if (method.responseStream) { + method_type = 'bidi'; + } else { + method_type = 'client_stream'; + } + } else { + if (method.responseStream) { + method_type = 'server_stream'; + } else { + method_type = 'unary'; + } + } + Client.prototype[decapitalize(method.name)] = + requester_makers[method_type]( + prefix + capitalize(method.name), + common.serializeCls(method.resolvedRequestType.build()), + common.deserializeCls(method.resolvedResponseType.build())); + }); + + Client.service = service; + + return Client; +} + +exports.makeClientConstructor = makeClientConstructor; + /** - * Status name to code number mapping + * See docs for client.status */ exports.status = grpc.status; /** - * Call error name to code number mapping + * See docs for client.callError */ exports.callError = grpc.callError; diff --git a/src/node/src/common.js b/src/node/src/common.js index 54247e3fa1..7560cf1bdd 100644 --- a/src/node/src/common.js +++ b/src/node/src/common.js @@ -31,6 +31,8 @@ * */ +var _ = require('underscore'); + var capitalize = require('underscore.string/capitalize'); /** @@ -88,6 +90,24 @@ function fullyQualifiedName(value) { } /** + * Wrap a function to pass null-like values through without calling it. If no + * function is given, just uses the identity; + * @param {?function} func The function to wrap + * @return {function} The wrapped function + */ +function wrapIgnoreNull(func) { + if (!func) { + return _.identity; + } + return function(arg) { + if (arg === null || arg === undefined) { + return null; + } + return func(arg); + }; +} + +/** * See docs for deserializeCls */ exports.deserializeCls = deserializeCls; @@ -101,3 +121,8 @@ exports.serializeCls = serializeCls; * See docs for fullyQualifiedName */ exports.fullyQualifiedName = fullyQualifiedName; + +/** + * See docs for wrapIgnoreNull + */ +exports.wrapIgnoreNull = wrapIgnoreNull; diff --git a/src/node/src/server.js b/src/node/src/server.js index e4f71ff05f..48c349ef99 100644 --- a/src/node/src/server.js +++ b/src/node/src/server.js @@ -1,6 +1,6 @@ /* * - * Copyright 2014, Google Inc. + * Copyright 2015, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,80 +33,108 @@ var _ = require('underscore'); +var capitalize = require('underscore.string/capitalize'); +var decapitalize = require('underscore.string/decapitalize'); + var grpc = require('bindings')('grpc.node'); var common = require('./common'); -var Duplex = require('stream').Duplex; +var stream = require('stream'); + +var Readable = stream.Readable; +var Writable = stream.Writable; +var Duplex = stream.Duplex; var util = require('util'); -util.inherits(GrpcServerStream, Duplex); +var EventEmitter = require('events').EventEmitter; + +var common = require('./common.js'); /** - * Class for representing a gRPC server side stream as a Node stream. Extends - * from stream.Duplex. - * @constructor - * @param {grpc.Call} call Call object to proxy - * @param {function(*):Buffer=} serialize Serialization function for responses - * @param {function(Buffer):*=} deserialize Deserialization function for - * requests + * Handle an error on a call by sending it as a status + * @param {grpc.Call} call The call to send the error on + * @param {Object} error The error object */ -function GrpcServerStream(call, serialize, deserialize) { - Duplex.call(this, {objectMode: true}); - if (!serialize) { - serialize = function(value) { - return value; - }; - } - if (!deserialize) { - deserialize = function(value) { - return value; - }; - } - this._call = call; - // Indicate that a status has been sent - var finished = false; - var self = this; +function handleError(call, error) { var status = { - 'code' : grpc.status.OK, - 'details' : 'OK' + code: grpc.status.INTERNAL, + details: 'Unknown Error', + metadata: {} }; - - /** - * Serialize a response value to a buffer. Always maps null to null. Otherwise - * uses the provided serialize function - * @param {*} value The value to serialize - * @return {Buffer} The serialized value - */ - this.serialize = function(value) { - if (value === null || value === undefined) { - return null; + if (error.hasOwnProperty('message')) { + status.details = error.message; + } + if (error.hasOwnProperty('code')) { + status.code = error.code; + if (error.hasOwnProperty('details')) { + status.details = error.details; } - return serialize(value); - }; + } + var error_batch = {}; + error_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = status; + call.startBatch(error_batch, function(){}); +} - /** - * Deserialize a request buffer to a value. Always maps null to null. - * Otherwise uses the provided deserialize function. - * @param {Buffer} buffer The buffer to deserialize - * @return {*} The deserialized value - */ - this.deserialize = function(buffer) { - if (buffer === null) { - return null; +/** + * Wait for the client to close, then emit a cancelled event if the client + * cancelled. + * @param {grpc.Call} call The call object to wait on + * @param {EventEmitter} emitter The event emitter to emit the cancelled event + * on + */ +function waitForCancel(call, emitter) { + var cancel_batch = {}; + cancel_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + call.startBatch(cancel_batch, function(err, result) { + if (err) { + emitter.emit('error', err); } - return deserialize(buffer); + if (result.cancelled) { + emitter.cancelled = true; + emitter.emit('cancelled'); + } + }); +} + +/** + * Send a response to a unary or client streaming call. + * @param {grpc.Call} call The call to respond on + * @param {*} value The value to respond with + * @param {function(*):Buffer=} serialize Serialization function for the + * response + */ +function sendUnaryResponse(call, value, serialize) { + var end_batch = {}; + end_batch[grpc.opType.SEND_MESSAGE] = serialize(value); + end_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + code: grpc.status.OK, + details: 'OK', + metadata: {} }; + call.startBatch(end_batch, function (){}); +} - /** - * Send the pending status - */ +/** + * Initialize a writable stream. This is used for both the writable and duplex + * stream constructors. + * @param {Writable} stream The stream to set up + * @param {function(*):Buffer=} Serialization function for responses + */ +function setUpWritable(stream, serialize) { + stream.finished = false; + stream.status = { + code : grpc.status.OK, + details : 'OK', + metadata : {} + }; + stream.serialize = common.wrapIgnoreNull(serialize); function sendStatus() { - call.startWriteStatus(status.code, status.details, function() { - }); - finished = true; + var batch = {}; + batch[grpc.opType.SEND_STATUS_FROM_SERVER] = stream.status; + stream.call.startBatch(batch, function(){}); } - this.on('finish', sendStatus); + stream.on('finish', sendStatus); /** * Set the pending status to a given error status. If the error does not have * code or details properties, the code will be set to grpc.status.INTERNAL @@ -116,14 +144,16 @@ function GrpcServerStream(call, serialize, deserialize) { function setStatus(err) { var code = grpc.status.INTERNAL; var details = 'Unknown Error'; - + if (err.hasOwnProperty('message')) { + details = err.message; + } if (err.hasOwnProperty('code')) { code = err.code; if (err.hasOwnProperty('details')) { details = err.details; } } - status = {'code': code, 'details': details}; + stream.status = {code: code, details: details, metadata: {}}; } /** * Terminate the call. This includes indicating that reads are done, draining @@ -133,69 +163,250 @@ function GrpcServerStream(call, serialize, deserialize) { */ function terminateCall(err) { // Drain readable data - this.on('data', function() {}); setStatus(err); - this.end(); + stream.end(); } - this.on('error', terminateCall); - // Indicates that a read is pending - var reading = false; + stream.on('error', terminateCall); +} + +/** + * Initialize a readable stream. This is used for both the readable and duplex + * stream constructors. + * @param {Readable} stream The stream to initialize + * @param {function(Buffer):*=} deserialize Deserialization function for + * incoming data. + */ +function setUpReadable(stream, deserialize) { + stream.deserialize = common.wrapIgnoreNull(deserialize); + stream.finished = false; + stream.reading = false; + + stream.terminate = function() { + stream.finished = true; + stream.on('data', function() {}); + }; + + stream.on('cancelled', function() { + stream.terminate(); + }); +} + +util.inherits(ServerWritableStream, Writable); + +/** + * A stream that the server can write to. Used for calls that are streaming from + * the server side. + * @constructor + * @param {grpc.Call} call The call object to send data with + * @param {function(*):Buffer=} serialize Serialization function for writes + */ +function ServerWritableStream(call, serialize) { + Writable.call(this, {objectMode: true}); + this.call = call; + + this.finished = false; + setUpWritable(this, serialize); +} + +/** + * Start writing a chunk of data. This is an implementation of a method required + * for implementing stream.Writable. + * @param {Buffer} chunk The chunk of data to write + * @param {string} encoding Ignored + * @param {function(Error=)} callback Callback to indicate that the write is + * complete + */ +function _write(chunk, encoding, callback) { + var batch = {}; + batch[grpc.opType.SEND_MESSAGE] = this.serialize(chunk); + this.call.startBatch(batch, function(err, value) { + if (err) { + this.emit('error', err); + return; + } + callback(); + }); +} + +ServerWritableStream.prototype._write = _write; + +util.inherits(ServerReadableStream, Readable); + +/** + * A stream that the server can read from. Used for calls that are streaming + * from the client side. + * @constructor + * @param {grpc.Call} call The call object to read data with + * @param {function(Buffer):*=} deserialize Deserialization function for reads + */ +function ServerReadableStream(call, deserialize) { + Readable.call(this, {objectMode: true}); + this.call = call; + setUpReadable(this, deserialize); +} + +/** + * Start reading from the gRPC data source. This is an implementation of a + * method required for implementing stream.Readable + * @param {number} size Ignored + */ +function _read(size) { + var self = this; /** * Callback to be called when a READ event is received. Pushes the data onto * the read queue and starts reading again if applicable * @param {grpc.Event} event READ event object */ - function readCallback(event) { - if (finished) { + function readCallback(err, event) { + if (err) { + self.terminate(); + return; + } + if (self.finished) { self.push(null); return; } - var data = event.data; + var data = event.read; if (self.push(self.deserialize(data)) && data != null) { - self._call.startRead(readCallback); + var read_batch = {}; + read_batch[grpc.opType.RECV_MESSAGE] = true; + self.call.startBatch(read_batch, readCallback); } else { - reading = false; + self.reading = false; } } - /** - * Start reading if there is not already a pending read. Reading will - * continue until self.push returns false (indicating reads should slow - * down) or the read data is null (indicating that there is no more data). - */ - this.startReading = function() { - if (finished) { - self.push(null); - } else { - if (!reading) { - reading = true; - self._call.startRead(readCallback); + if (self.finished) { + self.push(null); + } else { + if (!self.reading) { + self.reading = true; + var batch = {}; + batch[grpc.opType.RECV_MESSAGE] = true; + self.call.startBatch(batch, readCallback); + } + } +} + +ServerReadableStream.prototype._read = _read; + +util.inherits(ServerDuplexStream, Duplex); + +/** + * A stream that the server can read from or write to. Used for calls with + * duplex streaming. + * @constructor + * @param {grpc.Call} call Call object to proxy + * @param {function(*):Buffer=} serialize Serialization function for requests + * @param {function(Buffer):*=} deserialize Deserialization function for + * responses + */ +function ServerDuplexStream(call, serialize, deserialize) { + Duplex.call(this, {objectMode: true}); + this.call = call; + setUpWritable(this, serialize); + setUpReadable(this, deserialize); +} + +ServerDuplexStream.prototype._read = _read; +ServerDuplexStream.prototype._write = _write; + +/** + * Fully handle a unary call + * @param {grpc.Call} call The call to handle + * @param {Object} handler Request handler object for the method that was called + * @param {Object} metadata Metadata from the client + */ +function handleUnary(call, handler, metadata) { + var emitter = new EventEmitter(); + emitter.on('error', function(error) { + handleError(call, error); + }); + waitForCancel(call, emitter); + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + batch[grpc.opType.RECV_MESSAGE] = true; + call.startBatch(batch, function(err, result) { + if (err) { + handleError(call, err); + return; + } + emitter.request = handler.deserialize(result.read); + if (emitter.cancelled) { + return; + } + handler.func(emitter, function sendUnaryData(err, value) { + if (err) { + handleError(call, err); } + sendUnaryResponse(call, value, handler.serialize); + }); + }); +} + +/** + * Fully handle a server streaming call + * @param {grpc.Call} call The call to handle + * @param {Object} handler Request handler object for the method that was called + * @param {Object} metadata Metadata from the client + */ +function handleServerStreaming(call, handler, metadata) { + var stream = new ServerWritableStream(call, handler.serialize); + waitForCancel(call, stream); + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + batch[grpc.opType.RECV_MESSAGE] = true; + call.startBatch(batch, function(err, result) { + if (err) { + stream.emit('error', err); + return; } - }; + stream.request = handler.deserialize(result.read); + handler.func(stream); + }); } /** - * Start reading from the gRPC data source. This is an implementation of a - * method required for implementing stream.Readable - * @param {number} size Ignored + * Fully handle a client streaming call + * @param {grpc.Call} call The call to handle + * @param {Object} handler Request handler object for the method that was called + * @param {Object} metadata Metadata from the client */ -GrpcServerStream.prototype._read = function(size) { - this.startReading(); -}; +function handleClientStreaming(call, handler, metadata) { + var stream = new ServerReadableStream(call, handler.deserialize); + waitForCancel(call, stream); + var metadata_batch = {}; + metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + call.startBatch(metadata_batch, function() {}); + handler.func(stream, function(err, value) { + stream.terminate(); + if (err) { + handleError(call, err); + } + sendUnaryResponse(call, value, handler.serialize); + }); +} /** - * Start writing a chunk of data. This is an implementation of a method required - * for implementing stream.Writable. - * @param {Buffer} chunk The chunk of data to write - * @param {string} encoding Ignored - * @param {function(Error=)} callback Callback to indicate that the write is - * complete + * Fully handle a bidirectional streaming call + * @param {grpc.Call} call The call to handle + * @param {Object} handler Request handler object for the method that was called + * @param {Object} metadata Metadata from the client */ -GrpcServerStream.prototype._write = function(chunk, encoding, callback) { - var self = this; - self._call.startWrite(self.serialize(chunk), function(event) { - callback(); - }, 0); +function handleBidiStreaming(call, handler, metadata) { + var stream = new ServerDuplexStream(call, handler.serialize, + handler.deserialize); + waitForCancel(call, stream); + var metadata_batch = {}; + metadata_batch[grpc.opType.SEND_INITIAL_METADATA] = metadata; + call.startBatch(metadata_batch, function() {}); + handler.func(stream); +} + +var streamHandlers = { + unary: handleUnary, + server_stream: handleServerStreaming, + client_stream: handleClientStreaming, + bidi: handleBidiStreaming }; /** @@ -218,7 +429,7 @@ function Server(getMetadata, options) { * Start the server and begin handling requests * @this Server */ - this.start = function() { + this.listen = function() { console.log('Server starting'); _.each(handlers, function(handler, handler_name) { console.log('Serving', handler_name); @@ -233,48 +444,39 @@ function Server(getMetadata, options) { * wait for the next request * @param {grpc.Event} event The event to handle with tag SERVER_RPC_NEW */ - function handleNewCall(event) { - var call = event.call; - var data = event.data; - if (data === null) { + function handleNewCall(err, event) { + if (err) { + return; + } + var details = event['new call']; + var call = details.call; + var method = details.method; + var metadata = details.metadata; + if (method === null) { return; } server.requestCall(handleNewCall); var handler = undefined; - var deadline = data.absolute_deadline; - var cancelled = false; - call.serverAccept(function(event) { - if (event.data.code === grpc.status.CANCELLED) { - cancelled = true; - if (stream) { - stream.emit('cancelled'); - } - } - }, 0); - if (handlers.hasOwnProperty(data.method)) { - handler = handlers[data.method]; + var deadline = details.deadline; + if (handlers.hasOwnProperty(method)) { + handler = handlers[method]; } else { - call.serverEndInitialMetadata(0); - call.startWriteStatus( - grpc.status.UNIMPLEMENTED, - "This method is not available on this server.", - function() {}); + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + code: grpc.status.UNIMPLEMENTED, + details: "This method is not available on this server.", + metadata: {} + }; + batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + call.startBatch(batch, function() {}); return; } + var response_metadata = {}; if (getMetadata) { - call.addMetadata(getMetadata(data.method, data.metadata)); - } - call.serverEndInitialMetadata(0); - var stream = new GrpcServerStream(call, handler.serialize, - handler.deserialize); - Object.defineProperty(stream, 'cancelled', { - get: function() { return cancelled;} - }); - try { - handler.func(stream, data.metadata); - } catch (e) { - stream.emit('error', e); + response_metadata = getMetadata(method, metadata); } + streamHandlers[handler.type](call, handler, response_metadata); } server.requestCall(handleNewCall); }; @@ -294,17 +496,20 @@ function Server(getMetadata, options) { * returns a stream of response values * @param {function(*):Buffer} serialize Serialization function for responses * @param {function(Buffer):*} deserialize Deserialization function for requests + * @param {string} type The streaming type of method that this handles * @return {boolean} True if the handler was set. False if a handler was already * set for that name. */ -Server.prototype.register = function(name, handler, serialize, deserialize) { +Server.prototype.register = function(name, handler, serialize, deserialize, + type) { if (this.handlers.hasOwnProperty(name)) { return false; } this.handlers[name] = { func: handler, serialize: serialize, - deserialize: deserialize + deserialize: deserialize, + type: type }; return true; }; @@ -324,6 +529,110 @@ Server.prototype.bind = function(port, secure) { }; /** - * See documentation for Server + * Creates a constructor for servers with a service defined by the methods + * object. The methods object has string keys and values of this form: + * {serialize: function, deserialize: function, client_stream: bool, + * server_stream: bool} + * @param {Object} methods Method descriptor for each method the server should + * expose + * @param {string} prefix The prefex to prepend to each method name + * @return {function(Object, Object)} New server constructor + */ +function makeServerConstructor(services) { + var qual_names = []; + _.each(services, function(service) { + _.each(service.children, function(method) { + var name = common.fullyQualifiedName(method); + if (_.indexOf(qual_names, name) !== -1) { + throw new Error('Method ' + name + ' exposed by more than one service'); + } + qual_names.push(name); + }); + }); + /** + * Create a server with the given handlers for all of the methods. + * @constructor + * @param {Object} service_handlers Map from service names to map from method + * names to handlers + * @param {function(string, Object<string, Array<Buffer>>): + Object<string, Array<Buffer|string>>=} getMetadata Callback that + * gets metatada for a given method + * @param {Object=} options Options to pass to the underlying server + */ + function SurfaceServer(service_handlers, getMetadata, options) { + var server = new Server(getMetadata, options); + this.inner_server = server; + _.each(services, function(service) { + var service_name = common.fullyQualifiedName(service); + if (service_handlers[service_name] === undefined) { + throw new Error('Handlers for service ' + + service_name + ' not provided.'); + } + var prefix = '/' + common.fullyQualifiedName(service) + '/'; + _.each(service.children, function(method) { + var method_type; + if (method.requestStream) { + if (method.responseStream) { + method_type = 'bidi'; + } else { + method_type = 'client_stream'; + } + } else { + if (method.responseStream) { + method_type = 'server_stream'; + } else { + method_type = 'unary'; + } + } + if (service_handlers[service_name][decapitalize(method.name)] === + undefined) { + throw new Error('Method handler for ' + + common.fullyQualifiedName(method) + ' not provided.'); + } + var serialize = common.serializeCls( + method.resolvedResponseType.build()); + var deserialize = common.deserializeCls( + method.resolvedRequestType.build()); + server.register( + prefix + capitalize(method.name), + service_handlers[service_name][decapitalize(method.name)], + serialize, deserialize, method_type); + }); + }, this); + } + + /** + * Binds the server to the given port, with SSL enabled if secure is specified + * @param {string} port The port that the server should bind on, in the format + * "address:port" + * @param {boolean=} secure Whether the server should open a secure port + * @return {SurfaceServer} this + */ + SurfaceServer.prototype.bind = function(port, secure) { + return this.inner_server.bind(port, secure); + }; + + /** + * Starts the server listening on any bound ports + * @return {SurfaceServer} this + */ + SurfaceServer.prototype.listen = function() { + this.inner_server.listen(); + return this; + }; + + /** + * Shuts the server down; tells it to stop listening for new requests and to + * kill old requests. + */ + SurfaceServer.prototype.shutdown = function() { + this.inner_server.shutdown(); + }; + + return SurfaceServer; +} + +/** + * See documentation for makeServerConstructor */ -module.exports = Server; +exports.makeServerConstructor = makeServerConstructor; diff --git a/src/node/src/surface_client.js b/src/node/src/surface_client.js deleted file mode 100644 index 16c31809f4..0000000000 --- a/src/node/src/surface_client.js +++ /dev/null @@ -1,357 +0,0 @@ -/* - * - * 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. - * - */ - -var _ = require('underscore'); - -var capitalize = require('underscore.string/capitalize'); -var decapitalize = require('underscore.string/decapitalize'); - -var client = require('./client.js'); - -var common = require('./common.js'); - -var EventEmitter = require('events').EventEmitter; - -var stream = require('stream'); - -var Readable = stream.Readable; -var Writable = stream.Writable; -var Duplex = stream.Duplex; -var util = require('util'); - - -function forwardEvent(fromEmitter, toEmitter, event) { - fromEmitter.on(event, function forward() { - _.partial(toEmitter.emit, event).apply(toEmitter, arguments); - }); -} - -util.inherits(ClientReadableObjectStream, Readable); - -/** - * Class for representing a gRPC server streaming call as a Node stream on the - * client side. Extends from stream.Readable. - * @constructor - * @param {stream} stream Underlying binary Duplex stream for the call - */ -function ClientReadableObjectStream(stream) { - var options = {objectMode: true}; - Readable.call(this, options); - this._stream = stream; - var self = this; - forwardEvent(stream, this, 'status'); - forwardEvent(stream, this, 'metadata'); - this._stream.on('data', function forwardData(chunk) { - if (!self.push(chunk)) { - self._stream.pause(); - } - }); - this._stream.pause(); -} - -/** - * _read implementation for both types of streams that allow reading. - * @this {ClientReadableObjectStream} - * @param {number} size Ignored - */ -function _read(size) { - this._stream.resume(); -} - -/** - * See docs for _read - */ -ClientReadableObjectStream.prototype._read = _read; - -util.inherits(ClientWritableObjectStream, Writable); - -/** - * Class for representing a gRPC client streaming call as a Node stream on the - * client side. Extends from stream.Writable. - * @constructor - * @param {stream} stream Underlying binary Duplex stream for the call - */ -function ClientWritableObjectStream(stream) { - var options = {objectMode: true}; - Writable.call(this, options); - this._stream = stream; - forwardEvent(stream, this, 'status'); - forwardEvent(stream, this, 'metadata'); - this.on('finish', function() { - this._stream.end(); - }); -} - -/** - * _write implementation for both types of streams that allow writing - * @this {ClientWritableObjectStream} - * @param {*} chunk The value to write to the stream - * @param {string} encoding Ignored - * @param {function(Error)} callback Callback to call when finished writing - */ -function _write(chunk, encoding, callback) { - this._stream.write(chunk, encoding, callback); -} - -/** - * See docs for _write - */ -ClientWritableObjectStream.prototype._write = _write; - -/** - * Cancel the underlying call - */ -function cancel() { - this._stream.cancel(); -} - -ClientReadableObjectStream.prototype.cancel = cancel; -ClientWritableObjectStream.prototype.cancel = cancel; - -/** - * Get a function that can make unary requests to the specified method. - * @param {string} method The name of the method to request - * @param {function(*):Buffer} serialize The serialization function for inputs - * @param {function(Buffer)} deserialize The deserialization function for - * outputs - * @return {Function} makeUnaryRequest - */ -function makeUnaryRequestFunction(method, serialize, deserialize) { - /** - * Make a unary request with this method on the given channel with the given - * argument, callback, etc. - * @this {SurfaceClient} Client object. Must have a channel member. - * @param {*} argument The argument to the call. Should be serializable with - * serialize - * @param {function(?Error, value=)} callback The callback to for when the - * response is received - * @param {array=} metadata Array of metadata key/value pairs to add to the - * call - * @param {(number|Date)=} deadline The deadline for processing this request. - * Defaults to infinite future - * @return {EventEmitter} An event emitter for stream related events - */ - function makeUnaryRequest(argument, callback, metadata, deadline) { - var stream = client.makeRequest(this.channel, method, serialize, - deserialize, metadata, deadline); - var emitter = new EventEmitter(); - emitter.cancel = function cancel() { - stream.cancel(); - }; - forwardEvent(stream, emitter, 'status'); - forwardEvent(stream, emitter, 'metadata'); - stream.write(argument); - stream.end(); - stream.on('data', function forwardData(chunk) { - try { - callback(null, chunk); - } catch (e) { - callback(e); - } - }); - stream.on('status', function forwardStatus(status) { - if (status.code !== client.status.OK) { - callback(status); - } - }); - return emitter; - } - return makeUnaryRequest; -} - -/** - * Get a function that can make client stream requests to the specified method. - * @param {string} method The name of the method to request - * @param {function(*):Buffer} serialize The serialization function for inputs - * @param {function(Buffer)} deserialize The deserialization function for - * outputs - * @return {Function} makeClientStreamRequest - */ -function makeClientStreamRequestFunction(method, serialize, deserialize) { - /** - * Make a client stream request with this method on the given channel with the - * given callback, etc. - * @this {SurfaceClient} Client object. Must have a channel member. - * @param {function(?Error, value=)} callback The callback to for when the - * response is received - * @param {array=} metadata Array of metadata key/value pairs to add to the - * call - * @param {(number|Date)=} deadline The deadline for processing this request. - * Defaults to infinite future - * @return {EventEmitter} An event emitter for stream related events - */ - function makeClientStreamRequest(callback, metadata, deadline) { - var stream = client.makeRequest(this.channel, method, serialize, - deserialize, metadata, deadline); - var obj_stream = new ClientWritableObjectStream(stream); - stream.on('data', function forwardData(chunk) { - try { - callback(null, chunk); - } catch (e) { - callback(e); - } - }); - stream.on('status', function forwardStatus(status) { - if (status.code !== client.status.OK) { - callback(status); - } - }); - return obj_stream; - } - return makeClientStreamRequest; -} - -/** - * Get a function that can make server stream requests to the specified method. - * @param {string} method The name of the method to request - * @param {function(*):Buffer} serialize The serialization function for inputs - * @param {function(Buffer)} deserialize The deserialization function for - * outputs - * @return {Function} makeServerStreamRequest - */ -function makeServerStreamRequestFunction(method, serialize, deserialize) { - /** - * Make a server stream request with this method on the given channel with the - * given argument, etc. - * @this {SurfaceClient} Client object. Must have a channel member. - * @param {*} argument The argument to the call. Should be serializable with - * serialize - * @param {array=} metadata Array of metadata key/value pairs to add to the - * call - * @param {(number|Date)=} deadline The deadline for processing this request. - * Defaults to infinite future - * @return {EventEmitter} An event emitter for stream related events - */ - function makeServerStreamRequest(argument, metadata, deadline) { - var stream = client.makeRequest(this.channel, method, serialize, - deserialize, metadata, deadline); - var obj_stream = new ClientReadableObjectStream(stream); - stream.write(argument); - stream.end(); - return obj_stream; - } - return makeServerStreamRequest; -} - -/** - * Get a function that can make bidirectional stream requests to the specified - * method. - * @param {string} method The name of the method to request - * @param {function(*):Buffer} serialize The serialization function for inputs - * @param {function(Buffer)} deserialize The deserialization function for - * outputs - * @return {Function} makeBidiStreamRequest - */ -function makeBidiStreamRequestFunction(method, serialize, deserialize) { - /** - * Make a bidirectional stream request with this method on the given channel. - * @this {SurfaceClient} Client object. Must have a channel member. - * @param {array=} metadata Array of metadata key/value pairs to add to the - * call - * @param {(number|Date)=} deadline The deadline for processing this request. - * Defaults to infinite future - * @return {EventEmitter} An event emitter for stream related events - */ - function makeBidiStreamRequest(metadata, deadline) { - return client.makeRequest(this.channel, method, serialize, - deserialize, metadata, deadline); - } - return makeBidiStreamRequest; -} - -/** - * Map with short names for each of the requester maker functions. Used in - * makeClientConstructor - */ -var requester_makers = { - unary: makeUnaryRequestFunction, - server_stream: makeServerStreamRequestFunction, - client_stream: makeClientStreamRequestFunction, - bidi: makeBidiStreamRequestFunction -} - -/** - * Creates a constructor for clients for the given service - * @param {ProtoBuf.Reflect.Service} service The service to generate a client - * for - * @return {function(string, Object)} New client constructor - */ -function makeClientConstructor(service) { - var prefix = '/' + common.fullyQualifiedName(service) + '/'; - /** - * Create a client with the given methods - * @constructor - * @param {string} address The address of the server to connect to - * @param {Object} options Options to pass to the underlying channel - */ - function SurfaceClient(address, options) { - this.channel = new client.Channel(address, options); - } - - _.each(service.children, function(method) { - var method_type; - if (method.requestStream) { - if (method.responseStream) { - method_type = 'bidi'; - } else { - method_type = 'client_stream'; - } - } else { - if (method.responseStream) { - method_type = 'server_stream'; - } else { - method_type = 'unary'; - } - } - SurfaceClient.prototype[decapitalize(method.name)] = - requester_makers[method_type]( - prefix + capitalize(method.name), - common.serializeCls(method.resolvedRequestType.build()), - common.deserializeCls(method.resolvedResponseType.build())); - }); - - SurfaceClient.service = service; - - return SurfaceClient; -} - -exports.makeClientConstructor = makeClientConstructor; - -/** - * See docs for client.status - */ -exports.status = client.status; -/** - * See docs for client.callError - */ -exports.callError = client.callError; diff --git a/src/node/src/surface_server.js b/src/node/src/surface_server.js deleted file mode 100644 index a47d1fa23d..0000000000 --- a/src/node/src/surface_server.js +++ /dev/null @@ -1,340 +0,0 @@ -/* - * - * 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. - * - */ - -var _ = require('underscore'); - -var capitalize = require('underscore.string/capitalize'); -var decapitalize = require('underscore.string/decapitalize'); - -var Server = require('./server.js'); - -var stream = require('stream'); - -var Readable = stream.Readable; -var Writable = stream.Writable; -var Duplex = stream.Duplex; -var util = require('util'); - -var common = require('./common.js'); - -util.inherits(ServerReadableObjectStream, Readable); - -/** - * Class for representing a gRPC client streaming call as a Node stream on the - * server side. Extends from stream.Readable. - * @constructor - * @param {stream} stream Underlying binary Duplex stream for the call - */ -function ServerReadableObjectStream(stream) { - var options = {objectMode: true}; - Readable.call(this, options); - this._stream = stream; - Object.defineProperty(this, 'cancelled', { - get: function() { return stream.cancelled; } - }); - var self = this; - this._stream.on('cancelled', function() { - self.emit('cancelled'); - }); - this._stream.on('data', function forwardData(chunk) { - if (!self.push(chunk)) { - self._stream.pause(); - } - }); - this._stream.on('end', function forwardEnd() { - self.push(null); - }); - this._stream.pause(); -} - -/** - * _read implementation for both types of streams that allow reading. - * @this {ServerReadableObjectStream|ServerBidiObjectStream} - * @param {number} size Ignored - */ -function _read(size) { - this._stream.resume(); -} - -/** - * See docs for _read - */ -ServerReadableObjectStream.prototype._read = _read; - -util.inherits(ServerWritableObjectStream, Writable); - -/** - * Class for representing a gRPC server streaming call as a Node stream on the - * server side. Extends from stream.Writable. - * @constructor - * @param {stream} stream Underlying binary Duplex stream for the call - */ -function ServerWritableObjectStream(stream) { - var options = {objectMode: true}; - Writable.call(this, options); - this._stream = stream; - this._stream.on('cancelled', function() { - self.emit('cancelled'); - }); - this.on('finish', function() { - this._stream.end(); - }); -} - -/** - * _write implementation for both types of streams that allow writing - * @this {ServerWritableObjectStream} - * @param {*} chunk The value to write to the stream - * @param {string} encoding Ignored - * @param {function(Error)} callback Callback to call when finished writing - */ -function _write(chunk, encoding, callback) { - this._stream.write(chunk, encoding, callback); -} - -/** - * See docs for _write - */ -ServerWritableObjectStream.prototype._write = _write; - -/** - * Creates a binary stream handler function from a unary handler function - * @param {function(Object, function(Error, *), metadata=)} handler Unary call - * handler - * @return {function(stream, metadata=)} Binary stream handler - */ -function makeUnaryHandler(handler) { - /** - * Handles a stream by reading a single data value, passing it to the handler, - * and writing the response back to the stream. - * @param {stream} stream Binary data stream - * @param {metadata=} metadata Incoming metadata array - */ - return function handleUnaryCall(stream, metadata) { - stream.on('data', function handleUnaryData(value) { - var call = {request: value}; - Object.defineProperty(call, 'cancelled', { - get: function() { return stream.cancelled;} - }); - stream.on('cancelled', function() { - call.emit('cancelled'); - }); - handler(call, function sendUnaryData(err, value) { - if (err) { - stream.emit('error', err); - } else { - stream.write(value); - stream.end(); - } - }, metadata); - }); - }; -} - -/** - * Creates a binary stream handler function from a client stream handler - * function - * @param {function(Readable, function(Error, *), metadata=)} handler Client - * stream call handler - * @return {function(stream, metadata=)} Binary stream handler - */ -function makeClientStreamHandler(handler) { - /** - * Handles a stream by passing a deserializing stream to the handler and - * writing the response back to the stream. - * @param {stream} stream Binary data stream - * @param {metadata=} metadata Incoming metadata array - */ - return function handleClientStreamCall(stream, metadata) { - var object_stream = new ServerReadableObjectStream(stream); - handler(object_stream, function sendClientStreamData(err, value) { - if (err) { - stream.emit('error', err); - } else { - stream.write(value); - stream.end(); - } - }, metadata); - }; -} - -/** - * Creates a binary stream handler function from a server stream handler - * function - * @param {function(Writable, metadata=)} handler Server stream call handler - * @return {function(stream, metadata=)} Binary stream handler - */ -function makeServerStreamHandler(handler) { - /** - * Handles a stream by attaching it to a serializing stream, and passing it to - * the handler. - * @param {stream} stream Binary data stream - * @param {metadata=} metadata Incoming metadata array - */ - return function handleServerStreamCall(stream, metadata) { - stream.on('data', function handleClientData(value) { - var object_stream = new ServerWritableObjectStream(stream); - object_stream.request = value; - handler(object_stream, metadata); - }); - }; -} - -/** - * Creates a binary stream handler function from a bidi stream handler function - * @param {function(Duplex, metadata=)} handler Unary call handler - * @return {function(stream, metadata=)} Binary stream handler - */ -function makeBidiStreamHandler(handler) { - return handler; -} - -/** - * Map with short names for each of the handler maker functions. Used in - * makeServerConstructor - */ -var handler_makers = { - unary: makeUnaryHandler, - server_stream: makeServerStreamHandler, - client_stream: makeClientStreamHandler, - bidi: makeBidiStreamHandler -}; - -/** - * Creates a constructor for servers with a service defined by the methods - * object. The methods object has string keys and values of this form: - * {serialize: function, deserialize: function, client_stream: bool, - * server_stream: bool} - * @param {Object} methods Method descriptor for each method the server should - * expose - * @param {string} prefix The prefex to prepend to each method name - * @return {function(Object, Object)} New server constructor - */ -function makeServerConstructor(services) { - var qual_names = []; - _.each(services, function(service) { - _.each(service.children, function(method) { - var name = common.fullyQualifiedName(method); - if (_.indexOf(qual_names, name) !== -1) { - throw new Error('Method ' + name + ' exposed by more than one service'); - } - qual_names.push(name); - }); - }); - /** - * Create a server with the given handlers for all of the methods. - * @constructor - * @param {Object} service_handlers Map from service names to map from method - * names to handlers - * @param {function(string, Object<string, Array<Buffer>>): - Object<string, Array<Buffer|string>>=} getMetadata Callback that - * gets metatada for a given method - * @param {Object=} options Options to pass to the underlying server - */ - function SurfaceServer(service_handlers, getMetadata, options) { - var server = new Server(getMetadata, options); - this.inner_server = server; - _.each(services, function(service) { - var service_name = common.fullyQualifiedName(service); - if (service_handlers[service_name] === undefined) { - throw new Error('Handlers for service ' + - service_name + ' not provided.'); - } - var prefix = '/' + common.fullyQualifiedName(service) + '/'; - _.each(service.children, function(method) { - var method_type; - if (method.requestStream) { - if (method.responseStream) { - method_type = 'bidi'; - } else { - method_type = 'client_stream'; - } - } else { - if (method.responseStream) { - method_type = 'server_stream'; - } else { - method_type = 'unary'; - } - } - if (service_handlers[service_name][decapitalize(method.name)] === - undefined) { - throw new Error('Method handler for ' + - common.fullyQualifiedName(method) + ' not provided.'); - } - var binary_handler = handler_makers[method_type]( - service_handlers[service_name][decapitalize(method.name)]); - var serialize = common.serializeCls( - method.resolvedResponseType.build()); - var deserialize = common.deserializeCls( - method.resolvedRequestType.build()); - server.register(prefix + capitalize(method.name), binary_handler, - serialize, deserialize); - }); - }, this); - } - - /** - * Binds the server to the given port, with SSL enabled if secure is specified - * @param {string} port The port that the server should bind on, in the format - * "address:port" - * @param {boolean=} secure Whether the server should open a secure port - * @return {SurfaceServer} this - */ - SurfaceServer.prototype.bind = function(port, secure) { - return this.inner_server.bind(port, secure); - }; - - /** - * Starts the server listening on any bound ports - * @return {SurfaceServer} this - */ - SurfaceServer.prototype.listen = function() { - this.inner_server.start(); - return this; - }; - - /** - * Shuts the server down; tells it to stop listening for new requests and to - * kill old requests. - */ - SurfaceServer.prototype.shutdown = function() { - this.inner_server.shutdown(); - }; - - return SurfaceServer; -} - -/** - * See documentation for makeServerConstructor - */ -exports.makeServerConstructor = makeServerConstructor; diff --git a/src/node/test/call_test.js b/src/node/test/call_test.js index 48db245498..c1a7e95fa0 100644 --- a/src/node/test/call_test.js +++ b/src/node/test/call_test.js @@ -1,6 +1,6 @@ /* * - * Copyright 2014, Google Inc. + * Copyright 2015, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -98,100 +98,80 @@ describe('call', function() { }, TypeError); }); }); - describe('addMetadata', function() { - it('should succeed with a map from strings to string arrays', function() { + describe('startBatch', function() { + it('should fail without an object and a function', function() { var call = new grpc.Call(channel, 'method', getDeadline(1)); - assert.doesNotThrow(function() { - call.addMetadata({'key': ['value']}); + assert.throws(function() { + call.startBatch(); }); - assert.doesNotThrow(function() { - call.addMetadata({'key1': ['value1'], 'key2': ['value2']}); + assert.throws(function() { + call.startBatch({}); + }); + assert.throws(function() { + call.startBatch(null, function(){}); }); }); - it('should succeed with a map from strings to buffer arrays', function() { + it('should succeed with an empty object', function(done) { var call = new grpc.Call(channel, 'method', getDeadline(1)); assert.doesNotThrow(function() { - call.addMetadata({'key': [new Buffer('value')]}); - }); - assert.doesNotThrow(function() { - call.addMetadata({'key1': [new Buffer('value1')], - 'key2': [new Buffer('value2')]}); + call.startBatch({}, function(err) { + assert.ifError(err); + done(); + }); }); }); - it('should fail with other parameter types', function() { + }); + describe('startBatch with metadata', function() { + it('should succeed with a map of strings to string arrays', function(done) { var call = new grpc.Call(channel, 'method', getDeadline(1)); - assert.throws(function() { - call.addMetadata(); + assert.doesNotThrow(function() { + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = {'key1': ['value1'], + 'key2': ['value2']}; + call.startBatch(batch, function(err, resp) { + assert.ifError(err); + assert.deepEqual(resp, {'send metadata': true}); + done(); + }); }); - assert.throws(function() { - call.addMetadata(null); - }, TypeError); - assert.throws(function() { - call.addMetadata('value'); - }, TypeError); - assert.throws(function() { - call.addMetadata(5); - }, TypeError); }); - it.skip('should fail if invoke was already called', function(done) { + it('should succeed with a map of strings to buffer arrays', function(done) { var call = new grpc.Call(channel, 'method', getDeadline(1)); - call.invoke(function() {}, - function() {done();}, - 0); - assert.throws(function() { - call.addMetadata({'key': ['value']}); + assert.doesNotThrow(function() { + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = { + 'key1': [new Buffer('value1')], + 'key2': [new Buffer('value2')] + }; + call.startBatch(batch, function(err, resp) { + assert.ifError(err); + assert.deepEqual(resp, {'send metadata': true}); + done(); + }); }); - // Cancel to speed up the test - call.cancel(); }); - }); - describe('invoke', function() { - it('should fail with fewer than 3 arguments', function() { + it('should fail with other parameter types', function() { var call = new grpc.Call(channel, 'method', getDeadline(1)); assert.throws(function() { - call.invoke(); - }, TypeError); - assert.throws(function() { - call.invoke(function() {}); - }, TypeError); - assert.throws(function() { - call.invoke(function() {}, - function() {}); - }, TypeError); - }); - it('should work with 2 args and an int', function(done) { - assert.doesNotThrow(function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); - call.invoke(function() {}, - function() {done();}, - 0); - // Cancel to speed up the test - call.cancel(); + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = undefined; + call.startBatch(batch, function(){}); }); - }); - it('should reject incorrectly typed arguments', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); assert.throws(function() { - call.invoke(0, 0, 0); + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = null; + call.startBatch(batch, function(){}); }, TypeError); assert.throws(function() { - call.invoke(function() {}, - function() {}, 'test'); - }); - }); - }); - describe('serverAccept', function() { - it('should fail with fewer than 1 argument1', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); - assert.throws(function() { - call.serverAccept(); + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = 'value'; + call.startBatch(batch, function(){}); }, TypeError); - }); - it.skip('should return an error when called on a client Call', function() { - var call = new grpc.Call(channel, 'method', getDeadline(1)); assert.throws(function() { - call.serverAccept(function() {}); - }); + var batch = {}; + batch[grpc.opType.SEND_INITIAL_METADATA] = 5; + call.startBatch(batch, function(){}); + }, TypeError); }); }); describe('cancel', function() { diff --git a/src/node/test/client_server_test.js b/src/node/test/client_server_test.js deleted file mode 100644 index 1db9f69467..0000000000 --- a/src/node/test/client_server_test.js +++ /dev/null @@ -1,255 +0,0 @@ -/* - * - * 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. - * - */ - -var assert = require('assert'); -var fs = require('fs'); -var path = require('path'); -var grpc = require('bindings')('grpc.node'); -var Server = require('../src/server'); -var client = require('../src/client'); -var common = require('../src/common'); - -var ca_path = path.join(__dirname, 'data/ca.pem'); - -var key_path = path.join(__dirname, 'data/server1.key'); - -var pem_path = path.join(__dirname, 'data/server1.pem'); - -/** - * Helper function to return an absolute deadline given a relative timeout in - * seconds. - * @param {number} timeout_secs The number of seconds to wait before timing out - * @return {Date} A date timeout_secs in the future - */ -function getDeadline(timeout_secs) { - var deadline = new Date(); - deadline.setSeconds(deadline.getSeconds() + timeout_secs); - return deadline; -} - -/** - * Responds to every request with the same data as a response - * @param {Stream} stream - */ -function echoHandler(stream) { - stream.pipe(stream); -} - -/** - * Responds to every request with an error status - * @param {Stream} stream - */ -function errorHandler(stream) { - throw { - 'code' : grpc.status.UNIMPLEMENTED, - 'details' : 'error details' - }; -} - -/** - * Wait for a cancellation instead of responding - * @param {Stream} stream - */ -function cancelHandler(stream) { - // do nothing -} - -function metadataHandler(stream, metadata) { - stream.end(); -} - -/** - * Serialize a string to a Buffer - * @param {string} value The string to serialize - * @return {Buffer} The serialized value - */ -function stringSerialize(value) { - return new Buffer(value); -} - -/** - * Deserialize a Buffer to a string - * @param {Buffer} buffer The buffer to deserialize - * @return {string} The string value of the buffer - */ -function stringDeserialize(buffer) { - return buffer.toString(); -} - -describe('echo client', function() { - var server; - var channel; - before(function() { - server = new Server(function getMetadata(method, metadata) { - return {method: [method]}; - }); - var port_num = server.bind('0.0.0.0:0'); - server.register('echo', echoHandler); - server.register('error', errorHandler); - server.register('cancellation', cancelHandler); - server.register('metadata', metadataHandler); - server.start(); - - channel = new grpc.Channel('localhost:' + port_num); - }); - after(function() { - server.shutdown(); - }); - it('should receive echo responses', function(done) { - var messages = ['echo1', 'echo2', 'echo3', 'echo4']; - var stream = client.makeRequest( - channel, - 'echo', - stringSerialize, - stringDeserialize); - for (var i = 0; i < messages.length; i++) { - stream.write(messages[i]); - } - stream.end(); - var index = 0; - stream.on('data', function(chunk) { - assert.equal(messages[index], chunk); - index += 1; - }); - stream.on('status', function(status) { - assert.equal(status.code, client.status.OK); - }); - stream.on('end', function() { - assert.equal(index, messages.length); - done(); - }); - }); - it('should recieve metadata set by the server', function(done) { - var stream = client.makeRequest(channel, 'metadata'); - stream.on('metadata', function(metadata) { - assert.strictEqual(metadata.method[0].toString(), 'metadata'); - }); - stream.on('status', function(status) { - assert.equal(status.code, client.status.OK); - done(); - }); - stream.end(); - }); - it('should get an error status that the server throws', function(done) { - var stream = client.makeRequest(channel, 'error'); - - stream.on('data', function() {}); - stream.write(new Buffer('test')); - stream.end(); - stream.on('status', function(status) { - assert.equal(status.code, grpc.status.UNIMPLEMENTED); - assert.equal(status.details, 'error details'); - done(); - }); - }); - it('should be able to cancel a call', function(done) { - var stream = client.makeRequest( - channel, - 'cancellation', - null, - getDeadline(1)); - - stream.cancel(); - stream.on('status', function(status) { - assert.equal(status.code, grpc.status.CANCELLED); - done(); - }); - }); - it('should get correct status for unimplemented method', function(done) { - var stream = client.makeRequest(channel, 'unimplemented_method'); - stream.end(); - stream.on('status', function(status) { - assert.equal(status.code, grpc.status.UNIMPLEMENTED); - done(); - }); - }); -}); -/* TODO(mlumish): explore options for reducing duplication between this test - * and the insecure echo client test */ -describe('secure echo client', function() { - var server; - var channel; - before(function(done) { - fs.readFile(ca_path, function(err, ca_data) { - assert.ifError(err); - fs.readFile(key_path, function(err, key_data) { - assert.ifError(err); - fs.readFile(pem_path, function(err, pem_data) { - assert.ifError(err); - var creds = grpc.Credentials.createSsl(ca_data); - var server_creds = grpc.ServerCredentials.createSsl(null, - key_data, - pem_data); - - server = new Server(null, {'credentials' : server_creds}); - var port_num = server.bind('0.0.0.0:0', true); - server.register('echo', echoHandler); - server.start(); - - channel = new grpc.Channel('localhost:' + port_num, { - 'grpc.ssl_target_name_override' : 'foo.test.google.com', - 'credentials' : creds - }); - done(); - }); - }); - }); - }); - after(function() { - server.shutdown(); - }); - it('should recieve echo responses', function(done) { - var messages = ['echo1', 'echo2', 'echo3', 'echo4']; - var stream = client.makeRequest( - channel, - 'echo', - stringSerialize, - stringDeserialize); - for (var i = 0; i < messages.length; i++) { - stream.write(messages[i]); - } - stream.end(); - var index = 0; - stream.on('data', function(chunk) { - assert.equal(messages[index], chunk); - index += 1; - }); - stream.on('status', function(status) { - assert.equal(status.code, client.status.OK); - }); - stream.on('end', function() { - assert.equal(index, messages.length); - done(); - }); - }); -}); diff --git a/src/node/test/constant_test.js b/src/node/test/constant_test.js index 0138a55226..4d11e6f527 100644 --- a/src/node/test/constant_test.js +++ b/src/node/test/constant_test.js @@ -76,31 +76,6 @@ var callErrorNames = [ 'INVALID_FLAGS' ]; -/** - * List of all op error names - * @const - * @type {Array.<string>} - */ -var opErrorNames = [ - 'OK', - 'ERROR' -]; - -/** - * List of all completion type names - * @const - * @type {Array.<string>} - */ -var completionTypeNames = [ - 'QUEUE_SHUTDOWN', - 'READ', - 'WRITE_ACCEPTED', - 'FINISH_ACCEPTED', - 'CLIENT_METADATA_READ', - 'FINISHED', - 'SERVER_RPC_NEW' -]; - describe('constants', function() { it('should have all of the status constants', function() { for (var i = 0; i < statusNames.length; i++) { @@ -114,16 +89,4 @@ describe('constants', function() { 'call error missing: ' + callErrorNames[i]); } }); - it('should have all of the op errors', function() { - for (var i = 0; i < opErrorNames.length; i++) { - assert(grpc.opError.hasOwnProperty(opErrorNames[i]), - 'op error missing: ' + opErrorNames[i]); - } - }); - it('should have all of the completion types', function() { - for (var i = 0; i < completionTypeNames.length; i++) { - assert(grpc.completionType.hasOwnProperty(completionTypeNames[i]), - 'completion type missing: ' + completionTypeNames[i]); - } - }); }); diff --git a/src/node/test/end_to_end_test.js b/src/node/test/end_to_end_test.js index 1f53df23f3..f8899beae8 100644 --- a/src/node/test/end_to_end_test.js +++ b/src/node/test/end_to_end_test.js @@ -74,40 +74,49 @@ describe('end-to-end', function() { var status_text = 'xyz'; var call = new grpc.Call(channel, 'dummy_method', - deadline); - call.invoke(function(event) { - assert.strictEqual(event.type, - grpc.completionType.CLIENT_METADATA_READ); - },function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - var status = event.data; - assert.strictEqual(status.code, grpc.status.OK); - assert.strictEqual(status.details, status_text); + Infinity); + var client_batch = {}; + client_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + assert.ifError(err); + assert.deepEqual(response, { + 'send metadata': true, + 'client close': true, + 'metadata': {}, + 'status': { + 'code': grpc.status.OK, + 'details': status_text, + 'metadata': {} + } + }); done(); - }, 0); + }); - server.requestCall(function(event) { - assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); - var server_call = event.call; + server.requestCall(function(err, call_details) { + var new_call = call_details['new call']; + assert.notEqual(new_call, null); + var server_call = new_call.call; assert.notEqual(server_call, null); - server_call.serverAccept(function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - }, 0); - server_call.serverEndInitialMetadata(0); - server_call.startWriteStatus( - grpc.status.OK, - status_text, - function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - done(); - }); - }); - call.writesDone(function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); + var server_batch = {}; + server_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + server_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + 'metadata': {}, + 'code': grpc.status.OK, + 'details': status_text + }; + server_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + server_call.startBatch(server_batch, function(err, response) { + assert.ifError(err); + assert.deepEqual(response, { + 'send metadata': true, + 'send status': true, + 'cancelled': false + }); + done(); + }); }); }); it('should successfully send and receive metadata', function(complete) { @@ -117,115 +126,110 @@ describe('end-to-end', function() { var status_text = 'xyz'; var call = new grpc.Call(channel, 'dummy_method', - deadline); - call.addMetadata({'client_key': ['client_value']}); - call.invoke(function(event) { - assert.strictEqual(event.type, - grpc.completionType.CLIENT_METADATA_READ); - assert.strictEqual(event.data.server_key[0].toString(), 'server_value'); - },function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - var status = event.data; - assert.strictEqual(status.code, grpc.status.OK); - assert.strictEqual(status.details, status_text); + Infinity); + var client_batch = {}; + client_batch[grpc.opType.SEND_INITIAL_METADATA] = { + 'client_key': ['client_value'] + }; + client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + assert.ifError(err); + assert(response['send metadata']); + assert(response['client close']); + assert(response.hasOwnProperty('metadata')); + assert.strictEqual(response.metadata.server_key[0].toString(), + 'server_value'); + assert.deepEqual(response.status, {'code': grpc.status.OK, + 'details': status_text, + 'metadata': {}}); done(); - }, 0); + }); - server.requestCall(function(event) { - assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); - assert.strictEqual(event.data.metadata.client_key[0].toString(), + server.requestCall(function(err, call_details) { + var new_call = call_details['new call']; + assert.notEqual(new_call, null); + assert.strictEqual(new_call.metadata.client_key[0].toString(), 'client_value'); - var server_call = event.call; + var server_call = new_call.call; assert.notEqual(server_call, null); - server_call.serverAccept(function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - }, 0); - server_call.addMetadata({'server_key': ['server_value']}); - server_call.serverEndInitialMetadata(0); - server_call.startWriteStatus( - grpc.status.OK, - status_text, - function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - done(); - }); - }); - call.writesDone(function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); + var server_batch = {}; + server_batch[grpc.opType.SEND_INITIAL_METADATA] = { + 'server_key': ['server_value'] + }; + server_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + 'metadata': {}, + 'code': grpc.status.OK, + 'details': status_text + }; + server_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + server_call.startBatch(server_batch, function(err, response) { + assert.ifError(err); + assert.deepEqual(response, { + 'send metadata': true, + 'send status': true, + 'cancelled': false + }); + done(); + }); }); }); it('should send and receive data without error', function(complete) { var req_text = 'client_request'; var reply_text = 'server_response'; - var done = multiDone(complete, 6); + var done = multiDone(complete, 2); var deadline = new Date(); deadline.setSeconds(deadline.getSeconds() + 3); var status_text = 'success'; var call = new grpc.Call(channel, 'dummy_method', - deadline); - call.invoke(function(event) { - assert.strictEqual(event.type, - grpc.completionType.CLIENT_METADATA_READ); - done(); - },function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - var status = event.data; - assert.strictEqual(status.code, grpc.status.OK); - assert.strictEqual(status.details, status_text); - done(); - }, 0); - call.startWrite( - new Buffer(req_text), - function(event) { - assert.strictEqual(event.type, - grpc.completionType.WRITE_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - call.writesDone(function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - done(); - }); - }, 0); - call.startRead(function(event) { - assert.strictEqual(event.type, grpc.completionType.READ); - assert.strictEqual(event.data.toString(), reply_text); + Infinity); + var client_batch = {}; + client_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + client_batch[grpc.opType.SEND_MESSAGE] = new Buffer(req_text); + client_batch[grpc.opType.SEND_CLOSE_FROM_CLIENT] = true; + client_batch[grpc.opType.RECV_INITIAL_METADATA] = true; + client_batch[grpc.opType.RECV_MESSAGE] = true; + client_batch[grpc.opType.RECV_STATUS_ON_CLIENT] = true; + call.startBatch(client_batch, function(err, response) { + assert.ifError(err); + assert(response['send metadata']); + assert(response['client close']); + assert.deepEqual(response.metadata, {}); + assert(response['send message']); + assert.strictEqual(response.read.toString(), reply_text); + assert.deepEqual(response.status, {'code': grpc.status.OK, + 'details': status_text, + 'metadata': {}}); done(); }); - server.requestCall(function(event) { - assert.strictEqual(event.type, grpc.completionType.SERVER_RPC_NEW); - var server_call = event.call; + + server.requestCall(function(err, call_details) { + var new_call = call_details['new call']; + assert.notEqual(new_call, null); + var server_call = new_call.call; assert.notEqual(server_call, null); - server_call.serverAccept(function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - done(); - }); - server_call.serverEndInitialMetadata(0); - server_call.startRead(function(event) { - assert.strictEqual(event.type, grpc.completionType.READ); - assert.strictEqual(event.data.toString(), req_text); - server_call.startWrite( - new Buffer(reply_text), - function(event) { - assert.strictEqual(event.type, - grpc.completionType.WRITE_ACCEPTED); - assert.strictEqual(event.data, - grpc.opError.OK); - server_call.startWriteStatus( - grpc.status.OK, - status_text, - function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - done(); - }); - }, 0); + var server_batch = {}; + server_batch[grpc.opType.SEND_INITIAL_METADATA] = {}; + server_batch[grpc.opType.RECV_MESSAGE] = true; + server_call.startBatch(server_batch, function(err, response) { + assert.ifError(err); + assert(response['send metadata']); + assert.strictEqual(response.read.toString(), req_text); + var response_batch = {}; + response_batch[grpc.opType.SEND_MESSAGE] = new Buffer(reply_text); + response_batch[grpc.opType.SEND_STATUS_FROM_SERVER] = { + 'metadata': {}, + 'code': grpc.status.OK, + 'details': status_text + }; + response_batch[grpc.opType.RECV_CLOSE_ON_SERVER] = true; + server_call.startBatch(response_batch, function(err, response) { + assert(response['send status']); + assert(!response['cancelled']); + done(); + }); }); }); }); diff --git a/src/node/test/interop_sanity_test.js b/src/node/test/interop_sanity_test.js index 7ecaad833d..81cd9fa5b9 100644 --- a/src/node/test/interop_sanity_test.js +++ b/src/node/test/interop_sanity_test.js @@ -56,7 +56,7 @@ describe('Interop tests', function() { interop_client.runTest(port, name_override, 'empty_unary', true, done); }); // This fails due to an unknown bug - it.skip('should pass large_unary', function(done) { + it('should pass large_unary', function(done) { interop_client.runTest(port, name_override, 'large_unary', true, done); }); it('should pass client_streaming', function(done) { diff --git a/src/node/test/math_client_test.js b/src/node/test/math_client_test.js index 0e365bf870..61b4a2fa2f 100644 --- a/src/node/test/math_client_test.js +++ b/src/node/test/math_client_test.js @@ -63,9 +63,6 @@ describe('Math client', function() { assert.ifError(err); assert.equal(value.quotient, 1); assert.equal(value.remainder, 3); - }); - call.on('status', function checkStatus(status) { - assert.strictEqual(status.code, grpc.status.OK); done(); }); }); diff --git a/src/node/test/server_test.js b/src/node/test/server_test.js deleted file mode 100644 index a3e1edf50f..0000000000 --- a/src/node/test/server_test.js +++ /dev/null @@ -1,122 +0,0 @@ -/* - * - * 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. - * - */ - -var assert = require('assert'); -var grpc = require('bindings')('grpc.node'); -var Server = require('../src/server'); - -/** - * This is used for testing functions with multiple asynchronous calls that - * can happen in different orders. This should be passed the number of async - * function invocations that can occur last, and each of those should call this - * function's return value - * @param {function()} done The function that should be called when a test is - * complete. - * @param {number} count The number of calls to the resulting function if the - * test passes. - * @return {function()} The function that should be called at the end of each - * sequence of asynchronous functions. - */ -function multiDone(done, count) { - return function() { - count -= 1; - if (count <= 0) { - done(); - } - }; -} - -/** - * Responds to every request with the same data as a response - * @param {Stream} stream - */ -function echoHandler(stream) { - stream.pipe(stream); -} - -describe('echo server', function() { - var server; - var channel; - before(function() { - server = new Server(); - var port_num = server.bind('[::]:0'); - server.register('echo', echoHandler); - server.start(); - - channel = new grpc.Channel('localhost:' + port_num); - }); - after(function() { - server.shutdown(); - }); - it('should echo inputs as responses', function(done) { - done = multiDone(done, 4); - - var req_text = 'echo test string'; - var status_text = 'OK'; - - var deadline = new Date(); - deadline.setSeconds(deadline.getSeconds() + 3); - var call = new grpc.Call(channel, - 'echo', - deadline); - call.invoke(function(event) { - assert.strictEqual(event.type, - grpc.completionType.CLIENT_METADATA_READ); - done(); - },function(event) { - assert.strictEqual(event.type, grpc.completionType.FINISHED); - var status = event.data; - assert.strictEqual(status.code, grpc.status.OK); - assert.strictEqual(status.details, status_text); - done(); - }, 0); - call.startWrite( - new Buffer(req_text), - function(event) { - assert.strictEqual(event.type, - grpc.completionType.WRITE_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - call.writesDone(function(event) { - assert.strictEqual(event.type, - grpc.completionType.FINISH_ACCEPTED); - assert.strictEqual(event.data, grpc.opError.OK); - done(); - }); - }, 0); - call.startRead(function(event) { - assert.strictEqual(event.type, grpc.completionType.READ); - assert.strictEqual(event.data.toString(), req_text); - done(); - }); - }); -}); diff --git a/src/node/test/surface_test.js b/src/node/test/surface_test.js index 1038f9ab33..34e4ab4013 100644 --- a/src/node/test/surface_test.js +++ b/src/node/test/surface_test.js @@ -33,9 +33,9 @@ var assert = require('assert'); -var surface_server = require('../src/surface_server.js'); +var surface_server = require('../src/server.js'); -var surface_client = require('../src/surface_client.js'); +var surface_client = require('../src/client.js'); var ProtoBuf = require('protobufjs'); diff --git a/src/python/interop/interop/__init__.py b/src/python/interop/interop/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/src/python/interop/interop/__init__.py diff --git a/src/python/interop/interop/credentials/README b/src/python/interop/interop/credentials/README new file mode 100755 index 0000000000..cb20dcb49f --- /dev/null +++ b/src/python/interop/interop/credentials/README @@ -0,0 +1 @@ +These are test keys *NOT* to be used in production. diff --git a/src/python/interop/interop/credentials/server1.key b/src/python/interop/interop/credentials/server1.key new file mode 100755 index 0000000000..143a5b8765 --- /dev/null +++ b/src/python/interop/interop/credentials/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/python/interop/interop/credentials/server1.pem b/src/python/interop/interop/credentials/server1.pem new file mode 100755 index 0000000000..8e582e571f --- /dev/null +++ b/src/python/interop/interop/credentials/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/python/interop/interop/empty_pb2.py b/src/python/interop/interop/empty_pb2.py new file mode 100644 index 0000000000..753341c7da --- /dev/null +++ b/src/python/interop/interop/empty_pb2.py @@ -0,0 +1,60 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: test/cpp/interop/empty.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='test/cpp/interop/empty.proto', + package='grpc.testing', + serialized_pb=_b('\n\x1ctest/cpp/interop/empty.proto\x12\x0cgrpc.testing\"\x07\n\x05\x45mpty') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_EMPTY = _descriptor.Descriptor( + name='Empty', + full_name='grpc.testing.Empty', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=46, + serialized_end=53, +) + +DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY + +Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), dict( + DESCRIPTOR = _EMPTY, + __module__ = 'test.cpp.interop.empty_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.Empty) + )) +_sym_db.RegisterMessage(Empty) + + +# @@protoc_insertion_point(module_scope) diff --git a/src/python/interop/interop/messages_pb2.py b/src/python/interop/interop/messages_pb2.py new file mode 100644 index 0000000000..79270cdf12 --- /dev/null +++ b/src/python/interop/interop/messages_pb2.py @@ -0,0 +1,444 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: test/cpp/interop/messages.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='test/cpp/interop/messages.proto', + package='grpc.testing', + serialized_pb=_b('\n\x1ftest/cpp/interop/messages.proto\x12\x0cgrpc.testing\"@\n\x07Payload\x12\'\n\x04type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12\x0c\n\x04\x62ody\x18\x02 \x01(\x0c\"\xb1\x01\n\rSimpleRequest\x12\x30\n\rresponse_type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12\x15\n\rresponse_size\x18\x02 \x01(\x05\x12&\n\x07payload\x18\x03 \x01(\x0b\x32\x15.grpc.testing.Payload\x12\x15\n\rfill_username\x18\x04 \x01(\x08\x12\x18\n\x10\x66ill_oauth_scope\x18\x05 \x01(\x08\"_\n\x0eSimpleResponse\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x13\n\x0boauth_scope\x18\x03 \x01(\t\"C\n\x19StreamingInputCallRequest\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload\"=\n\x1aStreamingInputCallResponse\x12\x1f\n\x17\x61ggregated_payload_size\x18\x01 \x01(\x05\"7\n\x12ResponseParameters\x12\x0c\n\x04size\x18\x01 \x01(\x05\x12\x13\n\x0binterval_us\x18\x02 \x01(\x05\"\xb5\x01\n\x1aStreamingOutputCallRequest\x12\x30\n\rresponse_type\x18\x01 \x01(\x0e\x32\x19.grpc.testing.PayloadType\x12=\n\x13response_parameters\x18\x02 \x03(\x0b\x32 .grpc.testing.ResponseParameters\x12&\n\x07payload\x18\x03 \x01(\x0b\x32\x15.grpc.testing.Payload\"E\n\x1bStreamingOutputCallResponse\x12&\n\x07payload\x18\x01 \x01(\x0b\x32\x15.grpc.testing.Payload*?\n\x0bPayloadType\x12\x10\n\x0c\x43OMPRESSABLE\x10\x00\x12\x12\n\x0eUNCOMPRESSABLE\x10\x01\x12\n\n\x06RANDOM\x10\x02') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +_PAYLOADTYPE = _descriptor.EnumDescriptor( + name='PayloadType', + full_name='grpc.testing.PayloadType', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='COMPRESSABLE', index=0, number=0, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='UNCOMPRESSABLE', index=1, number=1, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='RANDOM', index=2, number=2, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=836, + serialized_end=899, +) +_sym_db.RegisterEnumDescriptor(_PAYLOADTYPE) + +PayloadType = enum_type_wrapper.EnumTypeWrapper(_PAYLOADTYPE) +COMPRESSABLE = 0 +UNCOMPRESSABLE = 1 +RANDOM = 2 + + + +_PAYLOAD = _descriptor.Descriptor( + name='Payload', + full_name='grpc.testing.Payload', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='type', full_name='grpc.testing.Payload.type', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='body', full_name='grpc.testing.Payload.body', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=49, + serialized_end=113, +) + + +_SIMPLEREQUEST = _descriptor.Descriptor( + name='SimpleRequest', + full_name='grpc.testing.SimpleRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='response_type', full_name='grpc.testing.SimpleRequest.response_type', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='response_size', full_name='grpc.testing.SimpleRequest.response_size', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='payload', full_name='grpc.testing.SimpleRequest.payload', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='fill_username', full_name='grpc.testing.SimpleRequest.fill_username', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='fill_oauth_scope', full_name='grpc.testing.SimpleRequest.fill_oauth_scope', index=4, + number=5, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=116, + serialized_end=293, +) + + +_SIMPLERESPONSE = _descriptor.Descriptor( + name='SimpleResponse', + full_name='grpc.testing.SimpleResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='payload', full_name='grpc.testing.SimpleResponse.payload', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='username', full_name='grpc.testing.SimpleResponse.username', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='oauth_scope', full_name='grpc.testing.SimpleResponse.oauth_scope', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=295, + serialized_end=390, +) + + +_STREAMINGINPUTCALLREQUEST = _descriptor.Descriptor( + name='StreamingInputCallRequest', + full_name='grpc.testing.StreamingInputCallRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='payload', full_name='grpc.testing.StreamingInputCallRequest.payload', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=392, + serialized_end=459, +) + + +_STREAMINGINPUTCALLRESPONSE = _descriptor.Descriptor( + name='StreamingInputCallResponse', + full_name='grpc.testing.StreamingInputCallResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='aggregated_payload_size', full_name='grpc.testing.StreamingInputCallResponse.aggregated_payload_size', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=461, + serialized_end=522, +) + + +_RESPONSEPARAMETERS = _descriptor.Descriptor( + name='ResponseParameters', + full_name='grpc.testing.ResponseParameters', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='size', full_name='grpc.testing.ResponseParameters.size', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='interval_us', full_name='grpc.testing.ResponseParameters.interval_us', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=524, + serialized_end=579, +) + + +_STREAMINGOUTPUTCALLREQUEST = _descriptor.Descriptor( + name='StreamingOutputCallRequest', + full_name='grpc.testing.StreamingOutputCallRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='response_type', full_name='grpc.testing.StreamingOutputCallRequest.response_type', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='response_parameters', full_name='grpc.testing.StreamingOutputCallRequest.response_parameters', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='payload', full_name='grpc.testing.StreamingOutputCallRequest.payload', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=582, + serialized_end=763, +) + + +_STREAMINGOUTPUTCALLRESPONSE = _descriptor.Descriptor( + name='StreamingOutputCallResponse', + full_name='grpc.testing.StreamingOutputCallResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='payload', full_name='grpc.testing.StreamingOutputCallResponse.payload', index=0, + number=1, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + extension_ranges=[], + oneofs=[ + ], + serialized_start=765, + serialized_end=834, +) + +_PAYLOAD.fields_by_name['type'].enum_type = _PAYLOADTYPE +_SIMPLEREQUEST.fields_by_name['response_type'].enum_type = _PAYLOADTYPE +_SIMPLEREQUEST.fields_by_name['payload'].message_type = _PAYLOAD +_SIMPLERESPONSE.fields_by_name['payload'].message_type = _PAYLOAD +_STREAMINGINPUTCALLREQUEST.fields_by_name['payload'].message_type = _PAYLOAD +_STREAMINGOUTPUTCALLREQUEST.fields_by_name['response_type'].enum_type = _PAYLOADTYPE +_STREAMINGOUTPUTCALLREQUEST.fields_by_name['response_parameters'].message_type = _RESPONSEPARAMETERS +_STREAMINGOUTPUTCALLREQUEST.fields_by_name['payload'].message_type = _PAYLOAD +_STREAMINGOUTPUTCALLRESPONSE.fields_by_name['payload'].message_type = _PAYLOAD +DESCRIPTOR.message_types_by_name['Payload'] = _PAYLOAD +DESCRIPTOR.message_types_by_name['SimpleRequest'] = _SIMPLEREQUEST +DESCRIPTOR.message_types_by_name['SimpleResponse'] = _SIMPLERESPONSE +DESCRIPTOR.message_types_by_name['StreamingInputCallRequest'] = _STREAMINGINPUTCALLREQUEST +DESCRIPTOR.message_types_by_name['StreamingInputCallResponse'] = _STREAMINGINPUTCALLRESPONSE +DESCRIPTOR.message_types_by_name['ResponseParameters'] = _RESPONSEPARAMETERS +DESCRIPTOR.message_types_by_name['StreamingOutputCallRequest'] = _STREAMINGOUTPUTCALLREQUEST +DESCRIPTOR.message_types_by_name['StreamingOutputCallResponse'] = _STREAMINGOUTPUTCALLRESPONSE +DESCRIPTOR.enum_types_by_name['PayloadType'] = _PAYLOADTYPE + +Payload = _reflection.GeneratedProtocolMessageType('Payload', (_message.Message,), dict( + DESCRIPTOR = _PAYLOAD, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.Payload) + )) +_sym_db.RegisterMessage(Payload) + +SimpleRequest = _reflection.GeneratedProtocolMessageType('SimpleRequest', (_message.Message,), dict( + DESCRIPTOR = _SIMPLEREQUEST, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.SimpleRequest) + )) +_sym_db.RegisterMessage(SimpleRequest) + +SimpleResponse = _reflection.GeneratedProtocolMessageType('SimpleResponse', (_message.Message,), dict( + DESCRIPTOR = _SIMPLERESPONSE, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.SimpleResponse) + )) +_sym_db.RegisterMessage(SimpleResponse) + +StreamingInputCallRequest = _reflection.GeneratedProtocolMessageType('StreamingInputCallRequest', (_message.Message,), dict( + DESCRIPTOR = _STREAMINGINPUTCALLREQUEST, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.StreamingInputCallRequest) + )) +_sym_db.RegisterMessage(StreamingInputCallRequest) + +StreamingInputCallResponse = _reflection.GeneratedProtocolMessageType('StreamingInputCallResponse', (_message.Message,), dict( + DESCRIPTOR = _STREAMINGINPUTCALLRESPONSE, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.StreamingInputCallResponse) + )) +_sym_db.RegisterMessage(StreamingInputCallResponse) + +ResponseParameters = _reflection.GeneratedProtocolMessageType('ResponseParameters', (_message.Message,), dict( + DESCRIPTOR = _RESPONSEPARAMETERS, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.ResponseParameters) + )) +_sym_db.RegisterMessage(ResponseParameters) + +StreamingOutputCallRequest = _reflection.GeneratedProtocolMessageType('StreamingOutputCallRequest', (_message.Message,), dict( + DESCRIPTOR = _STREAMINGOUTPUTCALLREQUEST, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.StreamingOutputCallRequest) + )) +_sym_db.RegisterMessage(StreamingOutputCallRequest) + +StreamingOutputCallResponse = _reflection.GeneratedProtocolMessageType('StreamingOutputCallResponse', (_message.Message,), dict( + DESCRIPTOR = _STREAMINGOUTPUTCALLRESPONSE, + __module__ = 'test.cpp.interop.messages_pb2' + # @@protoc_insertion_point(class_scope:grpc.testing.StreamingOutputCallResponse) + )) +_sym_db.RegisterMessage(StreamingOutputCallResponse) + + +# @@protoc_insertion_point(module_scope) diff --git a/src/python/interop/interop/methods.py b/src/python/interop/interop/methods.py new file mode 100644 index 0000000000..e5ce5902ca --- /dev/null +++ b/src/python/interop/interop/methods.py @@ -0,0 +1,109 @@ +# Copyright 2015, 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. + +"""Implementations of interoperability test methods.""" + +from grpc_early_adopter import utilities + +from interop import empty_pb2 +from interop import messages_pb2 + +def _empty_call(request): + return empty_pb2.Empty() + +EMPTY_CALL = utilities.unary_unary_rpc_method( + _empty_call, empty_pb2.Empty.SerializeToString, empty_pb2.Empty.FromString, + empty_pb2.Empty.SerializeToString, empty_pb2.Empty.FromString) + + +def _unary_call(request): + return messages_pb2.SimpleResponse( + payload=messages_pb2.Payload( + type=messages_pb2.COMPRESSABLE, + body=b'\x00' * request.response_size)) + +UNARY_CALL = utilities.unary_unary_rpc_method( + _unary_call, messages_pb2.SimpleRequest.SerializeToString, + messages_pb2.SimpleRequest.FromString, + messages_pb2.SimpleResponse.SerializeToString, + messages_pb2.SimpleResponse.FromString) + + +def _streaming_output_call(request): + for response_parameters in request.response_parameters: + yield messages_pb2.StreamingOutputCallResponse( + payload=messages_pb2.Payload( + type=request.response_type, + body=b'\x00' * response_parameters.size)) + +STREAMING_OUTPUT_CALL = utilities.unary_stream_rpc_method( + _streaming_output_call, + messages_pb2.StreamingOutputCallRequest.SerializeToString, + messages_pb2.StreamingOutputCallRequest.FromString, + messages_pb2.StreamingOutputCallResponse.SerializeToString, + messages_pb2.StreamingOutputCallResponse.FromString) + + +def _streaming_input_call(request_iterator): + aggregate_size = 0 + for request in request_iterator: + if request.payload and request.payload.body: + aggregate_size += len(request.payload.body) + return messages_pb2.StreamingInputCallResponse( + aggregated_payload_size=aggregate_size) + +STREAMING_INPUT_CALL = utilities.stream_unary_rpc_method( + _streaming_input_call, + messages_pb2.StreamingInputCallRequest.SerializeToString, + messages_pb2.StreamingInputCallRequest.FromString, + messages_pb2.StreamingInputCallResponse.SerializeToString, + messages_pb2.StreamingInputCallResponse.FromString) + + +def _full_duplex_call(request_iterator): + for request in request_iterator: + yield messages_pb2.StreamingOutputCallResponse( + payload=messages_pb2.Payload( + type=request.payload.type, + body=b'\x00' * request.response_parameters[0].size)) + +FULL_DUPLEX_CALL = utilities.stream_stream_rpc_method( + _full_duplex_call, + messages_pb2.StreamingOutputCallRequest.SerializeToString, + messages_pb2.StreamingOutputCallRequest.FromString, + messages_pb2.StreamingOutputCallResponse.SerializeToString, + messages_pb2.StreamingOutputCallResponse.FromString) + +# NOTE(nathaniel): Apparently this is the same as the full-duplex call? +HALF_DUPLEX_CALL = utilities.stream_stream_rpc_method( + _full_duplex_call, + messages_pb2.StreamingOutputCallRequest.SerializeToString, + messages_pb2.StreamingOutputCallRequest.FromString, + messages_pb2.StreamingOutputCallResponse.SerializeToString, + messages_pb2.StreamingOutputCallResponse.FromString) diff --git a/src/python/interop/interop/server.py b/src/python/interop/interop/server.py new file mode 100644 index 0000000000..404c87dd0a --- /dev/null +++ b/src/python/interop/interop/server.py @@ -0,0 +1,91 @@ +# Copyright 2015, 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. + +"""The Python implementation of the GRPC interoperability test server.""" + +import argparse +import logging +import pkg_resources +import time + +from grpc_early_adopter import implementations + +from interop import methods + +_ONE_DAY_IN_SECONDS = 60 * 60 * 24 + +_PRIVATE_KEY_RESOURCE_PATH = 'credentials/server1.key' +_CERTIFICATE_CHAIN_RESOURCE_PATH = 'credentials/server1.pem' + +_METHODS = { + '/grpc.testing.TestService/EmptyCall': methods.EMPTY_CALL, + '/grpc.testing.TestService/UnaryCall': methods.UNARY_CALL, + '/grpc.testing.TestService/StreamingOutputCall': + methods.STREAMING_OUTPUT_CALL, + '/grpc.testing.TestService/StreamingInputCall': + methods.STREAMING_INPUT_CALL, + '/grpc.testing.TestService/FullDuplexCall': + methods.FULL_DUPLEX_CALL, + '/grpc.testing.TestService/HalfDuplexCall': + methods.HALF_DUPLEX_CALL, +} + + +def serve(): + parser = argparse.ArgumentParser() + parser.add_argument( + '--port', help='the port on which to serve', type=int) + parser.add_argument( + '--use_tls', help='require a secure connection', dest='use_tls', + action='store_true') + args = parser.parse_args() + + if args.use_tls: + private_key = pkg_resources.resource_string( + __name__, _PRIVATE_KEY_RESOURCE_PATH) + certificate_chain = pkg_resources.resource_string( + __name__, _CERTIFICATE_CHAIN_RESOURCE_PATH) + server = implementations.secure_server( + _METHODS, args.port, private_key, certificate_chain) + else: + server = implementations.insecure_server( + _METHODS, args.port) + + server.start() + logging.info('Server serving.') + try: + while True: + time.sleep(_ONE_DAY_IN_SECONDS) + except BaseException as e: + logging.info('Caught exception "%s"; stopping server...', e) + server.stop() + logging.info('Server stopped; exiting.') + +if __name__ == '__main__': + serve() diff --git a/src/python/interop/interop/test_pb2.py b/src/python/interop/interop/test_pb2.py new file mode 100644 index 0000000000..1241453159 --- /dev/null +++ b/src/python/interop/interop/test_pb2.py @@ -0,0 +1,32 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: test/cpp/interop/test.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from test.cpp.interop import empty_pb2 as test_dot_cpp_dot_interop_dot_empty__pb2 +from test.cpp.interop import messages_pb2 as test_dot_cpp_dot_interop_dot_messages__pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='test/cpp/interop/test.proto', + package='grpc.testing', + serialized_pb=_b('\n\x1btest/cpp/interop/test.proto\x12\x0cgrpc.testing\x1a\x1ctest/cpp/interop/empty.proto\x1a\x1ftest/cpp/interop/messages.proto2\xbb\x04\n\x0bTestService\x12\x35\n\tEmptyCall\x12\x13.grpc.testing.Empty\x1a\x13.grpc.testing.Empty\x12\x46\n\tUnaryCall\x12\x1b.grpc.testing.SimpleRequest\x1a\x1c.grpc.testing.SimpleResponse\x12l\n\x13StreamingOutputCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse0\x01\x12i\n\x12StreamingInputCall\x12\'.grpc.testing.StreamingInputCallRequest\x1a(.grpc.testing.StreamingInputCallResponse(\x01\x12i\n\x0e\x46ullDuplexCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse(\x01\x30\x01\x12i\n\x0eHalfDuplexCall\x12(.grpc.testing.StreamingOutputCallRequest\x1a).grpc.testing.StreamingOutputCallResponse(\x01\x30\x01') + , + dependencies=[test_dot_cpp_dot_interop_dot_empty__pb2.DESCRIPTOR,test_dot_cpp_dot_interop_dot_messages__pb2.DESCRIPTOR,]) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + + +# @@protoc_insertion_point(module_scope) diff --git a/src/python/interop/setup.py b/src/python/interop/setup.py new file mode 100644 index 0000000000..4b7709f234 --- /dev/null +++ b/src/python/interop/setup.py @@ -0,0 +1,51 @@ +# Copyright 2015, 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. + +"""A setup module for the GRPC Python interop testing package.""" + +from distutils import core as _core + +_PACKAGES = ( + 'interop', +) + +_PACKAGE_DIRECTORIES = { + 'interop': 'interop', +} + +_PACKAGE_DATA = { + 'interop': ['credentials/server1.key', 'credentials/server1.pem',] +} + +_INSTALL_REQUIRES = ['grpc-2015>=0.0.1'] + +_core.setup( + name='interop', version='0.0.1', packages=_PACKAGES, + package_dir=_PACKAGE_DIRECTORIES, package_data=_PACKAGE_DATA, + install_requires=_INSTALL_REQUIRES) diff --git a/src/python/src/_adapter/_face_test_case.py b/src/python/src/_adapter/_face_test_case.py index 112dcfb928..2c6e6286b5 100644 --- a/src/python/src/_adapter/_face_test_case.py +++ b/src/python/src/_adapter/_face_test_case.py @@ -80,7 +80,7 @@ class FaceTestCase(test_case.FaceTestCase, coverage.BlockingCoverage): fore_link = fore.ForeLink( pool, serialization.request_deserializers, - serialization.response_serializers) + serialization.response_serializers, None, ()) port = fore_link.start() rear_link = rear.RearLink( 'localhost', port, pool, diff --git a/src/python/src/_adapter/_links_test.py b/src/python/src/_adapter/_links_test.py index 8341460a9a..d8bbb27127 100644 --- a/src/python/src/_adapter/_links_test.py +++ b/src/python/src/_adapter/_links_test.py @@ -67,7 +67,7 @@ class RoundTripTest(unittest.TestCase): test_rear_link = _test_links.RearLink(rear_action, None) fore_link = fore.ForeLink( - self.fore_link_pool, {test_method: None}, {test_method: None}) + self.fore_link_pool, {test_method: None}, {test_method: None}, None, ()) fore_link.join_rear_link(test_rear_link) test_rear_link.join_fore_link(fore_link) port = fore_link.start() @@ -120,7 +120,7 @@ class RoundTripTest(unittest.TestCase): fore_link = fore.ForeLink( self.fore_link_pool, {test_method: _IDENTITY}, - {test_method: _IDENTITY}) + {test_method: _IDENTITY}, None, ()) fore_link.join_rear_link(test_rear_link) test_rear_link.join_fore_link(fore_link) port = fore_link.start() @@ -182,7 +182,7 @@ class RoundTripTest(unittest.TestCase): fore_link = fore.ForeLink( self.fore_link_pool, {test_method: scenario.deserialize_request}, - {test_method: scenario.serialize_response}) + {test_method: scenario.serialize_response}, None, ()) fore_link.join_rear_link(test_rear_link) test_rear_link.join_fore_link(fore_link) port = fore_link.start() diff --git a/src/python/src/_adapter/_low.py b/src/python/src/_adapter/_low.py index 6c24087dad..09105eafa0 100644 --- a/src/python/src/_adapter/_low.py +++ b/src/python/src/_adapter/_low.py @@ -52,4 +52,5 @@ Call = _c.Call Channel = _c.Channel CompletionQueue = _c.CompletionQueue Server = _c.Server +ServerCredentials = _c.ServerCredentials # pylint: enable=invalid-name diff --git a/src/python/src/_adapter/_server.c b/src/python/src/_adapter/_server.c index 503be61ab4..2f8cc99e44 100644 --- a/src/python/src/_adapter/_server.c +++ b/src/python/src/_adapter/_server.c @@ -85,6 +85,19 @@ static PyObject *pygrpc_server_add_http2_addr(Server *self, PyObject *args) { return PyInt_FromLong(port); } +static PyObject *pygrpc_server_add_secure_http2_addr(Server *self, + PyObject *args) { + const char *addr; + int port; + PyArg_ParseTuple(args, "s", &addr); + port = grpc_server_add_secure_http2_port(self->c_server, addr); + if (port == 0) { + PyErr_SetString(PyExc_RuntimeError, "Couldn't add port to server!"); + return NULL; + } + return PyInt_FromLong(port); +} + static PyObject *pygrpc_server_start(Server *self) { grpc_server_start(self->c_server); @@ -118,6 +131,8 @@ static PyObject *pygrpc_server_stop(Server *self) { static PyMethodDef methods[] = { {"add_http2_addr", (PyCFunction)pygrpc_server_add_http2_addr, METH_VARARGS, "Add an HTTP2 address."}, + {"add_secure_http2_addr", (PyCFunction)pygrpc_server_add_secure_http2_addr, + METH_VARARGS, "Add a secure HTTP2 address."}, {"start", (PyCFunction)pygrpc_server_start, METH_NOARGS, "Starts the server."}, {"service", (PyCFunction)pygrpc_server_service, METH_VARARGS, diff --git a/src/python/src/_adapter/fore.py b/src/python/src/_adapter/fore.py index 2f102751f2..28aede1fd9 100644 --- a/src/python/src/_adapter/fore.py +++ b/src/python/src/_adapter/fore.py @@ -69,7 +69,8 @@ class ForeLink(ticket_interfaces.ForeLink): """A service-side bridge between RPC Framework and the C-ish _low code.""" def __init__( - self, pool, request_deserializers, response_serializers, port=None): + self, pool, request_deserializers, response_serializers, + root_certificates, key_chain_pairs, port=None): """Constructor. Args: @@ -78,6 +79,10 @@ class ForeLink(ticket_interfaces.ForeLink): deserializer behaviors. response_serializers: A dict from RPC method names to response object serializer behaviors. + root_certificates: The PEM-encoded client root certificates as a + bytestring or None. + key_chain_pairs: A sequence of PEM-encoded private key-certificate chain + pairs. port: The port on which to serve, or None to have a port selected automatically. """ @@ -85,6 +90,8 @@ class ForeLink(ticket_interfaces.ForeLink): self._pool = pool self._request_deserializers = request_deserializers self._response_serializers = response_serializers + self._root_certificates = root_certificates + self._key_chain_pairs = key_chain_pairs self._port = port self._rear_link = null.NULL_REAR_LINK @@ -264,10 +271,16 @@ class ForeLink(ticket_interfaces.ForeLink): object. """ with self._condition: + address = '[::]:%d' % (0 if self._port is None else self._port) self._completion_queue = _low.CompletionQueue() - self._server = _low.Server(self._completion_queue, None) - port = self._server.add_http2_addr( - '[::]:%d' % (0 if self._port is None else self._port)) + if self._root_certificates is None and not self._key_chain_pairs: + self._server = _low.Server(self._completion_queue, None) + port = self._server.add_http2_addr(address) + else: + server_credentials = _low.ServerCredentials( + self._root_certificates, self._key_chain_pairs) + self._server = _low.Server(self._completion_queue, server_credentials) + port = self._server.add_secure_http2_addr(address) self._server.start() self._server.service(None) diff --git a/src/python/src/_framework/base/packets/_ingestion.py b/src/python/src/_framework/base/packets/_ingestion.py index abc1e7a043..91f5a35359 100644 --- a/src/python/src/_framework/base/packets/_ingestion.py +++ b/src/python/src/_framework/base/packets/_ingestion.py @@ -183,7 +183,7 @@ class _WrappedConsumer(object): payload: A customer-significant payload object. May be None only if complete is True. complete: Whether or not the end of the payload sequence has been reached. - May be False only if payload is not None. + Must be True if payload is None. Returns: True if the wrapped consumer made progress or False if the wrapped @@ -191,13 +191,12 @@ class _WrappedConsumer(object): progress. """ try: - if payload: - if complete: - self._consumer.consume_and_terminate(payload) - else: - self._consumer.consume(payload) - else: + if payload is None: self._consumer.terminate() + elif complete: + self._consumer.consume_and_terminate(payload) + else: + self._consumer.consume(payload) return True except abandonment.Abandoned: return False diff --git a/src/python/src/_framework/face/_calls.py b/src/python/src/_framework/face/_calls.py index 9128aef7c4..a7d8be5e43 100644 --- a/src/python/src/_framework/face/_calls.py +++ b/src/python/src/_framework/face/_calls.py @@ -29,6 +29,7 @@ """Utility functions for invoking RPCs.""" +import sys import threading from _framework.base import interfaces as base_interfaces @@ -79,20 +80,46 @@ def _stream_event_subscription(result_consumer, abortion_callback): _EventServicedIngestor(result_consumer, abortion_callback)) +# NOTE(nathaniel): This class has some extremely special semantics around +# cancellation that allow it to be used by both "blocking" APIs and "futures" +# APIs. +# +# Since futures.Future defines its own exception for cancellation, we want these +# objects, when returned by methods of a returning-Futures-from-other-methods +# object, to raise the same exception for cancellation. But that's weird in a +# blocking API - why should this object, also returned by methods of blocking +# APIs, raise exceptions from the "future" module? Should we do something like +# have this class be parameterized by the type of exception that it raises in +# cancellation circumstances? +# +# We don't have to take such a dramatic step: since blocking APIs define no +# cancellation semantics whatsoever, there is no supported way for +# blocking-API-users of these objects to cancel RPCs, and thus no supported way +# for them to see an exception the type of which would be weird to them. +# +# Bonus: in both blocking and futures APIs, this object still properly raises +# exceptions.CancellationError for any *server-side cancellation* of an RPC. class _OperationCancellableIterator(interfaces.CancellableIterator): """An interfaces.CancellableIterator for response-streaming operations.""" def __init__(self, rendezvous, operation): + self._lock = threading.Lock() self._rendezvous = rendezvous self._operation = operation + self._cancelled = False def __iter__(self): return self def next(self): + with self._lock: + if self._cancelled: + raise future.CancelledError() return next(self._rendezvous) def cancel(self): + with self._lock: + self._cancelled = True self._operation.cancel() self._rendezvous.set_outcome(base_interfaces.Outcome.CANCELLED) @@ -105,46 +132,126 @@ class _OperationFuture(future.Future): self._rendezvous = rendezvous self._operation = operation - self._outcome = None + self._cancelled = False + self._computed = False + self._payload = None + self._exception = None + self._traceback = None self._callbacks = [] def cancel(self): """See future.Future.cancel for specification.""" with self._condition: - if self._outcome is None: + if not self._cancelled and not self._computed: self._operation.cancel() - self._outcome = future.aborted() + self._cancelled = True self._condition.notify_all() return False def cancelled(self): """See future.Future.cancelled for specification.""" - return False + with self._condition: + return self._cancelled + + def running(self): + """See future.Future.running for specification.""" + with self._condition: + return not self._cancelled and not self._computed def done(self): """See future.Future.done for specification.""" with self._condition: - return (self._outcome is not None and - self._outcome.category is not future.ABORTED) + return self._cancelled or self._computed + + def result(self, timeout=None): + """See future.Future.result for specification.""" + with self._condition: + if self._cancelled: + raise future.CancelledError() + if self._computed: + if self._payload is None: + raise self._exception # pylint: disable=raising-bad-type + else: + return self._payload + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._callbacks.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._condition: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + if self._payload is None: + raise self._exception # pylint: disable=raising-bad-type + else: + return self._payload + else: + raise future.TimeoutError() + + def exception(self, timeout=None): + """See future.Future.exception for specification.""" + with self._condition: + if self._cancelled: + raise future.CancelledError() + if self._computed: + return self._exception + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._callbacks.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) - def outcome(self): - """See future.Future.outcome for specification.""" with self._condition: - while self._outcome is None: - self._condition.wait() - return self._outcome + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._exception + else: + raise future.TimeoutError() - def add_done_callback(self, callback): + def traceback(self, timeout=None): + """See future.Future.traceback for specification.""" + with self._condition: + if self._cancelled: + raise future.CancelledError() + if self._computed: + return self._traceback + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._callbacks.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._condition: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._traceback + else: + raise future.TimeoutError() + + def add_done_callback(self, fn): """See future.Future.add_done_callback for specification.""" with self._condition: if self._callbacks is not None: - self._callbacks.add(callback) + self._callbacks.add(fn) return - outcome = self._outcome - - callable_util.call_logging_exceptions( - callback, _DONE_CALLBACK_LOG_MESSAGE, outcome) + callable_util.call_logging_exceptions(fn, _DONE_CALLBACK_LOG_MESSAGE, self) def on_operation_termination(self, operation_outcome): """Indicates to this object that the operation has terminated. @@ -154,34 +261,42 @@ class _OperationFuture(future.Future): outcome of the operation. """ with self._condition: - if (self._outcome is None and - operation_outcome is not base_interfaces.Outcome.COMPLETED): - self._outcome = future.raised( - _control.abortion_outcome_to_exception(operation_outcome)) - self._condition.notify_all() - - outcome = self._outcome - rendezvous = self._rendezvous - callbacks = list(self._callbacks) - self._callbacks = None - - if outcome is None: - try: - return_value = next(rendezvous) - except Exception as e: # pylint: disable=broad-except - outcome = future.raised(e) + cancelled = self._cancelled + if cancelled: + callbacks = list(self._callbacks) + self._callbacks = None else: - outcome = future.returned(return_value) + rendezvous = self._rendezvous + + if not cancelled: + payload = None + exception = None + traceback = None + if operation_outcome == base_interfaces.Outcome.COMPLETED: + try: + payload = next(rendezvous) + except Exception as e: # pylint: disable=broad-except + exception = e + traceback = sys.exc_info()[2] + else: + try: + # We raise and then immediately catch in order to create a traceback. + raise _control.abortion_outcome_to_exception(operation_outcome) + except Exception as e: # pylint: disable=broad-except + exception = e + traceback = sys.exc_info()[2] with self._condition: - if self._outcome is None: - self._outcome = outcome - self._condition.notify_all() - else: - outcome = self._outcome + if not self._cancelled: + self._computed = True + self._payload = payload + self._exception = exception + self._traceback = traceback + callbacks = list(self._callbacks) + self._callbacks = None for callback in callbacks: callable_util.call_logging_exceptions( - callback, _DONE_CALLBACK_LOG_MESSAGE, outcome) + callback, _DONE_CALLBACK_LOG_MESSAGE, self) class _Call(interfaces.Call): diff --git a/src/python/src/_framework/face/testing/future_invocation_asynchronous_event_service_test_case.py b/src/python/src/_framework/face/testing/future_invocation_asynchronous_event_service_test_case.py index cf8b2eeb95..939b238b66 100644 --- a/src/python/src/_framework/face/testing/future_invocation_asynchronous_event_service_test_case.py +++ b/src/python/src/_framework/face/testing/future_invocation_asynchronous_event_service_test_case.py @@ -116,7 +116,7 @@ class FutureInvocationAsynchronousEventServiceTestCase( response_future = self.stub.future_value_in_value_out( name, request, _TIMEOUT) - response = response_future.outcome().return_value + response = response_future.result() test_messages.verify(request, response, self) @@ -144,7 +144,7 @@ class FutureInvocationAsynchronousEventServiceTestCase( with request_iterator.pause(): response_future = self.stub.future_stream_in_value_out( name, request_iterator, _TIMEOUT) - response = response_future.outcome().return_value + response = response_future.result() test_messages.verify(requests, response, self) @@ -173,13 +173,13 @@ class FutureInvocationAsynchronousEventServiceTestCase( first_response_future = self.stub.future_value_in_value_out( name, first_request, _TIMEOUT) - first_response = first_response_future.outcome().return_value + first_response = first_response_future.result() test_messages.verify(first_request, first_response, self) second_response_future = self.stub.future_value_in_value_out( name, second_request, _TIMEOUT) - second_response = second_response_future.outcome().return_value + second_response = second_response_future.result() test_messages.verify(second_request, second_response, self) @@ -192,10 +192,10 @@ class FutureInvocationAsynchronousEventServiceTestCase( with self.control.pause(): response_future = self.stub.future_value_in_value_out( name, request, _TIMEOUT) - outcome = response_future.outcome() - - self.assertIsInstance( - outcome.exception, exceptions.ExpirationError) + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() def testExpiredUnaryRequestStreamResponse(self): for name, test_messages_sequence in ( @@ -203,11 +203,11 @@ class FutureInvocationAsynchronousEventServiceTestCase( for test_messages in test_messages_sequence: request = test_messages.request() - with self.control.pause(), self.assertRaises( - exceptions.ExpirationError): + with self.control.pause(): response_iterator = self.stub.inline_value_in_stream_out( name, request, _TIMEOUT) - list(response_iterator) + with self.assertRaises(exceptions.ExpirationError): + list(response_iterator) def testExpiredStreamRequestUnaryResponse(self): for name, test_messages_sequence in ( @@ -218,10 +218,10 @@ class FutureInvocationAsynchronousEventServiceTestCase( with self.control.pause(): response_future = self.stub.future_stream_in_value_out( name, iter(requests), _TIMEOUT) - outcome = response_future.outcome() - - self.assertIsInstance( - outcome.exception, exceptions.ExpirationError) + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() def testExpiredStreamRequestStreamResponse(self): for name, test_messages_sequence in ( @@ -229,11 +229,11 @@ class FutureInvocationAsynchronousEventServiceTestCase( for test_messages in test_messages_sequence: requests = test_messages.requests() - with self.control.pause(), self.assertRaises( - exceptions.ExpirationError): + with self.control.pause(): response_iterator = self.stub.inline_stream_in_stream_out( name, iter(requests), _TIMEOUT) - list(response_iterator) + with self.assertRaises(exceptions.ExpirationError): + list(response_iterator) def testFailedUnaryRequestUnaryResponse(self): for name, test_messages_sequence in ( @@ -244,13 +244,15 @@ class FutureInvocationAsynchronousEventServiceTestCase( with self.control.fail(): response_future = self.stub.future_value_in_value_out( name, request, _TIMEOUT) - outcome = response_future.outcome() - # Because the servicer fails outside of the thread from which the - # servicer-side runtime called into it its failure is indistinguishable - # from simply not having called its response_callback before the - # expiration of the RPC. - self.assertIsInstance(outcome.exception, exceptions.ExpirationError) + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is + # indistinguishable from simply not having called its + # response_callback before the expiration of the RPC. + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() def testFailedUnaryRequestStreamResponse(self): for name, test_messages_sequence in ( @@ -276,13 +278,15 @@ class FutureInvocationAsynchronousEventServiceTestCase( with self.control.fail(): response_future = self.stub.future_stream_in_value_out( name, iter(requests), _TIMEOUT) - outcome = response_future.outcome() - # Because the servicer fails outside of the thread from which the - # servicer-side runtime called into it its failure is indistinguishable - # from simply not having called its response_callback before the - # expiration of the RPC. - self.assertIsInstance(outcome.exception, exceptions.ExpirationError) + # Because the servicer fails outside of the thread from which the + # servicer-side runtime called into it its failure is + # indistinguishable from simply not having called its + # response_callback before the expiration of the RPC. + self.assertIsInstance( + response_future.exception(), exceptions.ExpirationError) + with self.assertRaises(exceptions.ExpirationError): + response_future.result() def testFailedStreamRequestStreamResponse(self): for name, test_messages_sequence in ( @@ -310,8 +314,8 @@ class FutureInvocationAsynchronousEventServiceTestCase( name, first_request, _TIMEOUT) second_response_future = self.stub.future_value_in_value_out( name, second_request, _TIMEOUT) - first_response = first_response_future.outcome().return_value - second_response = second_response_future.outcome().return_value + first_response = first_response_future.result() + second_response = second_response_future.result() test_messages.verify(first_request, first_response, self) test_messages.verify(second_request, second_response, self) @@ -329,10 +333,10 @@ class FutureInvocationAsynchronousEventServiceTestCase( with self.control.pause(): response_future = self.stub.future_value_in_value_out( name, request, _TIMEOUT) - cancelled = response_future.cancel() + cancel_method_return_value = response_future.cancel() - self.assertFalse(cancelled) - self.assertEqual(future.ABORTED, response_future.outcome().category) + self.assertFalse(cancel_method_return_value) + self.assertTrue(response_future.cancelled()) def testCancelledUnaryRequestStreamResponse(self): for name, test_messages_sequence in ( @@ -345,7 +349,7 @@ class FutureInvocationAsynchronousEventServiceTestCase( name, request, _TIMEOUT) response_iterator.cancel() - with self.assertRaises(exceptions.CancellationError): + with self.assertRaises(future.CancelledError): next(response_iterator) def testCancelledStreamRequestUnaryResponse(self): @@ -357,10 +361,10 @@ class FutureInvocationAsynchronousEventServiceTestCase( with self.control.pause(): response_future = self.stub.future_stream_in_value_out( name, iter(requests), _TIMEOUT) - cancelled = response_future.cancel() + cancel_method_return_value = response_future.cancel() - self.assertFalse(cancelled) - self.assertEqual(future.ABORTED, response_future.outcome().category) + self.assertFalse(cancel_method_return_value) + self.assertTrue(response_future.cancelled()) def testCancelledStreamRequestStreamResponse(self): for name, test_messages_sequence in ( @@ -373,5 +377,5 @@ class FutureInvocationAsynchronousEventServiceTestCase( name, iter(requests), _TIMEOUT) response_iterator.cancel() - with self.assertRaises(exceptions.CancellationError): + with self.assertRaises(future.CancelledError): next(response_iterator) diff --git a/src/python/src/_framework/foundation/_later_test.py b/src/python/src/_framework/foundation/_later_test.py index fbd17a4ad9..50b67907db 100644 --- a/src/python/src/_framework/foundation/_later_test.py +++ b/src/python/src/_framework/foundation/_later_test.py @@ -33,7 +33,6 @@ import threading import time import unittest -from _framework.foundation import future from _framework.foundation import later TICK = 0.1 @@ -44,10 +43,14 @@ class LaterTest(unittest.TestCase): def test_simple_delay(self): lock = threading.Lock() cell = [0] - def increment_cell(): + return_value = object() + + def computation(): with lock: cell[0] += 1 - computation_future = later.later(TICK * 2, increment_cell) + return return_value + computation_future = later.later(TICK * 2, computation) + self.assertFalse(computation_future.done()) self.assertFalse(computation_future.cancelled()) time.sleep(TICK) @@ -60,22 +63,21 @@ class LaterTest(unittest.TestCase): self.assertFalse(computation_future.cancelled()) with lock: self.assertEqual(1, cell[0]) - outcome = computation_future.outcome() - self.assertEqual(future.RETURNED, outcome.category) + self.assertEqual(return_value, computation_future.result()) def test_callback(self): lock = threading.Lock() cell = [0] callback_called = [False] - outcome_passed_to_callback = [None] - def increment_cell(): + future_passed_to_callback = [None] + def computation(): with lock: cell[0] += 1 - computation_future = later.later(TICK * 2, increment_cell) + computation_future = later.later(TICK * 2, computation) def callback(outcome): with lock: callback_called[0] = True - outcome_passed_to_callback[0] = outcome + future_passed_to_callback[0] = outcome computation_future.add_done_callback(callback) time.sleep(TICK) with lock: @@ -83,63 +85,67 @@ class LaterTest(unittest.TestCase): time.sleep(TICK * 2) with lock: self.assertTrue(callback_called[0]) - self.assertEqual(future.RETURNED, outcome_passed_to_callback[0].category) + self.assertTrue(future_passed_to_callback[0].done()) callback_called[0] = False - outcome_passed_to_callback[0] = None + future_passed_to_callback[0] = None computation_future.add_done_callback(callback) with lock: self.assertTrue(callback_called[0]) - self.assertEqual(future.RETURNED, outcome_passed_to_callback[0].category) + self.assertTrue(future_passed_to_callback[0].done()) def test_cancel(self): lock = threading.Lock() cell = [0] callback_called = [False] - outcome_passed_to_callback = [None] - def increment_cell(): + future_passed_to_callback = [None] + def computation(): with lock: cell[0] += 1 - computation_future = later.later(TICK * 2, increment_cell) + computation_future = later.later(TICK * 2, computation) def callback(outcome): with lock: callback_called[0] = True - outcome_passed_to_callback[0] = outcome + future_passed_to_callback[0] = outcome computation_future.add_done_callback(callback) time.sleep(TICK) with lock: self.assertFalse(callback_called[0]) computation_future.cancel() self.assertTrue(computation_future.cancelled()) - self.assertFalse(computation_future.done()) - self.assertEqual(future.ABORTED, computation_future.outcome().category) + self.assertFalse(computation_future.running()) + self.assertTrue(computation_future.done()) with lock: self.assertTrue(callback_called[0]) - self.assertEqual(future.ABORTED, outcome_passed_to_callback[0].category) + self.assertTrue(future_passed_to_callback[0].cancelled()) - def test_outcome(self): + def test_result(self): lock = threading.Lock() cell = [0] callback_called = [False] - outcome_passed_to_callback = [None] - def increment_cell(): + future_passed_to_callback_cell = [None] + return_value = object() + + def computation(): with lock: cell[0] += 1 - computation_future = later.later(TICK * 2, increment_cell) - def callback(outcome): + return return_value + computation_future = later.later(TICK * 2, computation) + + def callback(future_passed_to_callback): with lock: callback_called[0] = True - outcome_passed_to_callback[0] = outcome + future_passed_to_callback_cell[0] = future_passed_to_callback computation_future.add_done_callback(callback) - returned_outcome = computation_future.outcome() - self.assertEqual(future.RETURNED, returned_outcome.category) + returned_value = computation_future.result() + self.assertEqual(return_value, returned_value) # The callback may not yet have been called! Sleep a tick. time.sleep(TICK) with lock: self.assertTrue(callback_called[0]) - self.assertEqual(future.RETURNED, outcome_passed_to_callback[0].category) + self.assertEqual(return_value, future_passed_to_callback_cell[0].result()) if __name__ == '__main__': unittest.main() diff --git a/src/python/src/_framework/foundation/_timer_future.py b/src/python/src/_framework/foundation/_timer_future.py index 86bc073d56..4aa66991c5 100644 --- a/src/python/src/_framework/foundation/_timer_future.py +++ b/src/python/src/_framework/foundation/_timer_future.py @@ -29,6 +29,7 @@ """Affords a Future implementation based on Python's threading.Timer.""" +import sys import threading import time @@ -52,7 +53,9 @@ class TimerFuture(future.Future): self._computing = False self._computed = False self._cancelled = False - self._outcome = None + self._result = None + self._exception = None + self._traceback = None self._waiting = [] def _compute(self): @@ -70,19 +73,24 @@ class TimerFuture(future.Future): self._computing = True try: - returned_value = self._computation() - outcome = future.returned(returned_value) + return_value = self._computation() + exception = None + traceback = None except Exception as e: # pylint: disable=broad-except - outcome = future.raised(e) + return_value = None + exception = e + traceback = sys.exc_info()[2] with self._lock: self._computing = False self._computed = True - self._outcome = outcome + self._return_value = return_value + self._exception = exception + self._traceback = traceback waiting = self._waiting for callback in waiting: - callback(outcome) + callback(self) def start(self): """Starts this Future. @@ -104,13 +112,11 @@ class TimerFuture(future.Future): else: self._timer.cancel() self._cancelled = True - self._outcome = future.aborted() - outcome = self._outcome waiting = self._waiting for callback in waiting: try: - callback(outcome) + callback(self) except Exception: # pylint: disable=broad-except pass @@ -121,36 +127,102 @@ class TimerFuture(future.Future): with self._lock: return self._cancelled + def running(self): + """See future.Future.running for specification.""" + with self._lock: + return not self._computed and not self._cancelled + def done(self): """See future.Future.done for specification.""" with self._lock: - return self._computed + return self._computed or self._cancelled + + def result(self, timeout=None): + """See future.Future.result for specification.""" + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + if self._exception is None: + return self._return_value + else: + raise self._exception # pylint: disable=raising-bad-type + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._waiting.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + if self._exception is None: + return self._return_value + else: + raise self._exception # pylint: disable=raising-bad-type + else: + raise future.TimeoutError() + + def exception(self, timeout=None): + """See future.Future.exception for specification.""" + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._exception + + condition = threading.Condition() + def notify_condition(unused_future): + with condition: + condition.notify() + self._waiting.append(notify_condition) + + with condition: + condition.wait(timeout=timeout) + + with self._lock: + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._exception + else: + raise future.TimeoutError() - def outcome(self): - """See future.Future.outcome for specification.""" + def traceback(self, timeout=None): + """See future.Future.traceback for specification.""" with self._lock: - if self._computed or self._cancelled: - return self._outcome + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._traceback condition = threading.Condition() - def notify_condition(unused_outcome): + def notify_condition(unused_future): with condition: condition.notify() self._waiting.append(notify_condition) with condition: - condition.wait() + condition.wait(timeout=timeout) with self._lock: - return self._outcome + if self._cancelled: + raise future.CancelledError() + elif self._computed: + return self._traceback + else: + raise future.TimeoutError() - def add_done_callback(self, callback): + def add_done_callback(self, fn): """See future.Future.add_done_callback for specification.""" with self._lock: if not self._computed and not self._cancelled: - self._waiting.append(callback) + self._waiting.append(fn) return - else: - outcome = self._outcome - callback(outcome) + fn(self) diff --git a/src/python/src/_framework/foundation/callable_util.py b/src/python/src/_framework/foundation/callable_util.py index 1f7546cb76..32b0751a01 100644 --- a/src/python/src/_framework/foundation/callable_util.py +++ b/src/python/src/_framework/foundation/callable_util.py @@ -29,18 +29,47 @@ """Utilities for working with callables.""" +import abc +import collections +import enum import functools import logging -from _framework.foundation import future + +class Outcome(object): + """A sum type describing the outcome of some call. + + Attributes: + kind: One of Kind.RETURNED or Kind.RAISED respectively indicating that the + call returned a value or raised an exception. + return_value: The value returned by the call. Must be present if kind is + Kind.RETURNED. + exception: The exception raised by the call. Must be present if kind is + Kind.RAISED. + """ + __metaclass__ = abc.ABCMeta + + @enum.unique + class Kind(enum.Enum): + """Identifies the general kind of the outcome of some call.""" + + RETURNED = object() + RAISED = object() + + +class _EasyOutcome( + collections.namedtuple( + '_EasyOutcome', ['kind', 'return_value', 'exception']), + Outcome): + """A trivial implementation of Outcome.""" def _call_logging_exceptions(behavior, message, *args, **kwargs): try: - return future.returned(behavior(*args, **kwargs)) + return _EasyOutcome(Outcome.Kind.RETURNED, behavior(*args, **kwargs), None) except Exception as e: # pylint: disable=broad-except logging.exception(message) - return future.raised(e) + return _EasyOutcome(Outcome.Kind.RAISED, None, e) def with_exceptions_logged(behavior, message): @@ -72,7 +101,7 @@ def call_logging_exceptions(behavior, message, *args, **kwargs): **kwargs: Keyword arguments to pass to the given behavior. Returns: - A future.Outcome describing whether the given behavior returned a value or - raised an exception. + An Outcome describing whether the given behavior returned a value or raised + an exception. """ return _call_logging_exceptions(behavior, message, *args, **kwargs) diff --git a/src/python/src/_framework/foundation/future.py b/src/python/src/_framework/foundation/future.py index f00c503257..bfc16fc1ea 100644 --- a/src/python/src/_framework/foundation/future.py +++ b/src/python/src/_framework/foundation/future.py @@ -27,146 +27,210 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -"""The Future interface missing from Python's standard library. +"""A Future interface. -Python's concurrent.futures library defines a Future class very much like the -Future defined here, but since that class is concrete and without construction -semantics it is only available within the concurrent.futures library itself. -The Future class defined here is an entirely abstract interface that anyone may +Python doesn't have a Future interface in its standard library. In the absence +of such a standard, three separate, incompatible implementations +(concurrent.futures.Future, ndb.Future, and asyncio.Future) have appeared. This +interface attempts to be as compatible as possible with +concurrent.futures.Future. From ndb.Future it adopts a traceback-object accessor +method. + +Unlike the concrete and implemented Future classes listed above, the Future +class defined in this module is an entirely abstract interface that anyone may implement and use. + +The one known incompatibility between this interface and the interface of +concurrent.futures.Future is that this interface defines its own CancelledError +and TimeoutError exceptions rather than raising the implementation-private +concurrent.futures._base.CancelledError and the +built-in-but-only-in-3.3-and-later TimeoutError. """ import abc -import collections - -RETURNED = object() -RAISED = object() -ABORTED = object() - -class Outcome(object): - """A sum type describing the outcome of some computation. - - Attributes: - category: One of RETURNED, RAISED, or ABORTED, respectively indicating - that the computation returned a value, raised an exception, or was - aborted. - return_value: The value returned by the computation. Must be present if - category is RETURNED. - exception: The exception raised by the computation. Must be present if - category is RAISED. - """ - __metaclass__ = abc.ABCMeta +class TimeoutError(Exception): + """Indicates that a particular call timed out.""" -class _EasyOutcome( - collections.namedtuple('_EasyOutcome', - ['category', 'return_value', 'exception']), - Outcome): - """A trivial implementation of Outcome.""" -# All Outcomes describing abortion are indistinguishable so there might as well -# be only one. -_ABORTED_OUTCOME = _EasyOutcome(ABORTED, None, None) +class CancelledError(Exception): + """Indicates that the computation underlying a Future was cancelled.""" -def aborted(): - """Returns an Outcome indicating that a computation was aborted. +class Future(object): + """A representation of a computation in another control flow. - Returns: - An Outcome indicating that a computation was aborted. + Computations represented by a Future may be yet to be begun, may be ongoing, + or may have already completed. """ - return _ABORTED_OUTCOME - - -def raised(exception): - """Returns an Outcome indicating that a computation raised an exception. - - Args: - exception: The exception raised by the computation. + __metaclass__ = abc.ABCMeta - Returns: - An Outcome indicating that a computation raised the given exception. - """ - return _EasyOutcome(RAISED, None, exception) + # NOTE(nathaniel): This isn't the return type that I would want to have if it + # were up to me. Were this interface being written from scratch, the return + # type of this method would probably be a sum type like: + # + # NOT_COMMENCED + # COMMENCED_AND_NOT_COMPLETED + # PARTIAL_RESULT<Partial_Result_Type> + # COMPLETED<Result_Type> + # UNCANCELLABLE + # NOT_IMMEDIATELY_DETERMINABLE + @abc.abstractmethod + def cancel(self): + """Attempts to cancel the computation. + This method does not block. -def returned(value): - """Returns an Outcome indicating that a computation returned a value. + Returns: + True if the computation has not yet begun, will not be allowed to take + place, and determination of both was possible without blocking. False + under all other circumstances including but not limited to the + computation's already having begun, the computation's already having + finished, and the computation's having been scheduled for execution on a + remote system for which a determination of whether or not it commenced + before being cancelled cannot be made without blocking. + """ + raise NotImplementedError() - Args: - value: The value returned by the computation. + # NOTE(nathaniel): Here too this isn't the return type that I'd want this + # method to have if it were up to me. I think I'd go with another sum type + # like: + # + # NOT_CANCELLED (this object's cancel method hasn't been called) + # NOT_COMMENCED + # COMMENCED_AND_NOT_COMPLETED + # PARTIAL_RESULT<Partial_Result_Type> + # COMPLETED<Result_Type> + # UNCANCELLABLE + # NOT_IMMEDIATELY_DETERMINABLE + # + # Notice how giving the cancel method the right semantics obviates most + # reasons for this method to exist. + @abc.abstractmethod + def cancelled(self): + """Describes whether the computation was cancelled. - Returns: - An Outcome indicating that a computation returned the given value. - """ - return _EasyOutcome(RETURNED, value, None) + This method does not block. + Returns: + True if the computation was cancelled any time before its result became + immediately available. False under all other circumstances including but + not limited to this object's cancel method not having been called and + the computation's result having become immediately available. + """ + raise NotImplementedError() -class Future(object): - """A representation of a computation happening in another control flow. + @abc.abstractmethod + def running(self): + """Describes whether the computation is taking place. - Computations represented by a Future may have already completed, may be - ongoing, or may be yet to be begun. + This method does not block. - Computations represented by a Future are considered uninterruptable; once - started they will be allowed to terminate either by returning or raising - an exception. - """ - __metaclass__ = abc.ABCMeta + Returns: + True if the computation is scheduled to take place in the future or is + taking place now, or False if the computation took place in the past or + was cancelled. + """ + raise NotImplementedError() + # NOTE(nathaniel): These aren't quite the semantics I'd like here either. I + # would rather this only returned True in cases in which the underlying + # computation completed successfully. A computation's having been cancelled + # conflicts with considering that computation "done". @abc.abstractmethod - def cancel(self): - """Attempts to cancel the computation. + def done(self): + """Describes whether the computation has taken place. + + This method does not block. Returns: - True if the computation will not be allowed to take place or False if - the computation has already taken place or is currently taking place. + True if the computation is known to have either completed or have been + unscheduled or interrupted. False if the computation may possibly be + executing or scheduled to execute later. """ raise NotImplementedError() @abc.abstractmethod - def cancelled(self): - """Describes whether the computation was cancelled. + def result(self, timeout=None): + """Accesses the outcome of the computation or raises its exception. + + This method may return immediately or may block. + + Args: + timeout: The length of time in seconds to wait for the computation to + finish or be cancelled, or None if this method should block until the + computation has finished or is cancelled no matter how long that takes. Returns: - True if the computation was cancelled and did not take place or False - if the computation took place, is taking place, or is scheduled to - take place in the future. + The return value of the computation. + + Raises: + TimeoutError: If a timeout value is passed and the computation does not + terminate within the allotted time. + CancelledError: If the computation was cancelled. + Exception: If the computation raised an exception, this call will raise + the same exception. """ raise NotImplementedError() @abc.abstractmethod - def done(self): - """Describes whether the computation has taken place. + def exception(self, timeout=None): + """Return the exception raised by the computation. + + This method may return immediately or may block. + + Args: + timeout: The length of time in seconds to wait for the computation to + terminate or be cancelled, or None if this method should block until + the computation is terminated or is cancelled no matter how long that + takes. Returns: - True if the computation took place; False otherwise. + The exception raised by the computation, or None if the computation did + not raise an exception. + + Raises: + TimeoutError: If a timeout value is passed and the computation does not + terminate within the allotted time. + CancelledError: If the computation was cancelled. """ raise NotImplementedError() @abc.abstractmethod - def outcome(self): - """Accesses the outcome of the computation. + def traceback(self, timeout=None): + """Access the traceback of the exception raised by the computation. - If the computation has not yet completed, this method blocks until it has. + This method may return immediately or may block. + + Args: + timeout: The length of time in seconds to wait for the computation to + terminate or be cancelled, or None if this method should block until + the computation is terminated or is cancelled no matter how long that + takes. Returns: - An Outcome describing the outcome of the computation. + The traceback of the exception raised by the computation, or None if the + computation did not raise an exception. + + Raises: + TimeoutError: If a timeout value is passed and the computation does not + terminate within the allotted time. + CancelledError: If the computation was cancelled. """ raise NotImplementedError() @abc.abstractmethod - def add_done_callback(self, callback): + def add_done_callback(self, fn): """Adds a function to be called at completion of the computation. - The callback will be passed an Outcome object describing the outcome of + The callback will be passed this Future object describing the outcome of the computation. If the computation has already completed, the callback will be called immediately. Args: - callback: A callable taking an Outcome as its single parameter. + fn: A callable taking a this Future object as its single parameter. """ raise NotImplementedError() diff --git a/src/python/src/grpc_early_adopter/__init__.py b/src/python/src/grpc_early_adopter/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/src/python/src/grpc_early_adopter/__init__.py diff --git a/src/python/src/grpc_early_adopter/_face_utilities.py b/src/python/src/grpc_early_adopter/_face_utilities.py new file mode 100644 index 0000000000..8b10be729b --- /dev/null +++ b/src/python/src/grpc_early_adopter/_face_utilities.py @@ -0,0 +1,143 @@ +# Copyright 2015, 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. + +import abc +import collections + +from _framework.face import interfaces as face_interfaces + +from grpc_early_adopter import interfaces + + +class _InlineUnaryUnaryMethod(face_interfaces.InlineValueInValueOutMethod): + + def __init__(self, unary_unary_rpc_method): + self._method = unary_unary_rpc_method + + def service(self, request, context): + """See face_interfaces.InlineValueInValueOutMethod.service for spec.""" + return self._method.service_unary_unary(request) + + +class _InlineUnaryStreamMethod(face_interfaces.InlineValueInStreamOutMethod): + + def __init__(self, unary_stream_rpc_method): + self._method = unary_stream_rpc_method + + def service(self, request, context): + """See face_interfaces.InlineValueInStreamOutMethod.service for spec.""" + return self._method.service_unary_stream(request) + + +class _InlineStreamUnaryMethod(face_interfaces.InlineStreamInValueOutMethod): + + def __init__(self, stream_unary_rpc_method): + self._method = stream_unary_rpc_method + + def service(self, request_iterator, context): + """See face_interfaces.InlineStreamInValueOutMethod.service for spec.""" + return self._method.service_stream_unary(request_iterator) + + +class _InlineStreamStreamMethod(face_interfaces.InlineStreamInStreamOutMethod): + + def __init__(self, stream_stream_rpc_method): + self._method = stream_stream_rpc_method + + def service(self, request_iterator, context): + """See face_interfaces.InlineStreamInStreamOutMethod.service for spec.""" + return self._method.service_stream_stream(request_iterator) + + +class Breakdown(object): + """An intermediate representation of implementations of RPC methods. + + Attributes: + unary_unary_methods: + unary_stream_methods: + stream_unary_methods: + stream_stream_methods: + request_serializers: + request_deserializers: + response_serializers: + response_deserializers: + """ + __metaclass__ = abc.ABCMeta + + + +class _EasyBreakdown( + Breakdown, + collections.namedtuple( + '_EasyBreakdown', + ['unary_unary_methods', 'unary_stream_methods', 'stream_unary_methods', + 'stream_stream_methods', 'request_serializers', + 'request_deserializers', 'response_serializers', + 'response_deserializers'])): + pass + + +def break_down(methods): + """Breaks down RPC methods. + + Args: + methods: A dictionary from RPC mthod name to + interfaces.RpcMethod object describing the RPCs. + + Returns: + A Breakdown corresponding to the given methods. + """ + unary_unary = {} + unary_stream = {} + stream_unary = {} + stream_stream = {} + request_serializers = {} + request_deserializers = {} + response_serializers = {} + response_deserializers = {} + + for name, method in methods.iteritems(): + cardinality = method.cardinality() + if cardinality is interfaces.Cardinality.UNARY_UNARY: + unary_unary[name] = _InlineUnaryUnaryMethod(method) + elif cardinality is interfaces.Cardinality.UNARY_STREAM: + unary_stream[name] = _InlineUnaryStreamMethod(method) + elif cardinality is interfaces.Cardinality.STREAM_UNARY: + stream_unary[name] = _InlineStreamUnaryMethod(method) + elif cardinality is interfaces.Cardinality.STREAM_STREAM: + stream_stream[name] = _InlineStreamStreamMethod(method) + request_serializers[name] = method.serialize_request + request_deserializers[name] = method.deserialize_request + response_serializers[name] = method.serialize_response + response_deserializers[name] = method.deserialize_response + + return _EasyBreakdown( + unary_unary, unary_stream, stream_unary, stream_stream, + request_serializers, request_deserializers, response_serializers, + response_deserializers) diff --git a/src/python/src/grpc_early_adopter/implementations.py b/src/python/src/grpc_early_adopter/implementations.py new file mode 100644 index 0000000000..8a2f7fde61 --- /dev/null +++ b/src/python/src/grpc_early_adopter/implementations.py @@ -0,0 +1,129 @@ +# Copyright 2015, 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. + +"""Entry points into GRPC.""" + +import threading + +from _adapter import fore +from _framework.base.packets import implementations as _tickets_implementations +from _framework.face import implementations as _face_implementations +from _framework.foundation import logging_pool +from grpc_early_adopter import _face_utilities +from grpc_early_adopter import interfaces + +_MEGA_TIMEOUT = 60 * 60 * 24 +_THREAD_POOL_SIZE = 80 + + +class _Server(interfaces.Server): + + def __init__(self, breakdown, port, private_key, certificate_chain): + self._lock = threading.Lock() + self._breakdown = breakdown + self._port = port + self._private_key = private_key + self._certificate_chain = certificate_chain + + self._pool = None + self._fore_link = None + self._back = None + + def start(self): + """See interfaces.Server.start for specification.""" + with self._lock: + if self._pool is None: + self._pool = logging_pool.pool(_THREAD_POOL_SIZE) + servicer = _face_implementations.servicer( + self._pool, + inline_value_in_value_out_methods=self._breakdown.unary_unary_methods, + inline_value_in_stream_out_methods=self._breakdown.unary_stream_methods, + inline_stream_in_value_out_methods=self._breakdown.stream_unary_methods, + inline_stream_in_stream_out_methods=self._breakdown.stream_stream_methods) + self._fore_link = fore.ForeLink( + self._pool, self._breakdown.request_deserializers, + self._breakdown.response_serializers, None, + ((self._private_key, self._certificate_chain),), port=self._port) + port = self._fore_link.start() + self._back = _tickets_implementations.back( + servicer, self._pool, self._pool, self._pool, _MEGA_TIMEOUT, + _MEGA_TIMEOUT) + self._fore_link.join_rear_link(self._back) + self._back.join_fore_link(self._fore_link) + return port + else: + raise ValueError('Server currently running!') + + def stop(self): + """See interfaces.Server.stop for specification.""" + with self._lock: + if self._pool is None: + raise ValueError('Server not running!') + else: + self._fore_link.stop() + self._pool.shutdown(wait=True) + self._pool = None + + +def _build_server(methods, port, private_key, certificate_chain): + breakdown = _face_utilities.break_down(methods) + return _Server(breakdown, port, private_key, certificate_chain) + + +def insecure_server(methods, port): + """Constructs an insecure interfaces.Server. + + Args: + methods: A dictionary from RPC method name to + interfaces.RpcMethod object describing the RPCs to be + serviced by the created server. + port: The port on which to serve. + + Returns: + An interfaces.Server that will run with no security and + service unsecured raw requests. + """ + return _build_server(methods, port, None, None) + + +def secure_server(methods, port, private_key, certificate_chain): + """Constructs a secure interfaces.Server. + + Args: + methods: A dictionary from RPC method name to + interfaces.RpcMethod object describing the RPCs to be + serviced by the created server. + port: The port on which to serve. + private_key: A pem-encoded private key. + certificate_chain: A pem-encoded certificate chain. + + Returns: + An interfaces.Server that will serve secure traffic. + """ + return _build_server(methods, port, private_key, certificate_chain) diff --git a/src/python/src/grpc_early_adopter/interfaces.py b/src/python/src/grpc_early_adopter/interfaces.py new file mode 100644 index 0000000000..c2806c235c --- /dev/null +++ b/src/python/src/grpc_early_adopter/interfaces.py @@ -0,0 +1,194 @@ +# Copyright 2015, 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. + +"""Interfaces of GRPC.""" + +import abc +import enum + + +@enum.unique +class Cardinality(enum.Enum): + """Constants for the four cardinalities of RPC.""" + + UNARY_UNARY = 'request-unary/response-unary' + UNARY_STREAM = 'request-unary/response-streaming' + STREAM_UNARY = 'request-streaming/response-unary' + STREAM_STREAM = 'request-streaming/response-streaming' + + +class RpcMethod(object): + """A sum type for the implementation of an RPC method.""" + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def cardinality(self): + """Identifies the cardinality of this RpcMethod. + + Returns: + A Cardinality value identifying whether or not this + RpcMethod is request-unary or request-streaming and + whether or not it is response-unary or + response-streaming. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_request(self, request): + """Serializes a request value. + + Args: + request: A request value appropriate for this RpcMethod. + + Returns: + The serialization of the given request value as a + bytestring. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_request(self, serialized_request): + """Deserializes a request value. + + Args: + serialized_request: A bytestring that is the + serialization of a request value appropriate for this + RpcMethod. + + Returns: + A request value corresponding to the given bytestring. + """ + raise NotImplementedError() + + @abc.abstractmethod + def serialize_response(self, response): + """Serializes a response value. + + Args: + response: A response value appropriate for this RpcMethod. + + Returns: + The serialization of the given response value as a + bytestring. + """ + raise NotImplementedError() + + @abc.abstractmethod + def deserialize_response(self, serialized_response): + """Deserializes a response value. + + Args: + serialized_response: A bytestring that is the + serialization of a response value appropriate for this + RpcMethod. + + Returns: + A response value corresponding to the given bytestring. + """ + raise NotImplementedError() + + @abc.abstractmethod + def service_unary_unary(self, request): + """Carries out this RPC. + + This method may only be called if the cardinality of this + RpcMethod is Cardinality.UNARY_UNARY. + + Args: + request: A request value appropriate for this RpcMethod. + + Returns: + A response value appropriate for this RpcMethod. + """ + raise NotImplementedError() + + @abc.abstractmethod + def service_unary_stream(self, request): + """Carries out this RPC. + + This method may only be called if the cardinality of this + RpcMethod is Cardinality.UNARY_STREAM. + + Args: + request: A request value appropriate for this RpcMethod. + + Yields: + Zero or more response values appropriate for this + RpcMethod. + """ + raise NotImplementedError() + + @abc.abstractmethod + def service_stream_unary(self, request_iterator): + """Carries out this RPC. + + This method may only be called if the cardinality of this + RpcMethod is Cardinality.STREAM_UNARY. + + Args: + request_iterator: An iterator of request values + appropriate for this RpcMethod. + + Returns: + A response value appropriate for this RpcMethod. + """ + raise NotImplementedError() + + @abc.abstractmethod + def service_stream_stream(self, request_iterator): + """Carries out this RPC. + + This method may only be called if the cardinality of this + RpcMethod is Cardinality.STREAM_STREAM. + + Args: + request_iterator: An iterator of request values + appropriate for this RpcMethod. + + Yields: + Zero or more response values appropraite for this + RpcMethod. + """ + raise NotImplementedError() + + +class Server(object): + """A GRPC Server.""" + __metaclass__ = abc.ABCMeta + + + @abc.abstractmethod + def start(self): + """Instructs this server to commence service of RPCs.""" + raise NotImplementedError() + + @abc.abstractmethod + def stop(self): + """Instructs this server to halt service of RPCs.""" + raise NotImplementedError() diff --git a/src/python/src/grpc_early_adopter/utilities.py b/src/python/src/grpc_early_adopter/utilities.py new file mode 100644 index 0000000000..333ed3a9db --- /dev/null +++ b/src/python/src/grpc_early_adopter/utilities.py @@ -0,0 +1,213 @@ +# Copyright 2015, 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. + +"""Utilities for use with GRPC.""" + +from grpc_early_adopter import interfaces + + +class _RpcMethod(interfaces.RpcMethod): + + def __init__( + self, cardinality, unary_unary, unary_stream, stream_unary, + stream_stream, request_serializer, request_deserializer, + response_serializer, response_deserializer): + self._cardinality = cardinality + self._unary_unary = unary_unary + self._unary_stream = unary_stream + self._stream_unary = stream_unary + self._stream_stream = stream_stream + self._request_serializer = request_serializer + self._request_deserializer = request_deserializer + self._response_serializer = response_serializer + self._response_deserializer = response_deserializer + + def cardinality(self): + """See interfaces.RpcMethod.cardinality for specification.""" + return self._cardinality + + def serialize_request(self, request): + """See interfaces.RpcMethod.serialize_request for specification.""" + return self._request_serializer(request) + + def deserialize_request(self, serialized_request): + """See interfaces.RpcMethod.deserialize_request for specification.""" + return self._request_deserializer(serialized_request) + + def serialize_response(self, response): + """See interfaces.RpcMethod.serialize_response for specification.""" + return self._response_serializer(response) + + def deserialize_response(self, serialized_response): + """See interfaces.RpcMethod.deserialize_response for specification.""" + return self._response_deserializer(serialized_response) + + def service_unary_unary(self, request): + """See interfaces.RpcMethod.service_unary_unary for specification.""" + return self._unary_unary(request) + + def service_unary_stream(self, request): + """See interfaces.RpcMethod.service_unary_stream for specification.""" + return self._unary_stream(request) + + def service_stream_unary(self, request_iterator): + """See interfaces.RpcMethod.service_stream_unary for specification.""" + return self._stream_unary(request_iterator) + + def service_stream_stream(self, request_iterator): + """See interfaces.RpcMethod.service_stream_stream for specification.""" + return self._stream_stream(request_iterator) + + +def unary_unary_rpc_method( + behavior, request_serializer, request_deserializer, response_serializer, + response_deserializer): + """Constructs an interfaces.RpcMethod for the given behavior. + + Args: + behavior: A callable that implements a unary-unary RPC + method that accepts a single request and returns a single + response. + request_serializer: A callable that when called on a request + value returns a bytestring corresponding to that value. + request_deserializer: A callable that when called on a + bytestring returns the request value corresponding to that + bytestring. + response_serializer: A callable that when called on a + response value returns the bytestring corresponding to + that value. + response_deserializer: A callable that when called on a + bytestring returns the response value corresponding to + that bytestring. + + Returns: + An interfaces.RpcMethod constructed from the given + arguments representing a unary-request/unary-response RPC + method. + """ + return _RpcMethod( + interfaces.Cardinality.UNARY_UNARY, behavior, None, None, None, + request_serializer, request_deserializer, response_serializer, + response_deserializer) + + +def unary_stream_rpc_method( + behavior, request_serializer, request_deserializer, response_serializer, + response_deserializer): + """Constructs an interfaces.RpcMethod for the given behavior. + + Args: + behavior: A callable that implements a unary-stream RPC + method that accepts a single request and returns an + iterator of zero or more responses. + request_serializer: A callable that when called on a request + value returns a bytestring corresponding to that value. + request_deserializer: A callable that when called on a + bytestring returns the request value corresponding to that + bytestring. + response_serializer: A callable that when called on a + response value returns the bytestring corresponding to + that value. + response_deserializer: A callable that when called on a + bytestring returns the response value corresponding to + that bytestring. + + Returns: + An interfaces.RpcMethod constructed from the given + arguments representing a unary-request/streaming-response + RPC method. + """ + return _RpcMethod( + interfaces.Cardinality.UNARY_STREAM, None, behavior, None, None, + request_serializer, request_deserializer, response_serializer, + response_deserializer) + + +def stream_unary_rpc_method( + behavior, request_serializer, request_deserializer, response_serializer, + response_deserializer): + """Constructs an interfaces.RpcMethod for the given behavior. + + Args: + behavior: A callable that implements a stream-unary RPC + method that accepts an iterator of zero or more requests + and returns a single response. + request_serializer: A callable that when called on a request + value returns a bytestring corresponding to that value. + request_deserializer: A callable that when called on a + bytestring returns the request value corresponding to that + bytestring. + response_serializer: A callable that when called on a + response value returns the bytestring corresponding to + that value. + response_deserializer: A callable that when called on a + bytestring returns the response value corresponding to + that bytestring. + + Returns: + An interfaces.RpcMethod constructed from the given + arguments representing a streaming-request/unary-response + RPC method. + """ + return _RpcMethod( + interfaces.Cardinality.STREAM_UNARY, None, None, behavior, None, + request_serializer, request_deserializer, response_serializer, + response_deserializer) + + +def stream_stream_rpc_method( + behavior, request_serializer, request_deserializer, response_serializer, + response_deserializer): + """Constructs an interfaces.RpcMethod for the given behavior. + + Args: + behavior: A callable that implements a stream-stream RPC + method that accepts an iterator of zero or more requests + and returns an iterator of zero or more responses. + request_serializer: A callable that when called on a request + value returns a bytestring corresponding to that value. + request_deserializer: A callable that when called on a + bytestring returns the request value corresponding to that + bytestring. + response_serializer: A callable that when called on a + response value returns the bytestring corresponding to + that value. + response_deserializer: A callable that when called on a + bytestring returns the response value corresponding to + that bytestring. + + Returns: + An interfaces.RpcMethod constructed from the given + arguments representing a + streaming-request/streaming-response RPC method. + """ + return _RpcMethod( + interfaces.Cardinality.STREAM_STREAM, None, None, None, behavior, + request_serializer, request_deserializer, response_serializer, + response_deserializer) diff --git a/src/python/setup.py b/src/python/src/setup.py index 5e566bad4f..93af4d68ca 100644 --- a/src/python/setup.py +++ b/src/python/src/setup.py @@ -32,19 +32,17 @@ from distutils import core as _core _EXTENSION_SOURCES = ( - 'src/_adapter/_c.c', - 'src/_adapter/_call.c', - 'src/_adapter/_channel.c', - 'src/_adapter/_completion_queue.c', - 'src/_adapter/_error.c', - 'src/_adapter/_server.c', - 'src/_adapter/_server_credentials.c', + '_adapter/_c.c', + '_adapter/_call.c', + '_adapter/_channel.c', + '_adapter/_completion_queue.c', + '_adapter/_error.c', + '_adapter/_server.c', + '_adapter/_server_credentials.c', ) _EXTENSION_INCLUDE_DIRECTORIES = ( - 'src', - # TODO(nathaniel): Can this path specification be made to work? - #'../../include', + '.', ) _EXTENSION_LIBRARIES = ( @@ -52,16 +50,11 @@ _EXTENSION_LIBRARIES = ( 'grpc', ) -_EXTENSION_LIBRARY_DIRECTORIES = ( - # TODO(nathaniel): Can this path specification be made to work? - #'../../libs/dbg', -) - _EXTENSION_MODULE = _core.Extension( '_adapter._c', sources=list(_EXTENSION_SOURCES), include_dirs=_EXTENSION_INCLUDE_DIRECTORIES, libraries=_EXTENSION_LIBRARIES, - library_dirs=_EXTENSION_LIBRARY_DIRECTORIES) + ) _PACKAGES=( '_adapter', @@ -73,15 +66,17 @@ _PACKAGES=( '_framework.face.testing', '_framework.foundation', '_junkdrawer', + 'grpc_early_adopter', ) _PACKAGE_DIRECTORIES = { - '_adapter': 'src/_adapter', - '_framework': 'src/_framework', - '_junkdrawer': 'src/_junkdrawer', + '_adapter': '_adapter', + '_framework': '_framework', + '_junkdrawer': '_junkdrawer', + 'grpc_early_adopter': 'grpc_early_adopter', } _core.setup( - name='grpc', version='0.0.1', + name='grpc-2015', version='0.0.1', ext_modules=[_EXTENSION_MODULE], packages=_PACKAGES, package_dir=_PACKAGE_DIRECTORIES) diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb index f5acae896a..1321727f5c 100644 --- a/src/ruby/spec/client_server_spec.rb +++ b/src/ruby/spec/client_server_spec.rb @@ -362,9 +362,11 @@ describe 'the secure http client/server' do @server.close end - it_behaves_like 'basic GRPC message delivery is OK' do - end + # TODO: uncomment after updating the to the new c api + # it_behaves_like 'basic GRPC message delivery is OK' do + # end - it_behaves_like 'GRPC metadata delivery works OK' do - end + # TODO: uncomment after updating the to the new c api + # it_behaves_like 'GRPC metadata delivery works OK' do + # end end |