diff options
author | Craig Tiller <craig.tiller@gmail.com> | 2015-02-11 18:19:24 -0800 |
---|---|---|
committer | Craig Tiller <craig.tiller@gmail.com> | 2015-02-11 18:19:24 -0800 |
commit | d9dd8fee0485ee3b3c2c5caa1df39b7c97330ac2 (patch) | |
tree | d4bcdc37bbed36b9cdce6d507292add4c72ab4d5 /src | |
parent | bd217574fb7ec65f899103eb4e1f8719b83fa43a (diff) | |
parent | d8b88dec9182705356af8a862445c28c41ab35c8 (diff) |
Merge github.com:grpc/grpc into c++api
Diffstat (limited to 'src')
25 files changed, 782 insertions, 186 deletions
diff --git a/src/core/httpcli/httpcli_security_context.c b/src/core/httpcli/httpcli_security_context.c index d074e163f1..53e887ccd1 100644 --- a/src/core/httpcli/httpcli_security_context.c +++ b/src/core/httpcli/httpcli_security_context.c @@ -73,20 +73,23 @@ static grpc_security_status httpcli_ssl_create_handshaker( return GRPC_SECURITY_OK; } -static grpc_security_status httpcli_ssl_check_peer( - grpc_security_context *ctx, const tsi_peer *peer, - grpc_security_check_peer_cb cb, void *user_data) { +static grpc_security_status httpcli_ssl_check_peer(grpc_security_context *ctx, + tsi_peer peer, + grpc_security_check_cb cb, + void *user_data) { grpc_httpcli_ssl_channel_security_context *c = (grpc_httpcli_ssl_channel_security_context *)ctx; + grpc_security_status status = GRPC_SECURITY_OK; /* Check the peer name. */ if (c->secure_peer_name != NULL && - !tsi_ssl_peer_matches_name(peer, c->secure_peer_name)) { + !tsi_ssl_peer_matches_name(&peer, c->secure_peer_name)) { gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate", c->secure_peer_name); - return GRPC_SECURITY_ERROR; + status = GRPC_SECURITY_ERROR; } - return GRPC_SECURITY_OK; + tsi_peer_destruct(&peer); + return status; } static grpc_security_context_vtable httpcli_ssl_vtable = { diff --git a/src/core/iomgr/pollset_multipoller_with_epoll.c b/src/core/iomgr/pollset_multipoller_with_epoll.c new file mode 100644 index 0000000000..9fb2819506 --- /dev/null +++ b/src/core/iomgr/pollset_multipoller_with_epoll.c @@ -0,0 +1,197 @@ +/* + * + * 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> + +#ifdef GPR_LINUX_MULTIPOLL_WITH_EPOLL + +#include <errno.h> +#include <string.h> +#include <sys/epoll.h> +#include <unistd.h> + +#include "src/core/iomgr/fd_posix.h" +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> + +typedef struct { + int epoll_fd; + grpc_wakeup_fd_info wakeup_fd; +} pollset_hdr; + +static void multipoll_with_epoll_pollset_add_fd(grpc_pollset *pollset, + grpc_fd *fd) { + pollset_hdr *h = pollset->data.ptr; + struct epoll_event ev; + int err; + + ev.events = EPOLLIN | EPOLLOUT | EPOLLET; + ev.data.ptr = fd; + err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD, fd->fd, &ev); + if (err < 0) { + /* FDs may be added to a pollset multiple times, so EEXIST is normal. */ + if (errno != EEXIST) { + gpr_log(GPR_ERROR, "epoll_ctl add for %d failed: %s", fd->fd, + strerror(errno)); + } + } +} + +static void multipoll_with_epoll_pollset_del_fd(grpc_pollset *pollset, + grpc_fd *fd) { + pollset_hdr *h = pollset->data.ptr; + int err; + /* Note that this can race with concurrent poll, but that should be fine since + * at worst it creates a spurious read event on a reused grpc_fd object. */ + err = epoll_ctl(h->epoll_fd, EPOLL_CTL_DEL, fd->fd, NULL); + if (err < 0) { + gpr_log(GPR_ERROR, "epoll_ctl del for %d failed: %s", fd->fd, + strerror(errno)); + } +} + +/* TODO(klempner): We probably want to turn this down a bit */ +#define GRPC_EPOLL_MAX_EVENTS 1000 + +static int multipoll_with_epoll_pollset_maybe_work( + grpc_pollset *pollset, gpr_timespec deadline, gpr_timespec now, + int allow_synchronous_callback) { + struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS]; + int ep_rv; + pollset_hdr *h = pollset->data.ptr; + int timeout_ms; + + /* If you want to ignore epoll's ability to sanely handle parallel pollers, + * for a more apples-to-apples performance comparison with poll, add a + * if (pollset->counter == 0) { return 0 } + * here. + */ + + if (gpr_time_cmp(deadline, gpr_inf_future) == 0) { + timeout_ms = -1; + } else { + timeout_ms = gpr_time_to_millis(gpr_time_sub(deadline, now)); + if (timeout_ms <= 0) { + return 1; + } + } + pollset->counter += 1; + gpr_mu_unlock(&pollset->mu); + + do { + ep_rv = epoll_wait(h->epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms); + if (ep_rv < 0) { + if (errno != EINTR) { + gpr_log(GPR_ERROR, "epoll_wait() failed: %s", strerror(errno)); + } + } else { + int i; + for (i = 0; i < ep_rv; ++i) { + if (ep_ev[i].data.ptr == 0) { + grpc_wakeup_fd_consume_wakeup(&h->wakeup_fd); + } else { + grpc_fd *fd = ep_ev[i].data.ptr; + /* TODO(klempner): We might want to consider making err and pri + * separate events */ + int cancel = ep_ev[i].events & (EPOLLERR | EPOLLHUP); + int read = ep_ev[i].events & (EPOLLIN | EPOLLPRI); + int write = ep_ev[i].events & EPOLLOUT; + if (read || cancel) { + grpc_fd_become_readable(fd, allow_synchronous_callback); + } + if (write || cancel) { + grpc_fd_become_writable(fd, allow_synchronous_callback); + } + } + } + } + timeout_ms = 0; + } while (ep_rv == GRPC_EPOLL_MAX_EVENTS); + + gpr_mu_lock(&pollset->mu); + pollset->counter -= 1; + /* TODO(klempner): This should signal once per event rather than broadcast, + * although it probably doesn't matter because threads will generally be + * blocked in epoll_wait rather than being blocked on the cv. */ + gpr_cv_broadcast(&pollset->cv); + return 1; +} + +static void multipoll_with_epoll_pollset_destroy(grpc_pollset *pollset) { + pollset_hdr *h = pollset->data.ptr; + grpc_wakeup_fd_destroy(&h->wakeup_fd); + close(h->epoll_fd); + gpr_free(h); +} + +static void epoll_kick(grpc_pollset *pollset) { + pollset_hdr *h = pollset->data.ptr; + grpc_wakeup_fd_wakeup(&h->wakeup_fd); +} + +static const grpc_pollset_vtable multipoll_with_epoll_pollset = { + multipoll_with_epoll_pollset_add_fd, multipoll_with_epoll_pollset_del_fd, + multipoll_with_epoll_pollset_maybe_work, epoll_kick, + multipoll_with_epoll_pollset_destroy}; + +void grpc_platform_become_multipoller(grpc_pollset *pollset, grpc_fd **fds, + size_t nfds) { + size_t i; + pollset_hdr *h = gpr_malloc(sizeof(pollset_hdr)); + struct epoll_event ev; + int err; + + pollset->vtable = &multipoll_with_epoll_pollset; + pollset->data.ptr = h; + h->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + if (h->epoll_fd < 0) { + /* TODO(klempner): Fall back to poll here, especially on ENOSYS */ + gpr_log(GPR_ERROR, "epoll_create1 failed: %s", strerror(errno)); + abort(); + } + for (i = 0; i < nfds; i++) { + multipoll_with_epoll_pollset_add_fd(pollset, fds[i]); + } + + grpc_wakeup_fd_create(&h->wakeup_fd); + ev.events = EPOLLIN; + ev.data.ptr = 0; + err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD, + GRPC_WAKEUP_FD_GET_READ_FD(&h->wakeup_fd), &ev); + if (err < 0) { + gpr_log(GPR_ERROR, "Wakeup fd epoll_ctl failed: %s", strerror(errno)); + abort(); + } +} + +#endif /* GPR_LINUX_MULTIPOLL_WITH_EPOLL */ diff --git a/src/core/iomgr/pollset_multipoller_with_poll_posix.c b/src/core/iomgr/pollset_multipoller_with_poll_posix.c index 3244ae08db..c136ee0b52 100644 --- a/src/core/iomgr/pollset_multipoller_with_poll_posix.c +++ b/src/core/iomgr/pollset_multipoller_with_poll_posix.c @@ -200,6 +200,10 @@ static int multipoll_with_poll_pollset_maybe_work( return 1; } +static void multipoll_with_poll_pollset_kick(grpc_pollset *p) { + grpc_pollset_kick_kick(&p->kick_state); +} + static void multipoll_with_poll_pollset_destroy(grpc_pollset *pollset) { size_t i; pollset_hdr *h = pollset->data.ptr; @@ -219,7 +223,7 @@ static void multipoll_with_poll_pollset_destroy(grpc_pollset *pollset) { static const grpc_pollset_vtable multipoll_with_poll_pollset = { multipoll_with_poll_pollset_add_fd, multipoll_with_poll_pollset_del_fd, - multipoll_with_poll_pollset_maybe_work, + multipoll_with_poll_pollset_maybe_work, multipoll_with_poll_pollset_kick, multipoll_with_poll_pollset_destroy}; void grpc_platform_become_multipoller(grpc_pollset *pollset, grpc_fd **fds, diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c index 2837a0dff3..53c9806fb9 100644 --- a/src/core/iomgr/pollset_posix.c +++ b/src/core/iomgr/pollset_posix.c @@ -76,7 +76,7 @@ static void backup_poller(void *p) { void grpc_pollset_kick(grpc_pollset *p) { if (p->counter) { - grpc_pollset_kick_kick(&p->kick_state); + p->vtable->kick(p); } } @@ -84,6 +84,10 @@ void grpc_pollset_force_kick(grpc_pollset *p) { grpc_pollset_kick_kick(&p->kick_state); } +static void kick_using_pollset_kick(grpc_pollset *p) { + grpc_pollset_kick_kick(&p->kick_state); +} + /* global state management */ grpc_pollset *grpc_backup_pollset(void) { return &g_backup_pollset; } @@ -186,7 +190,7 @@ static void empty_pollset_destroy(grpc_pollset *pollset) {} static const grpc_pollset_vtable empty_pollset = { empty_pollset_add_fd, empty_pollset_del_fd, empty_pollset_maybe_work, - empty_pollset_destroy}; + kick_using_pollset_kick, empty_pollset_destroy}; static void become_empty_pollset(grpc_pollset *pollset) { pollset->vtable = &empty_pollset; @@ -296,7 +300,8 @@ static void unary_poll_pollset_destroy(grpc_pollset *pollset) { static const grpc_pollset_vtable unary_poll_pollset = { unary_poll_pollset_add_fd, unary_poll_pollset_del_fd, - unary_poll_pollset_maybe_work, unary_poll_pollset_destroy}; + unary_poll_pollset_maybe_work, kick_using_pollset_kick, + unary_poll_pollset_destroy}; static void become_unary_pollset(grpc_pollset *pollset, grpc_fd *fd) { pollset->vtable = &unary_poll_pollset; diff --git a/src/core/iomgr/pollset_posix.h b/src/core/iomgr/pollset_posix.h index cdcb995167..b1a82fccfe 100644 --- a/src/core/iomgr/pollset_posix.h +++ b/src/core/iomgr/pollset_posix.h @@ -66,6 +66,7 @@ struct grpc_pollset_vtable { void (*del_fd)(grpc_pollset *pollset, struct grpc_fd *fd); int (*maybe_work)(grpc_pollset *pollset, gpr_timespec deadline, gpr_timespec now, int allow_synchronous_callback); + void (*kick)(grpc_pollset *pollset); void (*destroy)(grpc_pollset *pollset); }; diff --git a/src/core/security/auth.c b/src/core/security/auth.c index 9d0c075bc3..18c32f90f4 100644 --- a/src/core/security/auth.c +++ b/src/core/security/auth.c @@ -35,22 +35,49 @@ #include <string.h> -#include "src/core/security/security_context.h" -#include "src/core/security/credentials.h" #include <grpc/support/alloc.h> #include <grpc/support/log.h> +#include "src/core/support/string.h" +#include "src/core/channel/channel_stack.h" +#include "src/core/security/security_context.h" +#include "src/core/security/credentials.h" +#include "src/core/surface/call.h" + /* We can have a per-call credentials. */ typedef struct { grpc_credentials *creds; + grpc_mdstr *host; grpc_call_op op; } call_data; /* We can have a per-channel credentials. */ typedef struct { grpc_channel_security_context *security_context; + grpc_mdctx *md_ctx; + grpc_mdstr *authority_string; + grpc_mdstr *error_msg_key; } channel_data; +static void do_nothing(void *ignored, grpc_op_error error) {} + +static void bubbleup_error(grpc_call_element *elem, const char *error_msg) { + grpc_call_op finish_op; + channel_data *channeld = elem->channel_data; + + gpr_log(GPR_ERROR, "%s", error_msg); + finish_op.type = GRPC_RECV_METADATA; + finish_op.dir = GRPC_CALL_UP; + finish_op.flags = 0; + finish_op.data.metadata = grpc_mdelem_from_metadata_strings( + channeld->md_ctx, channeld->error_msg_key, + grpc_mdstr_from_string(channeld->md_ctx, error_msg)); + finish_op.done_cb = do_nothing; + finish_op.user_data = NULL; + grpc_call_next_op(elem, &finish_op); + grpc_call_element_send_cancel(elem); +} + static void on_credentials_metadata(void *user_data, grpc_mdelem **md_elems, size_t num_md, grpc_credentials_status status) { @@ -62,6 +89,46 @@ static void on_credentials_metadata(void *user_data, grpc_mdelem **md_elems, grpc_call_next_op(elem, &((call_data *)elem->call_data)->op); } +static void send_security_metadata(grpc_call_element *elem, grpc_call_op *op) { + /* grab pointers to our data from the call element */ + call_data *calld = elem->call_data; + channel_data *channeld = elem->channel_data; + + grpc_credentials *channel_creds = + channeld->security_context->request_metadata_creds; + /* TODO(jboeuf): + Decide on the policy in this case: + - populate both channel and call? + - the call takes precedence over the channel? + - leave this decision up to the channel credentials? */ + if (calld->creds != NULL) { + gpr_log(GPR_ERROR, "Ignoring per call credentials for now."); + } + if (channel_creds != NULL && + grpc_credentials_has_request_metadata(channel_creds)) { + calld->op = *op; /* Copy op (originates from the caller's stack). */ + grpc_credentials_get_request_metadata(channel_creds, + on_credentials_metadata, elem); + } else { + grpc_call_next_op(elem, op); + } +} + +static void on_host_checked(void *user_data, grpc_security_status status) { + grpc_call_element *elem = (grpc_call_element *)user_data; + call_data *calld = elem->call_data; + + if (status == GRPC_SECURITY_OK) { + send_security_metadata(elem, &calld->op); + } else { + char *error_msg; + gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.", + grpc_mdstr_as_c_string(calld->host)); + bubbleup_error(elem, error_msg); + gpr_free(error_msg); + } +} + /* Called either: - in response to an API call (or similar) from above, to send something - a network event (or similar) from below, to receive something @@ -74,26 +141,36 @@ static void call_op(grpc_call_element *elem, grpc_call_element *from_elem, channel_data *channeld = elem->channel_data; switch (op->type) { - case GRPC_SEND_START: { - grpc_credentials *channel_creds = - channeld->security_context->request_metadata_creds; - /* TODO(jboeuf): - Decide on the policy in this case: - - populate both channel and call? - - the call takes precedence over the channel? - - leave this decision up to the channel credentials? */ - if (calld->creds != NULL) { - gpr_log(GPR_ERROR, "Ignoring per call credentials for now."); + case GRPC_SEND_METADATA: + /* Pointer comparison is OK for md_elems created from the same context. */ + if (op->data.metadata->key == channeld->authority_string) { + if (calld->host != NULL) grpc_mdstr_unref(calld->host); + calld->host = grpc_mdstr_ref(op->data.metadata->value); } - if (channel_creds != NULL && - grpc_credentials_has_request_metadata(channel_creds)) { + grpc_call_next_op(elem, op); + break; + + case GRPC_SEND_START: + if (calld->host != NULL) { + grpc_security_status status; + const char *call_host = grpc_mdstr_as_c_string(calld->host); calld->op = *op; /* Copy op (originates from the caller's stack). */ - grpc_credentials_get_request_metadata(channel_creds, - on_credentials_metadata, elem); - break; + status = grpc_channel_security_context_check_call_host( + channeld->security_context, call_host, on_host_checked, elem); + if (status != GRPC_SECURITY_OK) { + if (status == GRPC_SECURITY_ERROR) { + char *error_msg; + gpr_asprintf(&error_msg, + "Invalid host %s set in :authority metadata.", + call_host); + bubbleup_error(elem, error_msg); + gpr_free(error_msg); + } + break; + } } - /* FALLTHROUGH INTENDED. */ - } + send_security_metadata(elem, op); + break; default: /* pass control up or down the stack depending on op->dir */ @@ -116,6 +193,7 @@ static void init_call_elem(grpc_call_element *elem, Find a way to pass-in the credentials from the caller here. */ call_data *calld = elem->call_data; calld->creds = NULL; + calld->host = NULL; } /* Destructor for call_data */ @@ -124,6 +202,9 @@ static void destroy_call_elem(grpc_call_element *elem) { if (calld->creds != NULL) { grpc_credentials_unref(calld->creds); } + if (calld->host != NULL) { + grpc_mdstr_unref(calld->host); + } } /* Constructor for channel_data */ @@ -146,6 +227,11 @@ static void init_channel_elem(grpc_channel_element *elem, GPR_ASSERT(ctx->is_client_side); channeld->security_context = (grpc_channel_security_context *)grpc_security_context_ref(ctx); + channeld->md_ctx = metadata_context; + channeld->authority_string = + grpc_mdstr_from_string(channeld->md_ctx, ":authority"); + channeld->error_msg_key = + grpc_mdstr_from_string(channeld->md_ctx, "grpc-message"); } /* Destructor for channel data */ @@ -154,6 +240,12 @@ static void destroy_channel_elem(grpc_channel_element *elem) { channel_data *channeld = elem->channel_data; grpc_channel_security_context *ctx = channeld->security_context; if (ctx != NULL) grpc_security_context_unref(&ctx->base); + if (channeld->authority_string != NULL) { + grpc_mdstr_unref(channeld->authority_string); + } + if (channeld->error_msg_key != NULL) { + grpc_mdstr_unref(channeld->error_msg_key); + } } const grpc_channel_filter grpc_client_auth_filter = { diff --git a/src/core/security/secure_transport_setup.c b/src/core/security/secure_transport_setup.c index 50a6987fbf..59789a7e4d 100644 --- a/src/core/security/secure_transport_setup.c +++ b/src/core/security/secure_transport_setup.c @@ -113,8 +113,7 @@ static void check_peer(grpc_secure_transport_setup *s) { return; } peer_status = - grpc_security_context_check_peer(s->ctx, &peer, on_peer_checked, s); - tsi_peer_destruct(&peer); + grpc_security_context_check_peer(s->ctx, peer, on_peer_checked, s); if (peer_status == GRPC_SECURITY_ERROR) { gpr_log(GPR_ERROR, "Peer check failed."); secure_transport_setup_done(s, 0); diff --git a/src/core/security/security_context.c b/src/core/security/security_context.c index 1edec29775..adb0269792 100644 --- a/src/core/security/security_context.c +++ b/src/core/security/security_context.c @@ -69,12 +69,22 @@ grpc_security_status grpc_security_context_create_handshaker( } grpc_security_status grpc_security_context_check_peer( - grpc_security_context *ctx, const tsi_peer *peer, - grpc_security_check_peer_cb cb, void *user_data) { - if (ctx == NULL) return GRPC_SECURITY_ERROR; + grpc_security_context *ctx, tsi_peer peer, grpc_security_check_cb cb, + void *user_data) { + if (ctx == NULL) { + tsi_peer_destruct(&peer); + return GRPC_SECURITY_ERROR; + } return ctx->vtable->check_peer(ctx, peer, cb, user_data); } +grpc_security_status grpc_channel_security_context_check_call_host( + grpc_channel_security_context *ctx, const char *host, + grpc_security_check_cb cb, void *user_data) { + if (ctx == NULL || ctx->check_call_host == NULL) return GRPC_SECURITY_ERROR; + return ctx->check_call_host(ctx, host, cb, user_data); +} + void grpc_security_context_unref(grpc_security_context *ctx) { if (ctx == NULL) return; if (gpr_unref(&ctx->refcount)) ctx->vtable->destroy(ctx); @@ -137,6 +147,11 @@ static int check_request_metadata_creds(grpc_credentials *creds) { /* -- Fake implementation. -- */ +typedef struct { + grpc_channel_security_context base; + int call_host_check_is_async; +} grpc_fake_channel_security_context; + static void fake_channel_destroy(grpc_security_context *ctx) { grpc_channel_security_context *c = (grpc_channel_security_context *)ctx; grpc_credentials_unref(c->request_metadata_creds); @@ -158,31 +173,51 @@ static grpc_security_status fake_server_create_handshaker( } static grpc_security_status fake_check_peer(grpc_security_context *ctx, - const tsi_peer *peer, - grpc_security_check_peer_cb cb, + tsi_peer peer, + grpc_security_check_cb cb, void *user_data) { const char *prop_name; - if (peer->property_count != 1) { + grpc_security_status status = GRPC_SECURITY_OK; + if (peer.property_count != 1) { gpr_log(GPR_ERROR, "Fake peers should only have 1 property."); - return GRPC_SECURITY_ERROR; + status = GRPC_SECURITY_ERROR; + goto end; } - prop_name = peer->properties[0].name; + prop_name = peer.properties[0].name; if (prop_name == NULL || strcmp(prop_name, TSI_CERTIFICATE_TYPE_PEER_PROPERTY)) { gpr_log(GPR_ERROR, "Unexpected property in fake peer: %s.", prop_name == NULL ? "<EMPTY>" : prop_name); - return GRPC_SECURITY_ERROR; + status = GRPC_SECURITY_ERROR; + goto end; } - if (peer->properties[0].type != TSI_PEER_PROPERTY_TYPE_STRING) { + if (peer.properties[0].type != TSI_PEER_PROPERTY_TYPE_STRING) { gpr_log(GPR_ERROR, "Invalid type of cert type property."); - return GRPC_SECURITY_ERROR; + status = GRPC_SECURITY_ERROR; + goto end; } - if (strncmp(peer->properties[0].value.string.data, TSI_FAKE_CERTIFICATE_TYPE, - peer->properties[0].value.string.length)) { + if (strncmp(peer.properties[0].value.string.data, TSI_FAKE_CERTIFICATE_TYPE, + peer.properties[0].value.string.length)) { gpr_log(GPR_ERROR, "Invalid value for cert type property."); - return GRPC_SECURITY_ERROR; + status = GRPC_SECURITY_ERROR; + goto end; + } +end: + tsi_peer_destruct(&peer); + return status; +} + +static grpc_security_status fake_channel_check_call_host( + grpc_channel_security_context *ctx, const char *host, + grpc_security_check_cb cb, void *user_data) { + grpc_fake_channel_security_context *c = + (grpc_fake_channel_security_context *)ctx; + if (c->call_host_check_is_async) { + cb(user_data, GRPC_SECURITY_OK); + return GRPC_SECURITY_PENDING; + } else { + return GRPC_SECURITY_OK; } - return GRPC_SECURITY_OK; } static grpc_security_context_vtable fake_channel_vtable = { @@ -192,15 +227,17 @@ static grpc_security_context_vtable fake_server_vtable = { fake_server_destroy, fake_server_create_handshaker, fake_check_peer}; grpc_channel_security_context *grpc_fake_channel_security_context_create( - grpc_credentials *request_metadata_creds) { - grpc_channel_security_context *c = - gpr_malloc(sizeof(grpc_channel_security_context)); - gpr_ref_init(&c->base.refcount, 1); - c->base.is_client_side = 1; - c->base.vtable = &fake_channel_vtable; + grpc_credentials *request_metadata_creds, int call_host_check_is_async) { + grpc_fake_channel_security_context *c = + gpr_malloc(sizeof(grpc_fake_channel_security_context)); + gpr_ref_init(&c->base.base.refcount, 1); + c->base.base.is_client_side = 1; + c->base.base.vtable = &fake_channel_vtable; GPR_ASSERT(check_request_metadata_creds(request_metadata_creds)); - c->request_metadata_creds = grpc_credentials_ref(request_metadata_creds); - return c; + c->base.request_metadata_creds = grpc_credentials_ref(request_metadata_creds); + c->base.check_call_host = fake_channel_check_call_host; + c->call_host_check_is_async = call_host_check_is_async; + return &c->base; } grpc_security_context *grpc_fake_server_security_context_create(void) { @@ -215,7 +252,9 @@ grpc_security_context *grpc_fake_server_security_context_create(void) { typedef struct { grpc_channel_security_context base; tsi_ssl_handshaker_factory *handshaker_factory; - char *secure_peer_name; + char *target_name; + char *overridden_target_name; + tsi_peer peer; } grpc_ssl_channel_security_context; typedef struct { @@ -230,7 +269,9 @@ static void ssl_channel_destroy(grpc_security_context *ctx) { if (c->handshaker_factory != NULL) { tsi_ssl_handshaker_factory_destroy(c->handshaker_factory); } - if (c->secure_peer_name != NULL) gpr_free(c->secure_peer_name); + if (c->target_name != NULL) gpr_free(c->target_name); + if (c->overridden_target_name != NULL) gpr_free(c->overridden_target_name); + tsi_peer_destruct(&c->peer); gpr_free(ctx); } @@ -244,11 +285,11 @@ static void ssl_server_destroy(grpc_security_context *ctx) { static grpc_security_status ssl_create_handshaker( tsi_ssl_handshaker_factory *handshaker_factory, int is_client, - const char *secure_peer_name, tsi_handshaker **handshaker) { + const char *peer_name, tsi_handshaker **handshaker) { tsi_result result = TSI_OK; if (handshaker_factory == NULL) return GRPC_SECURITY_ERROR; result = tsi_ssl_handshaker_factory_create_handshaker( - handshaker_factory, is_client ? secure_peer_name : NULL, handshaker); + handshaker_factory, is_client ? peer_name : NULL, handshaker); if (result != TSI_OK) { gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", tsi_result_to_string(result)); @@ -261,7 +302,10 @@ static grpc_security_status ssl_channel_create_handshaker( grpc_security_context *ctx, tsi_handshaker **handshaker) { grpc_ssl_channel_security_context *c = (grpc_ssl_channel_security_context *)ctx; - return ssl_create_handshaker(c->handshaker_factory, 1, c->secure_peer_name, + return ssl_create_handshaker(c->handshaker_factory, 1, + c->overridden_target_name != NULL + ? c->overridden_target_name + : c->target_name, handshaker); } @@ -271,7 +315,7 @@ static grpc_security_status ssl_server_create_handshaker( return ssl_create_handshaker(c->handshaker_factory, 0, NULL, handshaker); } -static grpc_security_status ssl_check_peer(const char *secure_peer_name, +static grpc_security_status ssl_check_peer(const char *peer_name, const tsi_peer *peer) { /* Check the ALPN. */ const tsi_peer_property *p = @@ -291,28 +335,54 @@ static grpc_security_status ssl_check_peer(const char *secure_peer_name, } /* Check the peer name if specified. */ - if (secure_peer_name != NULL && - !tsi_ssl_peer_matches_name(peer, secure_peer_name)) { - gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate", - secure_peer_name); + if (peer_name != NULL && + !tsi_ssl_peer_matches_name(peer, peer_name)) { + gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate", peer_name); return GRPC_SECURITY_ERROR; } return GRPC_SECURITY_OK; } -static grpc_security_status ssl_channel_check_peer( - grpc_security_context *ctx, const tsi_peer *peer, - grpc_security_check_peer_cb cb, void *user_data) { +static grpc_security_status ssl_channel_check_peer(grpc_security_context *ctx, + tsi_peer peer, + grpc_security_check_cb cb, + void *user_data) { grpc_ssl_channel_security_context *c = (grpc_ssl_channel_security_context *)ctx; - return ssl_check_peer(c->secure_peer_name, peer); + grpc_security_status status = ssl_check_peer(c->overridden_target_name != NULL + ? c->overridden_target_name + : c->target_name, + &peer); + c->peer = peer; + return status; +} + +static grpc_security_status ssl_server_check_peer(grpc_security_context *ctx, + tsi_peer peer, + grpc_security_check_cb cb, + void *user_data) { + /* TODO(jboeuf): Find a way to expose the peer to the authorization layer. */ + grpc_security_status status = ssl_check_peer(NULL, &peer); + tsi_peer_destruct(&peer); + return status; } -static grpc_security_status ssl_server_check_peer( - grpc_security_context *ctx, const tsi_peer *peer, - grpc_security_check_peer_cb cb, void *user_data) { - /* TODO(jboeuf): Find a way to expose the peer to the authorization layer. */ - return ssl_check_peer(NULL, peer); +static grpc_security_status ssl_channel_check_call_host( + grpc_channel_security_context *ctx, const char *host, + grpc_security_check_cb cb, void *user_data) { + grpc_ssl_channel_security_context *c = + (grpc_ssl_channel_security_context *)ctx; + + if (tsi_ssl_peer_matches_name(&c->peer, host)) return GRPC_SECURITY_OK; + + /* If the target name was overridden, then the original target_name was + 'checked' transitively during the previous peer check at the end of the + handshake. */ + if (c->overridden_target_name != NULL && !strcmp(host, c->target_name)) { + return GRPC_SECURITY_OK; + } else { + return GRPC_SECURITY_ERROR; + } } static grpc_security_context_vtable ssl_channel_vtable = { @@ -345,7 +415,8 @@ static size_t get_default_pem_roots(const unsigned char **pem_root_certs) { grpc_security_status grpc_ssl_channel_security_context_create( grpc_credentials *request_metadata_creds, const grpc_ssl_config *config, - const char *secure_peer_name, grpc_channel_security_context **ctx) { + const char *target_name, const char *overridden_target_name, + grpc_channel_security_context **ctx) { size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions(); const unsigned char **alpn_protocol_strings = gpr_malloc(sizeof(const char *) * num_alpn_protocols); @@ -364,8 +435,8 @@ grpc_security_status grpc_ssl_channel_security_context_create( strlen(grpc_chttp2_get_alpn_version_index(i)); } - if (config == NULL || secure_peer_name == NULL) { - gpr_log(GPR_ERROR, "An ssl channel needs a config and a secure name."); + if (config == NULL || target_name == NULL) { + gpr_log(GPR_ERROR, "An ssl channel needs a config and a target name."); goto error; } if (!check_request_metadata_creds(request_metadata_creds)) { @@ -379,8 +450,12 @@ grpc_security_status grpc_ssl_channel_security_context_create( c->base.base.vtable = &ssl_channel_vtable; c->base.base.is_client_side = 1; c->base.request_metadata_creds = grpc_credentials_ref(request_metadata_creds); - if (secure_peer_name != NULL) { - c->secure_peer_name = gpr_strdup(secure_peer_name); + c->base.check_call_host = ssl_channel_check_call_host; + if (target_name != NULL) { + c->target_name = gpr_strdup(target_name); + } + if (overridden_target_name != NULL) { + c->overridden_target_name = gpr_strdup(overridden_target_name); } if (config->pem_root_certs == NULL) { pem_root_certs_size = get_default_pem_roots(&pem_root_certs); @@ -478,7 +553,7 @@ grpc_channel *grpc_ssl_channel_create(grpc_credentials *ssl_creds, grpc_channel *channel = NULL; grpc_security_status status = GRPC_SECURITY_OK; size_t i = 0; - const char *secure_peer_name = target; + const char *overridden_target_name = NULL; grpc_arg arg; grpc_channel_args *new_args; @@ -486,13 +561,13 @@ grpc_channel *grpc_ssl_channel_create(grpc_credentials *ssl_creds, grpc_arg *arg = &args->args[i]; if (!strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) && arg->type == GRPC_ARG_STRING) { - secure_peer_name = arg->value.string; + overridden_target_name = arg->value.string; break; } } status = grpc_ssl_channel_security_context_create( request_metadata_creds, grpc_ssl_credentials_get_config(ssl_creds), - secure_peer_name, &ctx); + target, overridden_target_name, &ctx); if (status != GRPC_SECURITY_OK) { return grpc_lame_client_channel_create(); } @@ -510,7 +585,7 @@ grpc_channel *grpc_fake_transport_security_channel_create( grpc_credentials *fake_creds, grpc_credentials *request_metadata_creds, const char *target, const grpc_channel_args *args) { grpc_channel_security_context *ctx = - grpc_fake_channel_security_context_create(request_metadata_creds); + grpc_fake_channel_security_context_create(request_metadata_creds, 1); grpc_channel *channel = grpc_secure_channel_create_internal(target, args, ctx); grpc_security_context_unref(&ctx->base); diff --git a/src/core/security/security_context.h b/src/core/security/security_context.h index 2caa2d3690..25d467d717 100644 --- a/src/core/security/security_context.h +++ b/src/core/security/security_context.h @@ -56,16 +56,15 @@ typedef struct grpc_security_context grpc_security_context; #define GRPC_SECURITY_CONTEXT_ARG "grpc.security_context" -typedef void (*grpc_security_check_peer_cb)(void *user_data, - grpc_security_status status); +typedef void (*grpc_security_check_cb)(void *user_data, + grpc_security_status status); typedef struct { void (*destroy)(grpc_security_context *ctx); grpc_security_status (*create_handshaker)(grpc_security_context *ctx, tsi_handshaker **handshaker); - grpc_security_status (*check_peer)(grpc_security_context *ctx, - const tsi_peer *peer, - grpc_security_check_peer_cb, + grpc_security_status (*check_peer)(grpc_security_context *ctx, tsi_peer peer, + grpc_security_check_cb cb, void *user_data); } grpc_security_context_vtable; @@ -87,18 +86,14 @@ grpc_security_status grpc_security_context_create_handshaker( /* Check the peer. Implementations can choose to check the peer either synchronously or - asynchronously. In the first case, a successful will return + asynchronously. In the first case, a successful call will return GRPC_SECURITY_OK. In the asynchronous case, the call will return GRPC_SECURITY_PENDING unless an error is detected early on. - - Note: - Asynchronous implementations of this interface should make a copy of the - fields of the peer they want to check as there is no guarantee on the - lifetime of the peer object beyond this call. + Ownership of the peer is transfered. */ grpc_security_status grpc_security_context_check_peer( - grpc_security_context *ctx, const tsi_peer *peer, - grpc_security_check_peer_cb cb, void *user_data); + grpc_security_context *ctx, tsi_peer peer, + grpc_security_check_cb cb, void *user_data); /* Util to encapsulate the context in a channel arg. */ grpc_arg grpc_security_context_to_arg(grpc_security_context *ctx); @@ -120,14 +115,26 @@ typedef struct grpc_channel_security_context grpc_channel_security_context; struct grpc_channel_security_context { grpc_security_context base; /* requires is_client_side to be non 0. */ grpc_credentials *request_metadata_creds; + grpc_security_status (*check_call_host)( + grpc_channel_security_context *ctx, const char *host, + grpc_security_check_cb cb, void *user_data); }; +/* Checks that the host that will be set for a call is acceptable. + Implementations can choose do the check either synchronously or + asynchronously. In the first case, a successful call will return + GRPC_SECURITY_OK. In the asynchronous case, the call will return + GRPC_SECURITY_PENDING unless an error is detected early on. */ +grpc_security_status grpc_channel_security_context_check_call_host( + grpc_channel_security_context *ctx, const char *host, + grpc_security_check_cb cb, void *user_data); + /* --- Creation security contexts. --- */ /* For TESTING ONLY! Creates a fake context that emulates real channel security. */ grpc_channel_security_context *grpc_fake_channel_security_context_create( - grpc_credentials *request_metadata_creds); + grpc_credentials *request_metadata_creds, int call_host_check_is_async); /* For TESTING ONLY! Creates a fake context that emulates real server security. */ @@ -148,7 +155,8 @@ grpc_security_context *grpc_fake_server_security_context_create(void); */ grpc_security_status grpc_ssl_channel_security_context_create( grpc_credentials *request_metadata_creds, const grpc_ssl_config *config, - const char *secure_peer_name, grpc_channel_security_context **ctx); + const char *target_name, const char *overridden_target_name, + grpc_channel_security_context **ctx); /* Creates an SSL server_security_context. - config is the SSL config to be used for the SSL channel establishment. diff --git a/src/core/support/cpu_windows.c b/src/core/support/cpu_windows.c new file mode 100644 index 0000000000..c533f9d554 --- /dev/null +++ b/src/core/support/cpu_windows.c @@ -0,0 +1,54 @@ +/* +* +* Copyright 2014, Google Inc. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following disclaimer +* in the documentation and/or other materials provided with the +* distribution. +* * Neither the name of Google Inc. nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include <grpc/support/port_platform.h> + +#ifdef GPR_WIN32 + +#include "src/core/support/cpu.h" + +#include <grpc/support/log.h> + +unsigned gpr_cpu_num_cores(void) { + /* TODO(jtattermusch): implement */ + gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1"); + return 1; +} + +unsigned gpr_cpu_current_cpu(void) { + /* TODO(jtattermusch): implement */ + gpr_log(GPR_ERROR, "Cannot determine current CPU"); + return 0; +} + +#endif /* GPR_WIN32 */ diff --git a/src/core/support/file_win32.c b/src/core/support/file_win32.c index af7eebe3de..7749d4553f 100644 --- a/src/core/support/file_win32.c +++ b/src/core/support/file_win32.c @@ -76,7 +76,7 @@ end: *tmp_filename_out = gpr_tchar_to_char(tmp_filename); } - gpr_free(tmp_filename); + gpr_free(template_string); return result; } diff --git a/src/core/surface/secure_channel_create.c b/src/core/surface/secure_channel_create.c index defee79766..562e27ff6d 100644 --- a/src/core/surface/secure_channel_create.c +++ b/src/core/surface/secure_channel_create.c @@ -189,8 +189,8 @@ static void done_setup(void *sp) { static grpc_transport_setup_result complete_setup(void *channel_stack, grpc_transport *transport, grpc_mdctx *mdctx) { - static grpc_channel_filter const *extra_filters[] = {&grpc_http_client_filter, - &grpc_http_filter}; + static grpc_channel_filter const *extra_filters[] = { + &grpc_client_auth_filter, &grpc_http_client_filter, &grpc_http_filter}; return grpc_client_channel_transport_setup_complete( channel_stack, transport, extra_filters, GPR_ARRAY_SIZE(extra_filters), mdctx); @@ -208,7 +208,7 @@ grpc_channel *grpc_secure_channel_create_internal( grpc_arg context_arg; grpc_channel_args *args_copy; grpc_mdctx *mdctx = grpc_mdctx_create(); -#define MAX_FILTERS 4 +#define MAX_FILTERS 3 const grpc_channel_filter *filters[MAX_FILTERS]; int n = 0; if (grpc_find_security_context_in_args(args) != NULL) { @@ -222,7 +222,6 @@ grpc_channel *grpc_secure_channel_create_internal( if (grpc_channel_args_is_census_enabled(args)) { filters[n++] = &grpc_client_census_filter; } - filters[n++] = &grpc_client_auth_filter; filters[n++] = &grpc_client_channel_filter; GPR_ASSERT(n <= MAX_FILTERS); channel = grpc_channel_create_from_filters(filters, n, args_copy, mdctx, 1); diff --git a/src/csharp/GrpcCore/GrpcEnvironment.cs b/src/csharp/GrpcCore/GrpcEnvironment.cs index 7a644f4961..201320828b 100644 --- a/src/csharp/GrpcCore/GrpcEnvironment.cs +++ b/src/csharp/GrpcCore/GrpcEnvironment.cs @@ -13,10 +13,10 @@ namespace Google.GRPC.Core { const int THREAD_POOL_SIZE = 1; - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern void grpc_init(); - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern void grpc_shutdown(); static object staticLock = new object(); diff --git a/src/csharp/GrpcCore/Internal/CallSafeHandle.cs b/src/csharp/GrpcCore/Internal/CallSafeHandle.cs index 6c9c58a4c3..bbb830b355 100644 --- a/src/csharp/GrpcCore/Internal/CallSafeHandle.cs +++ b/src/csharp/GrpcCore/Internal/CallSafeHandle.cs @@ -15,66 +15,66 @@ namespace Google.GRPC.Core.Internal { const UInt32 GRPC_WRITE_BUFFER_HINT = 1; - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern CallSafeHandle grpc_channel_create_call_old(ChannelSafeHandle channel, string method, string host, Timespec deadline); - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern GRPCCallError grpc_call_add_metadata(CallSafeHandle call, IntPtr metadata, UInt32 flags); - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern GRPCCallError grpc_call_invoke_old(CallSafeHandle call, CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, UInt32 flags); - [DllImport("libgrpc.so", EntryPoint = "grpc_call_invoke_old")] + [DllImport("grpc.dll", EntryPoint = "grpc_call_invoke_old")] static extern GRPCCallError grpc_call_invoke_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle cq, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate metadataReadCallback, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback, UInt32 flags); - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern GRPCCallError grpc_call_server_accept_old(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, IntPtr finishedTag); - [DllImport("libgrpc.so", EntryPoint = "grpc_call_server_accept_old")] + [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("libgrpc.so")] + [DllImport("grpc.dll")] static extern GRPCCallError grpc_call_server_end_initial_metadata_old(CallSafeHandle call, UInt32 flags); - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern GRPCCallError grpc_call_cancel(CallSafeHandle call); - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern GRPCCallError grpc_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description); - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern GRPCCallError grpc_call_start_write_status_old(CallSafeHandle call, StatusCode statusCode, string statusMessage, IntPtr tag); - [DllImport("libgrpc.so", EntryPoint = "grpc_call_start_write_status_old")] + [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("libgrpc.so")] + [DllImport("grpc.dll")] static extern GRPCCallError grpc_call_writes_done_old(CallSafeHandle call, IntPtr tag); - [DllImport("libgrpc.so", EntryPoint = "grpc_call_writes_done_old")] + [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("libgrpc.so")] + [DllImport("grpc.dll")] static extern GRPCCallError grpc_call_start_read_old(CallSafeHandle call, IntPtr tag); - [DllImport("libgrpc.so", EntryPoint = "grpc_call_start_read_old")] + [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("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern void grpc_call_start_write_from_copied_buffer(CallSafeHandle call, byte[] buffer, UIntPtr length, IntPtr tag, UInt32 flags); - [DllImport("libgrpc_csharp_ext.so", EntryPoint = "grpc_call_start_write_from_copied_buffer")] + [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, byte[] buffer, UIntPtr length, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback, UInt32 flags); - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern void grpc_call_destroy(IntPtr call); private CallSafeHandle() diff --git a/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs b/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs index 3a09d8b1b6..0f38d63f98 100644 --- a/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs +++ b/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs @@ -10,10 +10,10 @@ namespace Google.GRPC.Core.Internal /// </summary> internal class ChannelSafeHandle : SafeHandleZeroIsInvalid { - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern ChannelSafeHandle grpc_channel_create(string target, IntPtr channelArgs); - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern void grpc_channel_destroy(IntPtr channel); private ChannelSafeHandle() diff --git a/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs b/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs index 73dd3edde3..f098de6820 100644 --- a/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs +++ b/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs @@ -9,22 +9,22 @@ namespace Google.GRPC.Core.Internal /// </summary> internal class CompletionQueueSafeHandle : SafeHandleZeroIsInvalid { - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern CompletionQueueSafeHandle grpc_completion_queue_create(); - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern EventSafeHandle grpc_completion_queue_pluck(CompletionQueueSafeHandle cq, IntPtr tag, Timespec deadline); - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern EventSafeHandle grpc_completion_queue_next(CompletionQueueSafeHandle cq, Timespec deadline); - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern void grpc_completion_queue_shutdown(CompletionQueueSafeHandle cq); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern GRPCCompletionType grpc_completion_queue_next_with_callback(CompletionQueueSafeHandle cq); - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern void grpc_completion_queue_destroy(IntPtr cq); private CompletionQueueSafeHandle() diff --git a/src/csharp/GrpcCore/Internal/Enums.cs b/src/csharp/GrpcCore/Internal/Enums.cs index 46e3bca6eb..1151e94899 100644 --- a/src/csharp/GrpcCore/Internal/Enums.cs +++ b/src/csharp/GrpcCore/Internal/Enums.cs @@ -36,29 +36,36 @@ namespace Google.GRPC.Core.Internal /// </summary> internal enum GRPCCompletionType { - GRPC_QUEUE_SHUTDOWN, /* Shutting down */ - GRPC_READ, + GRPC_QUEUE_SHUTDOWN, + + /* operation completion */ + GRPC_OP_COMPLETE, + /* A read has completed */ - GRPC_INVOKE_ACCEPTED, - /* An invoke call has been accepted by flow - control */ + GRPC_READ, + + /* A write has been accepted by flow control */ GRPC_WRITE_ACCEPTED, - /* A write has been accepted by - flow control */ - GRPC_FINISH_ACCEPTED, + /* writes_done or write_status has been accepted */ + GRPC_FINISH_ACCEPTED, + + /* The metadata array sent by server received at client */ GRPC_CLIENT_METADATA_READ, - /* The metadata array sent by server received at - client */ + + /* An RPC has finished. The event contains status. + * On the server this will be OK or Cancelled. */ GRPC_FINISHED, - /* An RPC has finished. The event contains status. - On the server this will be OK or Cancelled. */ - GRPC_SERVER_RPC_NEW, + /* A new RPC has arrived at the server */ + GRPC_SERVER_RPC_NEW, + + /* The server has finished shutting down */ + GRPC_SERVER_SHUTDOWN, + + /* must be last, forces users to include a default: case */ GRPC_COMPLETION_DO_NOT_USE - /* must be last, forces users to include - a default: case */ } /// <summary> diff --git a/src/csharp/GrpcCore/Internal/Event.cs b/src/csharp/GrpcCore/Internal/Event.cs index 7056005ba6..5853ddd570 100644 --- a/src/csharp/GrpcCore/Internal/Event.cs +++ b/src/csharp/GrpcCore/Internal/Event.cs @@ -9,34 +9,34 @@ namespace Google.GRPC.Core.Internal /// </summary> internal class EventSafeHandle : SafeHandleZeroIsInvalid { - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern void grpc_event_finish(IntPtr ev); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern GRPCCompletionType grpc_event_type(EventSafeHandle ev); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern CallSafeHandle grpc_event_call(EventSafeHandle ev); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern GRPCOpError grpc_event_write_accepted(EventSafeHandle ev); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandle ev); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern StatusCode grpc_event_finished_status(EventSafeHandle ev); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern IntPtr grpc_event_finished_details(EventSafeHandle ev); // returns const char* - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern IntPtr grpc_event_read_length(EventSafeHandle ev); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern void grpc_event_read_copy_to_buffer(EventSafeHandle ev, byte[] buffer, UIntPtr bufferLen); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandle ev); // returns const char* public GRPCCompletionType GetCompletionType() @@ -98,34 +98,34 @@ namespace Google.GRPC.Core.Internal /// </summary> internal class EventSafeHandleNotOwned : SafeHandleZeroIsInvalid { - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern void grpc_event_finish(IntPtr ev); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern GRPCCompletionType grpc_event_type(EventSafeHandleNotOwned ev); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern CallSafeHandle grpc_event_call(EventSafeHandleNotOwned ev); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern GRPCOpError grpc_event_write_accepted(EventSafeHandleNotOwned ev); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandleNotOwned ev); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern StatusCode grpc_event_finished_status(EventSafeHandleNotOwned ev); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern IntPtr grpc_event_finished_details(EventSafeHandleNotOwned ev); // returns const char* - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern IntPtr grpc_event_read_length(EventSafeHandleNotOwned ev); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern void grpc_event_read_copy_to_buffer(EventSafeHandleNotOwned ev, byte[] buffer, UIntPtr bufferLen); - [DllImport("libgrpc_csharp_ext.so")] + [DllImport("grpc_csharp_ext.dll")] static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandleNotOwned ev); // returns const char* public EventSafeHandleNotOwned() : base(false) diff --git a/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs b/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs index 08d4cf0192..d363b34f0b 100644 --- a/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs +++ b/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs @@ -10,30 +10,30 @@ namespace Google.GRPC.Core.Internal /// </summary> internal sealed class ServerSafeHandle : SafeHandleZeroIsInvalid { - [DllImport("libgrpc.so", EntryPoint = "grpc_server_request_call_old")] + [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("libgrpc.so")] + [DllImport("grpc.dll")] static extern ServerSafeHandle grpc_server_create(CompletionQueueSafeHandle cq, IntPtr args); // TODO: check int representation size - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern int grpc_server_add_http2_port(ServerSafeHandle server, string addr); // TODO: check int representation size - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern int grpc_server_add_secure_http2_port(ServerSafeHandle server, string addr); - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern void grpc_server_start(ServerSafeHandle server); - [DllImport("libgrpc.so")] + [DllImport("grpc.dll")] static extern void grpc_server_shutdown(ServerSafeHandle server); - [DllImport("libgrpc.so", EntryPoint = "grpc_server_shutdown_and_notify")] + [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("libgrpc.so")] + [DllImport("grpc.dll")] static extern void grpc_server_destroy(IntPtr server); private ServerSafeHandle() diff --git a/src/csharp/GrpcCore/Internal/Timespec.cs b/src/csharp/GrpcCore/Internal/Timespec.cs index 8ffaf70bbf..5a197e121c 100644 --- a/src/csharp/GrpcCore/Internal/Timespec.cs +++ b/src/csharp/GrpcCore/Internal/Timespec.cs @@ -13,7 +13,7 @@ namespace Google.GRPC.Core.Internal const int nanosPerSecond = 1000 * 1000 * 1000; const int nanosPerTick = 100; - [DllImport("libgpr.so")] + [DllImport("gpr.dll")] static extern Timespec gpr_now(); // TODO: this only works on 64bit linux, can we autoselect the right size of ints? diff --git a/src/node/examples/perf_test.js b/src/node/examples/perf_test.js new file mode 100644 index 0000000000..c5e2872736 --- /dev/null +++ b/src/node/examples/perf_test.js @@ -0,0 +1,115 @@ +/* + * + * 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. + * + */ + +var grpc = require('..'); +var testProto = grpc.load(__dirname + '/../interop/test.proto').grpc.testing; +var _ = require('underscore'); +var interop_server = require('../interop/interop_server.js'); + +function runTest(iterations, callback) { + var testServer = interop_server.getServer(0, false); + testServer.server.listen(); + var client = new testProto.TestService('localhost:' + testServer.port); + + function runIterations(finish) { + var start = process.hrtime(); + var intervals = []; + var pending = iterations; + function next(i) { + if (i >= iterations) { + testServer.server.shutdown(); + var totalDiff = process.hrtime(start); + finish({ + total: totalDiff[0] * 1000000 + totalDiff[1] / 1000, + intervals: intervals + }); + } else{ + var deadline = new Date(); + deadline.setSeconds(deadline.getSeconds() + 3); + var startTime = process.hrtime(); + client.emptyCall({}, function(err, resp) { + var timeDiff = process.hrtime(startTime); + intervals[i] = timeDiff[0] * 1000000 + timeDiff[1] / 1000; + next(i+1); + }, {}, deadline); + } + } + next(0); + } + + function warmUp(num) { + var pending = num; + for (var i = 0; i < num; i++) { + (function(i) { + client.emptyCall({}, function(err, resp) { + pending--; + if (pending === 0) { + runIterations(callback); + } + }); + })(i); + } + } + warmUp(100); +} + +function percentile(arr, percentile) { + if (percentile > 99) { + percentile = 99; + } + if (percentile < 0) { + percentile = 0; + } + return arr[(arr.length * percentile / 100)|0]; +} + +if (require.main === module) { + var count; + if (process.argv.length >= 3) { + count = process.argv[2]; + } else { + count = 100; + } + runTest(count, function(results) { + var sorted_intervals = _.sortBy(results.intervals, _.identity); + console.log('count:', count); + console.log('total time:', results.total, 'us'); + console.log('median:', percentile(sorted_intervals, 50), 'us'); + console.log('90th percentile:', percentile(sorted_intervals, 90), 'us'); + console.log('95th percentile:', percentile(sorted_intervals, 95), 'us'); + console.log('99th percentile:', percentile(sorted_intervals, 99), 'us'); + console.log('QPS:', (count / results.total) * 1000000); + }); +} + +module.exports = runTest; diff --git a/src/python/src/_adapter/_c_test.py b/src/python/src/_adapter/_c_test.py index 19c91ffe01..210ac1fff7 100644 --- a/src/python/src/_adapter/_c_test.py +++ b/src/python/src/_adapter/_c_test.py @@ -92,7 +92,7 @@ class _CTest(unittest.TestCase): _c.init() completion_queue = _c.CompletionQueue() - server = _c.Server(completion_queue) + server = _c.Server(completion_queue, None) server.add_http2_addr('[::]:0') server.start() server.stop() @@ -102,7 +102,7 @@ class _CTest(unittest.TestCase): service_tag = object() completion_queue = _c.CompletionQueue() - server = _c.Server(completion_queue) + server = _c.Server(completion_queue, None) server.add_http2_addr('[::]:0') server.start() server.service(service_tag) @@ -119,7 +119,7 @@ class _CTest(unittest.TestCase): del completion_queue completion_queue = _c.CompletionQueue() - server = _c.Server(completion_queue) + server = _c.Server(completion_queue, None) server.add_http2_addr('[::]:0') server.start() thread = threading.Thread(target=completion_queue.get, args=(_FUTURE,)) @@ -162,6 +162,31 @@ class _CTest(unittest.TestCase): _c.shut_down() + @unittest.skip('TODO(nathaniel): find and use real-enough test credentials') + def test_secure_server(self): + _c.init() + + server_credentials = _c.ServerCredentials( + 'root certificate', (('private key', 'certificate chain'),)) + + completion_queue = _c.CompletionQueue() + server = _c.Server(completion_queue, server_credentials) + server.add_http2_addr('[::]:0') + server.start() + thread = threading.Thread(target=completion_queue.get, args=(_FUTURE,)) + thread.start() + time.sleep(1) + server.stop() + completion_queue.stop() + for _ in range(_IDEMPOTENCE_DEMONSTRATION): + event = completion_queue.get(time.time() + _TIMEOUT) + self.assertIs(event.kind, _datatypes.Event.Kind.STOP) + thread.join() + del server + del completion_queue + + _c.shut_down() + if __name__ == '__main__': unittest.main() diff --git a/src/python/src/_adapter/_low_test.py b/src/python/src/_adapter/_low_test.py index 57b3be66a0..899ccf53c8 100644 --- a/src/python/src/_adapter/_low_test.py +++ b/src/python/src/_adapter/_low_test.py @@ -82,7 +82,7 @@ class EchoTest(unittest.TestCase): self.host = 'localhost' self.server_completion_queue = _low.CompletionQueue() - self.server = _low.Server(self.server_completion_queue) + self.server = _low.Server(self.server_completion_queue, None) port = self.server.add_http2_addr('[::]:0') self.server.start() @@ -260,7 +260,7 @@ class CancellationTest(unittest.TestCase): self.host = 'localhost' self.server_completion_queue = _low.CompletionQueue() - self.server = _low.Server(self.server_completion_queue) + self.server = _low.Server(self.server_completion_queue, None) port = self.server.add_http2_addr('[::]:0') self.server.start() diff --git a/src/python/src/_adapter/_server.c b/src/python/src/_adapter/_server.c index d2730d9ae8..503be61ab4 100644 --- a/src/python/src/_adapter/_server.c +++ b/src/python/src/_adapter/_server.c @@ -38,18 +38,30 @@ #include "_adapter/_completion_queue.h" #include "_adapter/_error.h" +#include "_adapter/_server_credentials.h" static int pygrpc_server_init(Server *self, PyObject *args, PyObject *kwds) { const PyObject *completion_queue; - if (!(PyArg_ParseTuple(args, "O!", &pygrpc_CompletionQueueType, - &completion_queue))) { + PyObject *server_credentials; + if (!(PyArg_ParseTuple(args, "O!O", &pygrpc_CompletionQueueType, + &completion_queue, &server_credentials))) { + self->c_server = NULL; + return -1; + } + if (server_credentials == Py_None) { + self->c_server = grpc_server_create( + ((CompletionQueue *)completion_queue)->c_completion_queue, NULL); + return 0; + } else if (PyObject_TypeCheck(server_credentials, + &pygrpc_ServerCredentialsType)) { + self->c_server = grpc_secure_server_create( + ((ServerCredentials *)server_credentials)->c_server_credentials, + ((CompletionQueue *)completion_queue)->c_completion_queue, NULL); + return 0; + } else { self->c_server = NULL; return -1; } - - self->c_server = grpc_server_create( - ((CompletionQueue *)completion_queue)->c_completion_queue, NULL); - return 0; } static void pygrpc_server_dealloc(Server *self) { diff --git a/src/python/src/_adapter/fore.py b/src/python/src/_adapter/fore.py index c307e7ce63..2f102751f2 100644 --- a/src/python/src/_adapter/fore.py +++ b/src/python/src/_adapter/fore.py @@ -265,7 +265,7 @@ class ForeLink(ticket_interfaces.ForeLink): """ with self._condition: self._completion_queue = _low.CompletionQueue() - self._server = _low.Server(self._completion_queue) + self._server = _low.Server(self._completion_queue, None) port = self._server.add_http2_addr( '[::]:%d' % (0 if self._port is None else self._port)) self._server.start() |