mirror of https://github.com/grpc/grpc.git
commit
573a09688a
210 changed files with 22858 additions and 3123 deletions
@ -0,0 +1,53 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __GRPC_SUPPORT_LOG_WIN32_H__ |
||||||
|
#define __GRPC_SUPPORT_LOG_WIN32_H__ |
||||||
|
|
||||||
|
#include <windows.h> |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Returns a string allocated with gpr_malloc that contains a UTF-8
|
||||||
|
* formatted error message, corresponding to the error messageid. |
||||||
|
* Use in conjunction with GetLastError() et al. |
||||||
|
*/ |
||||||
|
char *gpr_format_message(DWORD messageid); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif /* __GRPC_SUPPORT_LOG_H__ */ |
@ -0,0 +1,200 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* 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_WINSOCK_SOCKET |
||||||
|
|
||||||
|
#include <winsock2.h> |
||||||
|
|
||||||
|
#include <grpc/support/log.h> |
||||||
|
#include <grpc/support/log_win32.h> |
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/thd.h> |
||||||
|
|
||||||
|
#include "src/core/iomgr/alarm_internal.h" |
||||||
|
#include "src/core/iomgr/iocp_windows.h" |
||||||
|
#include "src/core/iomgr/iomgr_internal.h" |
||||||
|
#include "src/core/iomgr/socket_windows.h" |
||||||
|
|
||||||
|
static ULONG g_iocp_kick_token; |
||||||
|
static OVERLAPPED g_iocp_custom_overlap; |
||||||
|
|
||||||
|
static gpr_event g_shutdown_iocp; |
||||||
|
static gpr_event g_iocp_done; |
||||||
|
|
||||||
|
static HANDLE g_iocp; |
||||||
|
|
||||||
|
static int do_iocp_work() { |
||||||
|
BOOL success; |
||||||
|
DWORD bytes = 0; |
||||||
|
DWORD flags = 0; |
||||||
|
ULONG_PTR completion_key; |
||||||
|
LPOVERLAPPED overlapped; |
||||||
|
gpr_timespec wait_time = gpr_inf_future; |
||||||
|
grpc_winsocket *socket; |
||||||
|
grpc_winsocket_callback_info *info; |
||||||
|
void(*f)(void *, int) = NULL; |
||||||
|
void *opaque = NULL; |
||||||
|
success = GetQueuedCompletionStatus(g_iocp, &bytes, |
||||||
|
&completion_key, &overlapped, |
||||||
|
gpr_time_to_millis(wait_time)); |
||||||
|
if (!success && !overlapped) { |
||||||
|
/* The deadline got attained. */ |
||||||
|
return 0; |
||||||
|
} |
||||||
|
GPR_ASSERT(completion_key && overlapped); |
||||||
|
if (overlapped == &g_iocp_custom_overlap) { |
||||||
|
if (completion_key == (ULONG_PTR) &g_iocp_kick_token) { |
||||||
|
/* We were awoken from a kick. */ |
||||||
|
gpr_log(GPR_DEBUG, "do_iocp_work - got a kick"); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
gpr_log(GPR_ERROR, "Unknown custom completion key."); |
||||||
|
abort(); |
||||||
|
} |
||||||
|
|
||||||
|
socket = (grpc_winsocket*) completion_key; |
||||||
|
if (overlapped == &socket->write_info.overlapped) { |
||||||
|
gpr_log(GPR_DEBUG, "do_iocp_work - got write packet"); |
||||||
|
info = &socket->write_info; |
||||||
|
} else if (overlapped == &socket->read_info.overlapped) { |
||||||
|
gpr_log(GPR_DEBUG, "do_iocp_work - got read packet"); |
||||||
|
info = &socket->read_info; |
||||||
|
} else { |
||||||
|
gpr_log(GPR_ERROR, "Unknown IOCP operation"); |
||||||
|
abort(); |
||||||
|
} |
||||||
|
success = WSAGetOverlappedResult(socket->socket, &info->overlapped, &bytes, |
||||||
|
FALSE, &flags); |
||||||
|
gpr_log(GPR_DEBUG, "bytes: %u, flags: %u - op %s", bytes, flags, |
||||||
|
success ? "succeeded" : "failed"); |
||||||
|
info->bytes_transfered = bytes; |
||||||
|
info->wsa_error = success ? 0 : WSAGetLastError(); |
||||||
|
GPR_ASSERT(overlapped == &info->overlapped); |
||||||
|
gpr_mu_lock(&socket->state_mu); |
||||||
|
GPR_ASSERT(!info->has_pending_iocp); |
||||||
|
if (info->cb) { |
||||||
|
f = info->cb; |
||||||
|
opaque = info->opaque; |
||||||
|
info->cb = NULL; |
||||||
|
} else { |
||||||
|
info->has_pending_iocp = 1; |
||||||
|
} |
||||||
|
gpr_mu_unlock(&socket->state_mu); |
||||||
|
if (f) f(opaque, 1); |
||||||
|
|
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
static void iocp_loop(void *p) { |
||||||
|
while (!gpr_event_get(&g_shutdown_iocp)) { |
||||||
|
grpc_maybe_call_delayed_callbacks(NULL, 1); |
||||||
|
do_iocp_work(); |
||||||
|
} |
||||||
|
|
||||||
|
gpr_event_set(&g_iocp_done, (void *)1); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_iocp_init(void) { |
||||||
|
gpr_thd_id id; |
||||||
|
|
||||||
|
g_iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, |
||||||
|
(ULONG_PTR)NULL, 0); |
||||||
|
GPR_ASSERT(g_iocp); |
||||||
|
|
||||||
|
gpr_event_init(&g_iocp_done); |
||||||
|
gpr_event_init(&g_shutdown_iocp); |
||||||
|
gpr_thd_new(&id, iocp_loop, NULL, NULL); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_iocp_shutdown(void) { |
||||||
|
BOOL success; |
||||||
|
gpr_event_set(&g_shutdown_iocp, (void *)1); |
||||||
|
success = PostQueuedCompletionStatus(g_iocp, 0, |
||||||
|
(ULONG_PTR) &g_iocp_kick_token, |
||||||
|
&g_iocp_custom_overlap); |
||||||
|
GPR_ASSERT(success); |
||||||
|
gpr_event_wait(&g_iocp_done, gpr_inf_future); |
||||||
|
success = CloseHandle(g_iocp); |
||||||
|
GPR_ASSERT(success); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_iocp_add_socket(grpc_winsocket *socket) { |
||||||
|
HANDLE ret; |
||||||
|
if (socket->added_to_iocp) return; |
||||||
|
ret = CreateIoCompletionPort((HANDLE)socket->socket, |
||||||
|
g_iocp, (gpr_uintptr) socket, 0); |
||||||
|
if (!ret) { |
||||||
|
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||||
|
gpr_log(GPR_ERROR, "Unable to add socket to iocp: %s", utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
__debugbreak(); |
||||||
|
abort(); |
||||||
|
} |
||||||
|
socket->added_to_iocp = 1; |
||||||
|
GPR_ASSERT(ret == g_iocp); |
||||||
|
} |
||||||
|
|
||||||
|
static void socket_notify_on_iocp(grpc_winsocket *socket, |
||||||
|
void(*cb)(void *, int), void *opaque, |
||||||
|
grpc_winsocket_callback_info *info) { |
||||||
|
int run_now = 0; |
||||||
|
GPR_ASSERT(!info->cb); |
||||||
|
gpr_mu_lock(&socket->state_mu); |
||||||
|
if (info->has_pending_iocp) { |
||||||
|
run_now = 1; |
||||||
|
info->has_pending_iocp = 0; |
||||||
|
gpr_log(GPR_DEBUG, "socket_notify_on_iocp - runs now"); |
||||||
|
} else { |
||||||
|
info->cb = cb; |
||||||
|
info->opaque = opaque; |
||||||
|
gpr_log(GPR_DEBUG, "socket_notify_on_iocp - queued"); |
||||||
|
} |
||||||
|
gpr_mu_unlock(&socket->state_mu); |
||||||
|
if (run_now) cb(opaque, 1); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_socket_notify_on_write(grpc_winsocket *socket, |
||||||
|
void(*cb)(void *, int), void *opaque) { |
||||||
|
gpr_log(GPR_DEBUG, "grpc_socket_notify_on_write"); |
||||||
|
socket_notify_on_iocp(socket, cb, opaque, &socket->write_info); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_socket_notify_on_read(grpc_winsocket *socket, |
||||||
|
void(*cb)(void *, int), void *opaque) { |
||||||
|
gpr_log(GPR_DEBUG, "grpc_socket_notify_on_read"); |
||||||
|
socket_notify_on_iocp(socket, cb, opaque, &socket->read_info); |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* GPR_WINSOCK_SOCKET */ |
@ -0,0 +1,52 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __GRPC_INTERNAL_IOMGR_IOCP_WINDOWS_H_ |
||||||
|
#define __GRPC_INTERNAL_IOMGR_IOCP_WINDOWS_H_ |
||||||
|
|
||||||
|
#include <windows.h> |
||||||
|
#include <grpc/support/sync.h> |
||||||
|
|
||||||
|
#include "src/core/iomgr/socket_windows.h" |
||||||
|
|
||||||
|
void grpc_iocp_init(void); |
||||||
|
void grpc_iocp_shutdown(void); |
||||||
|
void grpc_iocp_add_socket(grpc_winsocket *); |
||||||
|
|
||||||
|
void grpc_socket_notify_on_write(grpc_winsocket *, void(*cb)(void *, int success), |
||||||
|
void *opaque); |
||||||
|
|
||||||
|
void grpc_socket_notify_on_read(grpc_winsocket *, void(*cb)(void *, int success), |
||||||
|
void *opaque); |
||||||
|
|
||||||
|
#endif /* __GRPC_INTERNAL_IOMGR_IOCP_WINDOWS_H_ */ |
@ -0,0 +1,67 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#ifdef GPR_WINSOCK_SOCKET |
||||||
|
|
||||||
|
#include "src/core/iomgr/sockaddr_win32.h" |
||||||
|
|
||||||
|
#include <grpc/support/log.h> |
||||||
|
|
||||||
|
#include "src/core/iomgr/socket_windows.h" |
||||||
|
#include "src/core/iomgr/iocp_windows.h" |
||||||
|
#include "src/core/iomgr/iomgr.h" |
||||||
|
|
||||||
|
static void winsock_init(void) { |
||||||
|
WSADATA wsaData; |
||||||
|
int status = WSAStartup(MAKEWORD(2, 0), &wsaData); |
||||||
|
GPR_ASSERT(status == 0); |
||||||
|
} |
||||||
|
|
||||||
|
static void winsock_shutdown(void) { |
||||||
|
int status = WSACleanup(); |
||||||
|
GPR_ASSERT(status == 0); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_iomgr_platform_init(void) { |
||||||
|
winsock_init(); |
||||||
|
grpc_iocp_init(); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_iomgr_platform_shutdown(void) { |
||||||
|
grpc_iocp_shutdown(); |
||||||
|
winsock_shutdown(); |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* GRPC_WINSOCK_SOCKET */ |
@ -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 */ |
@ -0,0 +1,77 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* 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> |
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
|
||||||
|
#ifdef GPR_WINSOCK_SOCKET |
||||||
|
|
||||||
|
#include "src/core/iomgr/iocp_windows.h" |
||||||
|
#include "src/core/iomgr/iomgr.h" |
||||||
|
#include "src/core/iomgr/iomgr_internal.h" |
||||||
|
#include "src/core/iomgr/socket_windows.h" |
||||||
|
#include "src/core/iomgr/pollset.h" |
||||||
|
#include "src/core/iomgr/pollset_windows.h" |
||||||
|
|
||||||
|
grpc_winsocket *grpc_winsocket_create(SOCKET socket) { |
||||||
|
grpc_winsocket *r = gpr_malloc(sizeof(grpc_winsocket)); |
||||||
|
gpr_log(GPR_DEBUG, "grpc_winsocket_create"); |
||||||
|
memset(r, 0, sizeof(grpc_winsocket)); |
||||||
|
r->socket = socket; |
||||||
|
gpr_mu_init(&r->state_mu); |
||||||
|
grpc_iomgr_ref(); |
||||||
|
grpc_iocp_add_socket(r); |
||||||
|
return r; |
||||||
|
} |
||||||
|
|
||||||
|
void shutdown_op(grpc_winsocket_callback_info *info) { |
||||||
|
if (!info->cb) return; |
||||||
|
grpc_iomgr_add_delayed_callback(info->cb, info->opaque, 0); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_winsocket_shutdown(grpc_winsocket *socket) { |
||||||
|
gpr_log(GPR_DEBUG, "grpc_winsocket_shutdown"); |
||||||
|
shutdown_op(&socket->read_info); |
||||||
|
shutdown_op(&socket->write_info); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_winsocket_orphan(grpc_winsocket *socket) { |
||||||
|
gpr_log(GPR_DEBUG, "grpc_winsocket_orphan"); |
||||||
|
grpc_iomgr_unref(); |
||||||
|
closesocket(socket->socket); |
||||||
|
gpr_mu_destroy(&socket->state_mu); |
||||||
|
gpr_free(socket); |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* GPR_WINSOCK_SOCKET */ |
@ -0,0 +1,75 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __GRPC_INTERNAL_IOMGR_HANDLE_WINDOWS_H__ |
||||||
|
#define __GRPC_INTERNAL_IOMGR_HANDLE_WINDOWS_H__ |
||||||
|
|
||||||
|
#include <windows.h> |
||||||
|
|
||||||
|
#include <grpc/support/sync.h> |
||||||
|
#include <grpc/support/atm.h> |
||||||
|
|
||||||
|
typedef struct grpc_winsocket_callback_info { |
||||||
|
/* This is supposed to be a WSAOVERLAPPED, but in order to get that
|
||||||
|
* definition, we need to include ws2tcpip.h, which needs to be included |
||||||
|
* from the top, otherwise it'll clash with a previous inclusion of |
||||||
|
* windows.h that in turns includes winsock.h. If anyone knows a way |
||||||
|
* to do it properly, feel free to send a patch. |
||||||
|
*/ |
||||||
|
OVERLAPPED overlapped; |
||||||
|
void(*cb)(void *opaque, int success); |
||||||
|
void *opaque; |
||||||
|
int has_pending_iocp; |
||||||
|
DWORD bytes_transfered; |
||||||
|
int wsa_error; |
||||||
|
} grpc_winsocket_callback_info; |
||||||
|
|
||||||
|
typedef struct grpc_winsocket { |
||||||
|
SOCKET socket; |
||||||
|
|
||||||
|
int added_to_iocp; |
||||||
|
|
||||||
|
grpc_winsocket_callback_info write_info; |
||||||
|
grpc_winsocket_callback_info read_info; |
||||||
|
|
||||||
|
gpr_mu state_mu; |
||||||
|
} grpc_winsocket; |
||||||
|
|
||||||
|
/* Create a wrapped windows handle.
|
||||||
|
This takes ownership of closing it. */ |
||||||
|
grpc_winsocket *grpc_winsocket_create(SOCKET socket); |
||||||
|
|
||||||
|
void grpc_winsocket_shutdown(grpc_winsocket *socket); |
||||||
|
void grpc_winsocket_orphan(grpc_winsocket *socket); |
||||||
|
|
||||||
|
#endif /* __GRPC_INTERNAL_IOMGR_HANDLE_WINDOWS_H__ */ |
@ -0,0 +1,215 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#ifdef GPR_WINSOCK_SOCKET |
||||||
|
|
||||||
|
#include "src/core/iomgr/sockaddr_win32.h" |
||||||
|
|
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
#include <grpc/support/log_win32.h> |
||||||
|
#include <grpc/support/slice_buffer.h> |
||||||
|
#include <grpc/support/useful.h> |
||||||
|
|
||||||
|
#include "src/core/iomgr/tcp_client.h" |
||||||
|
#include "src/core/iomgr/tcp_windows.h" |
||||||
|
#include "src/core/iomgr/socket_windows.h" |
||||||
|
#include "src/core/iomgr/alarm.h" |
||||||
|
#include "src/core/iomgr/sockaddr.h" |
||||||
|
#include "src/core/iomgr/sockaddr_utils.h" |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
void(*cb)(void *arg, grpc_endpoint *tcp); |
||||||
|
void *cb_arg; |
||||||
|
gpr_mu mu; |
||||||
|
grpc_winsocket *socket; |
||||||
|
gpr_timespec deadline; |
||||||
|
grpc_alarm alarm; |
||||||
|
int refs; |
||||||
|
} async_connect; |
||||||
|
|
||||||
|
static void async_connect_cleanup(async_connect *ac) { |
||||||
|
int done = (--ac->refs == 0); |
||||||
|
gpr_mu_unlock(&ac->mu); |
||||||
|
if (done) { |
||||||
|
gpr_mu_destroy(&ac->mu); |
||||||
|
gpr_free(ac); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void on_alarm(void *acp, int success) { |
||||||
|
async_connect *ac = acp; |
||||||
|
gpr_mu_lock(&ac->mu); |
||||||
|
if (ac->socket != NULL && success) { |
||||||
|
grpc_winsocket_shutdown(ac->socket); |
||||||
|
} |
||||||
|
async_connect_cleanup(ac); |
||||||
|
} |
||||||
|
|
||||||
|
static void on_connect(void *acp, int success) { |
||||||
|
async_connect *ac = acp; |
||||||
|
SOCKET sock = ac->socket->socket; |
||||||
|
grpc_endpoint *ep = NULL; |
||||||
|
grpc_winsocket_callback_info *info = &ac->socket->write_info; |
||||||
|
void(*cb)(void *arg, grpc_endpoint *tcp) = ac->cb; |
||||||
|
void *cb_arg = ac->cb_arg; |
||||||
|
|
||||||
|
grpc_alarm_cancel(&ac->alarm); |
||||||
|
|
||||||
|
if (success) { |
||||||
|
DWORD transfered_bytes = 0; |
||||||
|
DWORD flags; |
||||||
|
BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped, |
||||||
|
&transfered_bytes, FALSE, |
||||||
|
&flags); |
||||||
|
GPR_ASSERT(transfered_bytes == 0); |
||||||
|
if (!wsa_success) { |
||||||
|
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||||
|
gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
goto finish; |
||||||
|
} else { |
||||||
|
gpr_log(GPR_DEBUG, "on_connect: connection established"); |
||||||
|
ep = grpc_tcp_create(ac->socket); |
||||||
|
goto finish; |
||||||
|
} |
||||||
|
} else { |
||||||
|
gpr_log(GPR_ERROR, "on_connect is shutting down"); |
||||||
|
goto finish; |
||||||
|
} |
||||||
|
|
||||||
|
abort(); |
||||||
|
|
||||||
|
finish: |
||||||
|
gpr_mu_lock(&ac->mu); |
||||||
|
if (!ep) { |
||||||
|
grpc_winsocket_orphan(ac->socket); |
||||||
|
} |
||||||
|
async_connect_cleanup(ac); |
||||||
|
cb(cb_arg, ep); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_tcp_client_connect(void(*cb)(void *arg, grpc_endpoint *tcp), |
||||||
|
void *arg, const struct sockaddr *addr, |
||||||
|
int addr_len, gpr_timespec deadline) { |
||||||
|
SOCKET sock = INVALID_SOCKET; |
||||||
|
BOOL success; |
||||||
|
int status; |
||||||
|
struct sockaddr_in6 addr6_v4mapped; |
||||||
|
struct sockaddr_in6 local_address; |
||||||
|
async_connect *ac; |
||||||
|
grpc_winsocket *socket = NULL; |
||||||
|
LPFN_CONNECTEX ConnectEx; |
||||||
|
GUID guid = WSAID_CONNECTEX; |
||||||
|
DWORD ioctl_num_bytes; |
||||||
|
const char *message = NULL; |
||||||
|
char *utf8_message; |
||||||
|
grpc_winsocket_callback_info *info; |
||||||
|
|
||||||
|
/* Use dualstack sockets where available. */ |
||||||
|
if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { |
||||||
|
addr = (const struct sockaddr *)&addr6_v4mapped; |
||||||
|
addr_len = sizeof(addr6_v4mapped); |
||||||
|
} |
||||||
|
|
||||||
|
sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, |
||||||
|
WSA_FLAG_OVERLAPPED); |
||||||
|
if (sock == INVALID_SOCKET) { |
||||||
|
message = "Unable to create socket: %s"; |
||||||
|
goto failure; |
||||||
|
} |
||||||
|
|
||||||
|
if (!grpc_tcp_prepare_socket(sock)) { |
||||||
|
message = "Unable to set socket options: %s"; |
||||||
|
goto failure; |
||||||
|
} |
||||||
|
|
||||||
|
status = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, |
||||||
|
&guid, sizeof(guid), &ConnectEx, sizeof(ConnectEx), |
||||||
|
&ioctl_num_bytes, NULL, NULL); |
||||||
|
|
||||||
|
if (status != 0) { |
||||||
|
message = "Unable to retreive ConnectEx pointer: %s"; |
||||||
|
goto failure; |
||||||
|
} |
||||||
|
|
||||||
|
grpc_sockaddr_make_wildcard6(0, &local_address); |
||||||
|
|
||||||
|
status = bind(sock, (struct sockaddr *) &local_address, |
||||||
|
sizeof(local_address)); |
||||||
|
if (status != 0) { |
||||||
|
message = "Unable to bind socket: %s"; |
||||||
|
goto failure; |
||||||
|
} |
||||||
|
|
||||||
|
socket = grpc_winsocket_create(sock); |
||||||
|
info = &socket->write_info; |
||||||
|
success = ConnectEx(sock, addr, addr_len, NULL, 0, NULL, &info->overlapped); |
||||||
|
|
||||||
|
if (success) { |
||||||
|
gpr_log(GPR_DEBUG, "connected immediately - but we still go to sleep"); |
||||||
|
} else { |
||||||
|
int error = WSAGetLastError(); |
||||||
|
if (error != ERROR_IO_PENDING) { |
||||||
|
message = "ConnectEx failed: %s"; |
||||||
|
goto failure; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
gpr_log(GPR_DEBUG, "grpc_tcp_client_connect: connection pending"); |
||||||
|
ac = gpr_malloc(sizeof(async_connect)); |
||||||
|
ac->cb = cb; |
||||||
|
ac->cb_arg = arg; |
||||||
|
ac->socket = socket; |
||||||
|
gpr_mu_init(&ac->mu); |
||||||
|
ac->refs = 2; |
||||||
|
|
||||||
|
grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac, gpr_now()); |
||||||
|
grpc_socket_notify_on_write(socket, on_connect, ac); |
||||||
|
return; |
||||||
|
|
||||||
|
failure: |
||||||
|
utf8_message = gpr_format_message(WSAGetLastError()); |
||||||
|
gpr_log(GPR_ERROR, message, utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
if (socket) { |
||||||
|
grpc_winsocket_orphan(socket); |
||||||
|
} else if (sock != INVALID_SOCKET) { |
||||||
|
closesocket(sock); |
||||||
|
} |
||||||
|
cb(arg, NULL); |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* GPR_WINSOCK_SOCKET */ |
@ -0,0 +1,374 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* 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_WINSOCK_SOCKET |
||||||
|
|
||||||
|
#define _GNU_SOURCE |
||||||
|
#include "src/core/iomgr/sockaddr_utils.h" |
||||||
|
|
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
#include <grpc/support/log_win32.h> |
||||||
|
#include <grpc/support/sync.h> |
||||||
|
#include <grpc/support/time.h> |
||||||
|
|
||||||
|
#include "src/core/iomgr/iocp_windows.h" |
||||||
|
#include "src/core/iomgr/pollset_windows.h" |
||||||
|
#include "src/core/iomgr/socket_windows.h" |
||||||
|
#include "src/core/iomgr/tcp_server.h" |
||||||
|
#include "src/core/iomgr/tcp_windows.h" |
||||||
|
|
||||||
|
#define INIT_PORT_CAP 2 |
||||||
|
#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100 |
||||||
|
|
||||||
|
static gpr_once s_init_max_accept_queue_size; |
||||||
|
static int s_max_accept_queue_size; |
||||||
|
|
||||||
|
/* one listening port */ |
||||||
|
typedef struct server_port { |
||||||
|
gpr_uint8 addresses[sizeof(struct sockaddr_in6) * 2 + 32]; |
||||||
|
SOCKET new_socket; |
||||||
|
grpc_winsocket *socket; |
||||||
|
grpc_tcp_server *server; |
||||||
|
LPFN_ACCEPTEX AcceptEx; |
||||||
|
} server_port; |
||||||
|
|
||||||
|
/* the overall server */ |
||||||
|
struct grpc_tcp_server { |
||||||
|
grpc_tcp_server_cb cb; |
||||||
|
void *cb_arg; |
||||||
|
|
||||||
|
gpr_mu mu; |
||||||
|
gpr_cv cv; |
||||||
|
|
||||||
|
/* active port count: how many ports are actually still listening */ |
||||||
|
int active_ports; |
||||||
|
|
||||||
|
/* all listening ports */ |
||||||
|
server_port *ports; |
||||||
|
size_t nports; |
||||||
|
size_t port_capacity; |
||||||
|
}; |
||||||
|
|
||||||
|
grpc_tcp_server *grpc_tcp_server_create(void) { |
||||||
|
grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server)); |
||||||
|
gpr_mu_init(&s->mu); |
||||||
|
gpr_cv_init(&s->cv); |
||||||
|
s->active_ports = 0; |
||||||
|
s->cb = NULL; |
||||||
|
s->cb_arg = NULL; |
||||||
|
s->ports = gpr_malloc(sizeof(server_port) * INIT_PORT_CAP); |
||||||
|
s->nports = 0; |
||||||
|
s->port_capacity = INIT_PORT_CAP; |
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_tcp_server_destroy(grpc_tcp_server *s) { |
||||||
|
size_t i; |
||||||
|
gpr_mu_lock(&s->mu); |
||||||
|
/* shutdown all fd's */ |
||||||
|
for (i = 0; i < s->nports; i++) { |
||||||
|
grpc_winsocket_shutdown(s->ports[i].socket); |
||||||
|
} |
||||||
|
/* wait while that happens */ |
||||||
|
while (s->active_ports) { |
||||||
|
gpr_cv_wait(&s->cv, &s->mu, gpr_inf_future); |
||||||
|
} |
||||||
|
gpr_mu_unlock(&s->mu); |
||||||
|
|
||||||
|
/* delete ALL the things */ |
||||||
|
for (i = 0; i < s->nports; i++) { |
||||||
|
server_port *sp = &s->ports[i]; |
||||||
|
grpc_winsocket_orphan(sp->socket); |
||||||
|
} |
||||||
|
gpr_free(s->ports); |
||||||
|
gpr_free(s); |
||||||
|
} |
||||||
|
|
||||||
|
/* Prepare a recently-created socket for listening. */ |
||||||
|
static int prepare_socket(SOCKET sock, |
||||||
|
const struct sockaddr *addr, int addr_len) { |
||||||
|
struct sockaddr_storage sockname_temp; |
||||||
|
socklen_t sockname_len; |
||||||
|
|
||||||
|
if (sock == INVALID_SOCKET) goto error; |
||||||
|
|
||||||
|
if (!grpc_tcp_prepare_socket(sock)) { |
||||||
|
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||||
|
gpr_log(GPR_ERROR, "Unable to prepare socket: %s", utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
goto error; |
||||||
|
} |
||||||
|
|
||||||
|
if (bind(sock, addr, addr_len) == SOCKET_ERROR) { |
||||||
|
char *addr_str; |
||||||
|
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||||
|
grpc_sockaddr_to_string(&addr_str, addr, 0); |
||||||
|
gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
gpr_free(addr_str); |
||||||
|
goto error; |
||||||
|
} |
||||||
|
|
||||||
|
if (listen(sock, SOMAXCONN) == SOCKET_ERROR) { |
||||||
|
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||||
|
gpr_log(GPR_ERROR, "listen: %s", utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
goto error; |
||||||
|
} |
||||||
|
|
||||||
|
sockname_len = sizeof(sockname_temp); |
||||||
|
if (getsockname(sock, (struct sockaddr *) &sockname_temp, &sockname_len) |
||||||
|
== SOCKET_ERROR) { |
||||||
|
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||||
|
gpr_log(GPR_ERROR, "getsockname: %s", utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
goto error; |
||||||
|
} |
||||||
|
|
||||||
|
return grpc_sockaddr_get_port((struct sockaddr *) &sockname_temp); |
||||||
|
|
||||||
|
error: |
||||||
|
if (sock != INVALID_SOCKET) closesocket(sock); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
static void on_accept(void *arg, int success); |
||||||
|
|
||||||
|
static void start_accept(server_port *port) { |
||||||
|
SOCKET sock = INVALID_SOCKET; |
||||||
|
char *message; |
||||||
|
char *utf8_message; |
||||||
|
BOOL success; |
||||||
|
DWORD addrlen = sizeof(struct sockaddr_in6) + 16; |
||||||
|
DWORD bytes_received = 0; |
||||||
|
|
||||||
|
sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, |
||||||
|
WSA_FLAG_OVERLAPPED); |
||||||
|
|
||||||
|
if (sock == INVALID_SOCKET) { |
||||||
|
message = "Unable to create socket: %s"; |
||||||
|
goto failure; |
||||||
|
} |
||||||
|
|
||||||
|
if (!grpc_tcp_prepare_socket(sock)) { |
||||||
|
message = "Unable to prepare socket: %s"; |
||||||
|
goto failure; |
||||||
|
} |
||||||
|
|
||||||
|
success = port->AcceptEx(port->socket->socket, sock, port->addresses, 0, |
||||||
|
addrlen, addrlen, &bytes_received, |
||||||
|
&port->socket->read_info.overlapped); |
||||||
|
|
||||||
|
if (success) { |
||||||
|
gpr_log(GPR_DEBUG, "accepted immediately - but we still go to sleep"); |
||||||
|
} else { |
||||||
|
int error = WSAGetLastError(); |
||||||
|
if (error != ERROR_IO_PENDING) { |
||||||
|
message = "AcceptEx failed: %s"; |
||||||
|
goto failure; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
port->new_socket = sock; |
||||||
|
grpc_socket_notify_on_read(port->socket, on_accept, port); |
||||||
|
return; |
||||||
|
|
||||||
|
failure: |
||||||
|
utf8_message = gpr_format_message(WSAGetLastError()); |
||||||
|
gpr_log(GPR_ERROR, message, utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
if (sock != INVALID_SOCKET) closesocket(sock); |
||||||
|
} |
||||||
|
|
||||||
|
/* event manager callback when reads are ready */ |
||||||
|
static void on_accept(void *arg, int success) { |
||||||
|
server_port *sp = arg; |
||||||
|
SOCKET sock = sp->new_socket; |
||||||
|
grpc_winsocket_callback_info *info = &sp->socket->read_info; |
||||||
|
grpc_endpoint *ep = NULL; |
||||||
|
|
||||||
|
if (success) { |
||||||
|
DWORD transfered_bytes = 0; |
||||||
|
DWORD flags; |
||||||
|
BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped, |
||||||
|
&transfered_bytes, FALSE, |
||||||
|
&flags); |
||||||
|
if (!wsa_success) { |
||||||
|
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||||
|
gpr_log(GPR_ERROR, "on_accept error: %s", utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
closesocket(sock); |
||||||
|
} else { |
||||||
|
gpr_log(GPR_DEBUG, "on_accept: accepted connection"); |
||||||
|
ep = grpc_tcp_create(grpc_winsocket_create(sock)); |
||||||
|
} |
||||||
|
} else { |
||||||
|
gpr_log(GPR_DEBUG, "on_accept: shutting down"); |
||||||
|
closesocket(sock); |
||||||
|
gpr_mu_lock(&sp->server->mu); |
||||||
|
if (0 == --sp->server->active_ports) { |
||||||
|
gpr_cv_broadcast(&sp->server->cv); |
||||||
|
} |
||||||
|
gpr_mu_unlock(&sp->server->mu); |
||||||
|
} |
||||||
|
|
||||||
|
if (ep) sp->server->cb(sp->server->cb_arg, ep); |
||||||
|
start_accept(sp); |
||||||
|
} |
||||||
|
|
||||||
|
static int add_socket_to_server(grpc_tcp_server *s, SOCKET sock, |
||||||
|
const struct sockaddr *addr, int addr_len) { |
||||||
|
server_port *sp; |
||||||
|
int port; |
||||||
|
int status; |
||||||
|
GUID guid = WSAID_ACCEPTEX; |
||||||
|
DWORD ioctl_num_bytes; |
||||||
|
LPFN_ACCEPTEX AcceptEx; |
||||||
|
|
||||||
|
if (sock == INVALID_SOCKET) return -1; |
||||||
|
|
||||||
|
status = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, |
||||||
|
&guid, sizeof(guid), &AcceptEx, sizeof(AcceptEx), |
||||||
|
&ioctl_num_bytes, NULL, NULL); |
||||||
|
|
||||||
|
if (status != 0) { |
||||||
|
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||||
|
gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
closesocket(sock); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
port = prepare_socket(sock, addr, addr_len); |
||||||
|
if (port >= 0) { |
||||||
|
gpr_mu_lock(&s->mu); |
||||||
|
GPR_ASSERT(!s->cb && "must add ports before starting server"); |
||||||
|
/* append it to the list under a lock */ |
||||||
|
if (s->nports == s->port_capacity) { |
||||||
|
s->port_capacity *= 2; |
||||||
|
s->ports = gpr_realloc(s->ports, sizeof(server_port) * s->port_capacity); |
||||||
|
} |
||||||
|
sp = &s->ports[s->nports++]; |
||||||
|
sp->server = s; |
||||||
|
sp->socket = grpc_winsocket_create(sock); |
||||||
|
sp->AcceptEx = AcceptEx; |
||||||
|
GPR_ASSERT(sp->socket); |
||||||
|
gpr_mu_unlock(&s->mu); |
||||||
|
} |
||||||
|
|
||||||
|
return port; |
||||||
|
} |
||||||
|
|
||||||
|
int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, |
||||||
|
int addr_len) { |
||||||
|
int allocated_port = -1; |
||||||
|
unsigned i; |
||||||
|
SOCKET sock; |
||||||
|
struct sockaddr_in6 addr6_v4mapped; |
||||||
|
struct sockaddr_in6 wildcard; |
||||||
|
struct sockaddr *allocated_addr = NULL; |
||||||
|
struct sockaddr_storage sockname_temp; |
||||||
|
socklen_t sockname_len; |
||||||
|
int port; |
||||||
|
|
||||||
|
/* 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) { |
||||||
|
for (i = 0; i < s->nports; i++) { |
||||||
|
sockname_len = sizeof(sockname_temp); |
||||||
|
if (0 == getsockname(s->ports[i].socket->socket, |
||||||
|
(struct sockaddr *) &sockname_temp, |
||||||
|
&sockname_len)) { |
||||||
|
port = grpc_sockaddr_get_port((struct sockaddr *) &sockname_temp); |
||||||
|
if (port > 0) { |
||||||
|
allocated_addr = malloc(addr_len); |
||||||
|
memcpy(allocated_addr, addr, addr_len); |
||||||
|
grpc_sockaddr_set_port(allocated_addr, port); |
||||||
|
addr = allocated_addr; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { |
||||||
|
addr = (const struct sockaddr *)&addr6_v4mapped; |
||||||
|
addr_len = sizeof(addr6_v4mapped); |
||||||
|
} |
||||||
|
|
||||||
|
/* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ |
||||||
|
if (grpc_sockaddr_is_wildcard(addr, &port)) { |
||||||
|
grpc_sockaddr_make_wildcard6(port, &wildcard); |
||||||
|
|
||||||
|
addr = (struct sockaddr *) &wildcard; |
||||||
|
addr_len = sizeof(wildcard); |
||||||
|
} |
||||||
|
|
||||||
|
sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, |
||||||
|
WSA_FLAG_OVERLAPPED); |
||||||
|
if (sock == INVALID_SOCKET) { |
||||||
|
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||||
|
gpr_log(GPR_ERROR, "unable to create socket: %s", utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
} |
||||||
|
|
||||||
|
allocated_port = add_socket_to_server(s, sock, addr, addr_len); |
||||||
|
gpr_free(allocated_addr); |
||||||
|
|
||||||
|
return allocated_port; |
||||||
|
} |
||||||
|
|
||||||
|
SOCKET grpc_tcp_server_get_socket(grpc_tcp_server *s, unsigned index) { |
||||||
|
return (index < s->nports) ? s->ports[index].socket->socket : INVALID_SOCKET; |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_tcp_server_start(grpc_tcp_server *s, grpc_pollset *pollset, |
||||||
|
grpc_tcp_server_cb cb, void *cb_arg) { |
||||||
|
size_t i; |
||||||
|
GPR_ASSERT(cb); |
||||||
|
gpr_mu_lock(&s->mu); |
||||||
|
GPR_ASSERT(!s->cb); |
||||||
|
GPR_ASSERT(s->active_ports == 0); |
||||||
|
s->cb = cb; |
||||||
|
s->cb_arg = cb_arg; |
||||||
|
for (i = 0; i < s->nports; i++) { |
||||||
|
start_accept(s->ports + i); |
||||||
|
s->active_ports++; |
||||||
|
} |
||||||
|
gpr_mu_unlock(&s->mu); |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* GPR_WINSOCK_SOCKET */ |
@ -0,0 +1,373 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* 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_WINSOCK_SOCKET |
||||||
|
|
||||||
|
#include "src/core/iomgr/sockaddr_win32.h" |
||||||
|
|
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
#include <grpc/support/log.h> |
||||||
|
#include <grpc/support/log_win32.h> |
||||||
|
#include <grpc/support/slice_buffer.h> |
||||||
|
#include <grpc/support/useful.h> |
||||||
|
|
||||||
|
#include "src/core/iomgr/alarm.h" |
||||||
|
#include "src/core/iomgr/iocp_windows.h" |
||||||
|
#include "src/core/iomgr/sockaddr.h" |
||||||
|
#include "src/core/iomgr/sockaddr_utils.h" |
||||||
|
#include "src/core/iomgr/socket_windows.h" |
||||||
|
#include "src/core/iomgr/tcp_client.h" |
||||||
|
|
||||||
|
static int set_non_block(SOCKET sock) { |
||||||
|
int status; |
||||||
|
unsigned long param = 1; |
||||||
|
DWORD ret; |
||||||
|
status = WSAIoctl(sock, FIONBIO, ¶m, sizeof(param), NULL, 0, &ret, |
||||||
|
NULL, NULL); |
||||||
|
return status == 0; |
||||||
|
} |
||||||
|
|
||||||
|
static int set_dualstack(SOCKET sock) { |
||||||
|
int status; |
||||||
|
unsigned long param = 0; |
||||||
|
status = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, |
||||||
|
(const char *) ¶m, sizeof(param)); |
||||||
|
return status == 0; |
||||||
|
} |
||||||
|
|
||||||
|
int grpc_tcp_prepare_socket(SOCKET sock) { |
||||||
|
if (!set_non_block(sock)) |
||||||
|
return 0; |
||||||
|
if (!set_dualstack(sock)) |
||||||
|
return 0; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
typedef struct grpc_tcp { |
||||||
|
grpc_endpoint base; |
||||||
|
grpc_winsocket *socket; |
||||||
|
gpr_refcount refcount; |
||||||
|
|
||||||
|
grpc_endpoint_read_cb read_cb; |
||||||
|
void *read_user_data; |
||||||
|
gpr_slice read_slice; |
||||||
|
int outstanding_read; |
||||||
|
|
||||||
|
grpc_endpoint_write_cb write_cb; |
||||||
|
void *write_user_data; |
||||||
|
gpr_slice_buffer write_slices; |
||||||
|
int outstanding_write; |
||||||
|
|
||||||
|
} grpc_tcp; |
||||||
|
|
||||||
|
static void tcp_ref(grpc_tcp *tcp) { |
||||||
|
gpr_log(GPR_DEBUG, "tcp_ref"); |
||||||
|
gpr_ref(&tcp->refcount); |
||||||
|
} |
||||||
|
|
||||||
|
static void tcp_unref(grpc_tcp *tcp) { |
||||||
|
gpr_log(GPR_DEBUG, "tcp_unref"); |
||||||
|
if (gpr_unref(&tcp->refcount)) { |
||||||
|
gpr_log(GPR_DEBUG, "tcp_unref: destroying"); |
||||||
|
gpr_slice_buffer_destroy(&tcp->write_slices); |
||||||
|
grpc_winsocket_orphan(tcp->socket); |
||||||
|
gpr_free(tcp); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void on_read(void *tcpp, int success) { |
||||||
|
grpc_tcp *tcp = (grpc_tcp *) tcpp; |
||||||
|
grpc_winsocket *socket = tcp->socket; |
||||||
|
gpr_slice sub; |
||||||
|
gpr_slice *slice = NULL; |
||||||
|
size_t nslices = 0; |
||||||
|
grpc_endpoint_cb_status status; |
||||||
|
grpc_endpoint_read_cb cb = tcp->read_cb; |
||||||
|
grpc_winsocket_callback_info *info = &socket->read_info; |
||||||
|
void *opaque = tcp->read_user_data; |
||||||
|
|
||||||
|
GPR_ASSERT(tcp->outstanding_read); |
||||||
|
|
||||||
|
if (!success) { |
||||||
|
tcp_unref(tcp); |
||||||
|
cb(opaque, NULL, 0, GRPC_ENDPOINT_CB_SHUTDOWN); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
gpr_log(GPR_DEBUG, "on_read"); |
||||||
|
tcp->outstanding_read = 0; |
||||||
|
|
||||||
|
if (socket->read_info.wsa_error != 0) { |
||||||
|
char *utf8_message = gpr_format_message(info->wsa_error); |
||||||
|
__debugbreak(); |
||||||
|
gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
status = GRPC_ENDPOINT_CB_ERROR; |
||||||
|
} else { |
||||||
|
if (info->bytes_transfered != 0) { |
||||||
|
sub = gpr_slice_sub(tcp->read_slice, 0, info->bytes_transfered); |
||||||
|
gpr_log(GPR_DEBUG, "on_read: calling callback"); |
||||||
|
status = GRPC_ENDPOINT_CB_OK; |
||||||
|
slice = ⊂ |
||||||
|
nslices = 1; |
||||||
|
} else { |
||||||
|
gpr_log(GPR_DEBUG, "on_read: closed socket"); |
||||||
|
gpr_slice_unref(tcp->read_slice); |
||||||
|
status = GRPC_ENDPOINT_CB_EOF; |
||||||
|
} |
||||||
|
} |
||||||
|
tcp_unref(tcp); |
||||||
|
cb(opaque, slice, nslices, status); |
||||||
|
} |
||||||
|
|
||||||
|
static void win_notify_on_read(grpc_endpoint *ep, |
||||||
|
grpc_endpoint_read_cb cb, void *arg) { |
||||||
|
grpc_tcp *tcp = (grpc_tcp *) ep; |
||||||
|
grpc_winsocket *handle = tcp->socket; |
||||||
|
grpc_winsocket_callback_info *info = &handle->read_info; |
||||||
|
int status; |
||||||
|
DWORD bytes_read = 0; |
||||||
|
DWORD flags = 0; |
||||||
|
int error; |
||||||
|
WSABUF buffer; |
||||||
|
|
||||||
|
GPR_ASSERT(!tcp->outstanding_read); |
||||||
|
tcp_ref(tcp); |
||||||
|
tcp->outstanding_read = 1; |
||||||
|
tcp->read_cb = cb; |
||||||
|
tcp->read_user_data = arg; |
||||||
|
|
||||||
|
tcp->read_slice = gpr_slice_malloc(8192); |
||||||
|
|
||||||
|
buffer.len = GPR_SLICE_LENGTH(tcp->read_slice); |
||||||
|
buffer.buf = GPR_SLICE_START_PTR(tcp->read_slice); |
||||||
|
|
||||||
|
gpr_log(GPR_DEBUG, "win_notify_on_read: calling WSARecv without overlap"); |
||||||
|
status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, |
||||||
|
NULL, NULL); |
||||||
|
info->wsa_error = status == 0 ? 0 : WSAGetLastError(); |
||||||
|
|
||||||
|
if (info->wsa_error != WSAEWOULDBLOCK) { |
||||||
|
gpr_log(GPR_DEBUG, "got response immediately, calling on_read"); |
||||||
|
info->bytes_transfered = bytes_read; |
||||||
|
/* This might heavily recurse. */ |
||||||
|
on_read(tcp, 1); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
gpr_log(GPR_DEBUG, "got WSAEWOULDBLOCK - calling WSARecv with overlap"); |
||||||
|
|
||||||
|
memset(&tcp->socket->read_info.overlapped, 0, sizeof(OVERLAPPED)); |
||||||
|
status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, |
||||||
|
&info->overlapped, NULL); |
||||||
|
|
||||||
|
if (status == 0) { |
||||||
|
gpr_log(GPR_DEBUG, "got response immediately, but we're going to sleep"); |
||||||
|
grpc_socket_notify_on_read(tcp->socket, on_read, tcp); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
error = WSAGetLastError(); |
||||||
|
|
||||||
|
if (error != WSA_IO_PENDING) { |
||||||
|
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||||
|
__debugbreak(); |
||||||
|
gpr_log(GPR_ERROR, "WSARecv error: %s", utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
/* would the IO completion port be called anyway... ? Let's assume not. */ |
||||||
|
tcp->outstanding_read = 0; |
||||||
|
tcp_unref(tcp); |
||||||
|
cb(arg, NULL, 0, GRPC_ENDPOINT_CB_ERROR); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
gpr_log(GPR_DEBUG, "waiting on the IO completion port now"); |
||||||
|
grpc_socket_notify_on_read(tcp->socket, on_read, tcp); |
||||||
|
} |
||||||
|
|
||||||
|
static void on_write(void *tcpp, int success) { |
||||||
|
grpc_tcp *tcp = (grpc_tcp *) tcpp; |
||||||
|
grpc_winsocket *handle = tcp->socket; |
||||||
|
grpc_winsocket_callback_info *info = &handle->write_info; |
||||||
|
grpc_endpoint_cb_status status = GRPC_ENDPOINT_CB_OK; |
||||||
|
grpc_endpoint_write_cb cb = tcp->write_cb; |
||||||
|
void *opaque = tcp->write_user_data; |
||||||
|
|
||||||
|
GPR_ASSERT(tcp->outstanding_write); |
||||||
|
|
||||||
|
gpr_log(GPR_DEBUG, "on_write"); |
||||||
|
|
||||||
|
if (!success) { |
||||||
|
tcp_unref(tcp); |
||||||
|
cb(opaque, GRPC_ENDPOINT_CB_SHUTDOWN); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (info->wsa_error != 0) { |
||||||
|
char *utf8_message = gpr_format_message(info->wsa_error); |
||||||
|
gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
status = GRPC_ENDPOINT_CB_ERROR; |
||||||
|
} else { |
||||||
|
GPR_ASSERT(info->bytes_transfered == tcp->write_slices.length); |
||||||
|
} |
||||||
|
|
||||||
|
gpr_slice_buffer_reset_and_unref(&tcp->write_slices); |
||||||
|
tcp->outstanding_write = 0; |
||||||
|
|
||||||
|
tcp_unref(tcp); |
||||||
|
cb(opaque, status); |
||||||
|
} |
||||||
|
|
||||||
|
static grpc_endpoint_write_status win_write(grpc_endpoint *ep, |
||||||
|
gpr_slice *slices, size_t nslices, |
||||||
|
grpc_endpoint_write_cb cb, |
||||||
|
void *arg) { |
||||||
|
grpc_tcp *tcp = (grpc_tcp *) ep; |
||||||
|
grpc_winsocket *socket = tcp->socket; |
||||||
|
grpc_winsocket_callback_info *info = &socket->write_info; |
||||||
|
unsigned i; |
||||||
|
DWORD bytes_sent; |
||||||
|
int status; |
||||||
|
WSABUF local_buffers[16]; |
||||||
|
WSABUF *allocated = NULL; |
||||||
|
WSABUF *buffers = local_buffers; |
||||||
|
|
||||||
|
GPR_ASSERT(nslices != 0); |
||||||
|
GPR_ASSERT(GPR_SLICE_LENGTH(slices[0]) != 0); |
||||||
|
GPR_ASSERT(!tcp->outstanding_write); |
||||||
|
tcp_ref(tcp); |
||||||
|
|
||||||
|
gpr_log(GPR_DEBUG, "win_write"); |
||||||
|
|
||||||
|
tcp->outstanding_write = 1; |
||||||
|
tcp->write_cb = cb; |
||||||
|
tcp->write_user_data = arg; |
||||||
|
gpr_slice_buffer_addn(&tcp->write_slices, slices, nslices); |
||||||
|
|
||||||
|
if (tcp->write_slices.count > GPR_ARRAY_SIZE(local_buffers)) { |
||||||
|
buffers = (WSABUF *) gpr_malloc(sizeof(WSABUF) * tcp->write_slices.count); |
||||||
|
allocated = buffers; |
||||||
|
} |
||||||
|
|
||||||
|
for (i = 0; i < tcp->write_slices.count; i++) { |
||||||
|
buffers[i].len = GPR_SLICE_LENGTH(tcp->write_slices.slices[i]); |
||||||
|
buffers[i].buf = GPR_SLICE_START_PTR(tcp->write_slices.slices[i]); |
||||||
|
} |
||||||
|
|
||||||
|
gpr_log(GPR_DEBUG, "win_write: calling WSASend without overlap"); |
||||||
|
status = WSASend(socket->socket, buffers, tcp->write_slices.count, |
||||||
|
&bytes_sent, 0, NULL, NULL); |
||||||
|
info->wsa_error = status == 0 ? 0 : WSAGetLastError(); |
||||||
|
|
||||||
|
if (info->wsa_error != WSAEWOULDBLOCK) { |
||||||
|
grpc_endpoint_write_status ret = GRPC_ENDPOINT_WRITE_ERROR; |
||||||
|
gpr_log(GPR_DEBUG, "got response immediately, cleaning up and leaving"); |
||||||
|
if (status == 0) { |
||||||
|
ret = GRPC_ENDPOINT_WRITE_DONE; |
||||||
|
GPR_ASSERT(bytes_sent == tcp->write_slices.length); |
||||||
|
} else { |
||||||
|
char *utf8_message = gpr_format_message(info->wsa_error); |
||||||
|
gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
} |
||||||
|
if (allocated) gpr_free(allocated); |
||||||
|
gpr_slice_buffer_reset_and_unref(&tcp->write_slices); |
||||||
|
tcp->outstanding_write = 0; |
||||||
|
tcp_unref(tcp); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
gpr_log(GPR_DEBUG, "got WSAEWOULDBLOCK - calling WSASend with overlap"); |
||||||
|
|
||||||
|
memset(&socket->write_info, 0, sizeof(OVERLAPPED)); |
||||||
|
status = WSASend(socket->socket, buffers, tcp->write_slices.count, |
||||||
|
&bytes_sent, 0, &socket->write_info.overlapped, NULL); |
||||||
|
if (allocated) gpr_free(allocated); |
||||||
|
|
||||||
|
if (status != 0) { |
||||||
|
int error = WSAGetLastError(); |
||||||
|
if (error != WSA_IO_PENDING) { |
||||||
|
char *utf8_message = gpr_format_message(WSAGetLastError()); |
||||||
|
__debugbreak(); |
||||||
|
gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message); |
||||||
|
gpr_free(utf8_message); |
||||||
|
/* would the IO completion port be called anyway ? Let's assume not. */ |
||||||
|
tcp->outstanding_write = 0; |
||||||
|
tcp_unref(tcp); |
||||||
|
return GRPC_ENDPOINT_WRITE_ERROR; |
||||||
|
} |
||||||
|
gpr_log(GPR_DEBUG, "win_write: got pending op"); |
||||||
|
} else { |
||||||
|
gpr_log(GPR_DEBUG, "wrote data immediately - but we're going to sleep"); |
||||||
|
} |
||||||
|
|
||||||
|
grpc_socket_notify_on_write(socket, on_write, tcp); |
||||||
|
return GRPC_ENDPOINT_WRITE_PENDING; |
||||||
|
} |
||||||
|
|
||||||
|
static void win_add_to_pollset(grpc_endpoint *ep, grpc_pollset *pollset) { |
||||||
|
grpc_tcp *tcp = (grpc_tcp *) ep; |
||||||
|
gpr_log(GPR_DEBUG, "win_add_to_pollset"); |
||||||
|
grpc_iocp_add_socket(tcp->socket); |
||||||
|
} |
||||||
|
|
||||||
|
static void win_shutdown(grpc_endpoint *ep) { |
||||||
|
grpc_tcp *tcp = (grpc_tcp *) ep; |
||||||
|
gpr_log(GPR_DEBUG, "win_shutdown"); |
||||||
|
grpc_winsocket_shutdown(tcp->socket); |
||||||
|
} |
||||||
|
|
||||||
|
static void win_destroy(grpc_endpoint *ep) { |
||||||
|
grpc_tcp *tcp = (grpc_tcp *) ep; |
||||||
|
gpr_log(GPR_DEBUG, "win_destroy"); |
||||||
|
tcp_unref(tcp); |
||||||
|
} |
||||||
|
|
||||||
|
static grpc_endpoint_vtable vtable = { |
||||||
|
win_notify_on_read, win_write, win_add_to_pollset, win_shutdown, win_destroy |
||||||
|
}; |
||||||
|
|
||||||
|
grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket) { |
||||||
|
grpc_tcp *tcp = (grpc_tcp *) gpr_malloc(sizeof(grpc_tcp)); |
||||||
|
memset(tcp, 0, sizeof(grpc_tcp)); |
||||||
|
tcp->base.vtable = &vtable; |
||||||
|
tcp->socket = socket; |
||||||
|
gpr_slice_buffer_init(&tcp->write_slices); |
||||||
|
gpr_ref_init(&tcp->refcount, 1); |
||||||
|
return &tcp->base; |
||||||
|
} |
||||||
|
|
||||||
|
#endif /* GPR_WINSOCK_SOCKET */ |
@ -0,0 +1,57 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __GRPC_INTERNAL_IOMGR_TCP_WINDOWS_H__ |
||||||
|
#define __GRPC_INTERNAL_IOMGR_TCP_WINDOWS_H__ |
||||||
|
/*
|
||||||
|
Low level TCP "bottom half" implementation, for use by transports built on |
||||||
|
top of a TCP connection. |
||||||
|
|
||||||
|
Note that this file does not (yet) include APIs for creating the socket in |
||||||
|
the first place. |
||||||
|
|
||||||
|
All calls passing slice transfer ownership of a slice refcount unless |
||||||
|
otherwise specified. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "src/core/iomgr/endpoint.h" |
||||||
|
#include "src/core/iomgr/socket_windows.h" |
||||||
|
|
||||||
|
/* Create a tcp endpoint given a winsock handle.
|
||||||
|
* Takes ownership of the handle. |
||||||
|
*/ |
||||||
|
grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket); |
||||||
|
|
||||||
|
int grpc_tcp_prepare_socket(SOCKET sock); |
||||||
|
|
||||||
|
#endif /* __GRPC_INTERNAL_IOMGR_TCP_WINDOWS_H__ */ |
@ -0,0 +1,54 @@ |
|||||||
|
/*
|
||||||
|
* |
||||||
|
* Copyright 2014, Google Inc. |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* * Redistributions in binary form must reproduce the above |
||||||
|
* copyright notice, this list of conditions and the following disclaimer |
||||||
|
* in the documentation and/or other materials provided with the |
||||||
|
* distribution. |
||||||
|
* * Neither the name of Google Inc. nor the names of its |
||||||
|
* contributors may be used to endorse or promote products derived from |
||||||
|
* this software without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||||
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#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 */ |
@ -0,0 +1,13 @@ |
|||||||
|
#include <grpc/grpc.h> |
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
|
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
void grpc_call_details_init(grpc_call_details *cd) { |
||||||
|
memset(cd, 0, sizeof(*cd)); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_call_details_destroy(grpc_call_details *cd) { |
||||||
|
gpr_free(cd->method); |
||||||
|
gpr_free(cd->host); |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
#include <grpc/grpc.h> |
||||||
|
#include <grpc/support/alloc.h> |
||||||
|
|
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
void grpc_metadata_array_init(grpc_metadata_array *array) { |
||||||
|
memset(array, 0, sizeof(*array)); |
||||||
|
} |
||||||
|
|
||||||
|
void grpc_metadata_array_destroy(grpc_metadata_array *array) { |
||||||
|
gpr_free(array->metadata); |
||||||
|
} |
@ -1,2 +1,4 @@ |
|||||||
*.userprefs |
*.userprefs |
||||||
test-results |
test-results |
||||||
|
packages |
||||||
|
Grpc.v12.suo |
||||||
|
@ -1,2 +1,3 @@ |
|||||||
test-results |
test-results |
||||||
bin |
bin |
||||||
|
obj |
||||||
|
@ -0,0 +1,282 @@ |
|||||||
|
// Generated by ProtoGen, Version=2.4.1.521, Culture=neutral, PublicKeyToken=17b3b1f090c3ea48. DO NOT EDIT! |
||||||
|
#pragma warning disable 1591, 0612, 3021 |
||||||
|
#region Designer generated code |
||||||
|
|
||||||
|
using pb = global::Google.ProtocolBuffers; |
||||||
|
using pbc = global::Google.ProtocolBuffers.Collections; |
||||||
|
using pbd = global::Google.ProtocolBuffers.Descriptors; |
||||||
|
using scg = global::System.Collections.Generic; |
||||||
|
namespace grpc.testing { |
||||||
|
|
||||||
|
namespace Proto { |
||||||
|
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] |
||||||
|
public static partial class Empty { |
||||||
|
|
||||||
|
#region Extension registration |
||||||
|
public static void RegisterAllExtensions(pb::ExtensionRegistry registry) { |
||||||
|
} |
||||||
|
#endregion |
||||||
|
#region Static variables |
||||||
|
internal static pbd::MessageDescriptor internal__static_grpc_testing_Empty__Descriptor; |
||||||
|
internal static pb::FieldAccess.FieldAccessorTable<global::grpc.testing.Empty, global::grpc.testing.Empty.Builder> internal__static_grpc_testing_Empty__FieldAccessorTable; |
||||||
|
#endregion |
||||||
|
#region Descriptor |
||||||
|
public static pbd::FileDescriptor Descriptor { |
||||||
|
get { return descriptor; } |
||||||
|
} |
||||||
|
private static pbd::FileDescriptor descriptor; |
||||||
|
|
||||||
|
static Empty() { |
||||||
|
byte[] descriptorData = global::System.Convert.FromBase64String( |
||||||
|
string.Concat( |
||||||
|
"CgtlbXB0eS5wcm90bxIMZ3JwYy50ZXN0aW5nIgcKBUVtcHR5")); |
||||||
|
pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) { |
||||||
|
descriptor = root; |
||||||
|
internal__static_grpc_testing_Empty__Descriptor = Descriptor.MessageTypes[0]; |
||||||
|
internal__static_grpc_testing_Empty__FieldAccessorTable = |
||||||
|
new pb::FieldAccess.FieldAccessorTable<global::grpc.testing.Empty, global::grpc.testing.Empty.Builder>(internal__static_grpc_testing_Empty__Descriptor, |
||||||
|
new string[] { }); |
||||||
|
return null; |
||||||
|
}; |
||||||
|
pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, |
||||||
|
new pbd::FileDescriptor[] { |
||||||
|
}, assigner); |
||||||
|
} |
||||||
|
#endregion |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
#region Messages |
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] |
||||||
|
public sealed partial class Empty : pb::GeneratedMessage<Empty, Empty.Builder> { |
||||||
|
private Empty() { } |
||||||
|
private static readonly Empty defaultInstance = new Empty().MakeReadOnly(); |
||||||
|
private static readonly string[] _emptyFieldNames = new string[] { }; |
||||||
|
private static readonly uint[] _emptyFieldTags = new uint[] { }; |
||||||
|
public static Empty DefaultInstance { |
||||||
|
get { return defaultInstance; } |
||||||
|
} |
||||||
|
|
||||||
|
public override Empty DefaultInstanceForType { |
||||||
|
get { return DefaultInstance; } |
||||||
|
} |
||||||
|
|
||||||
|
protected override Empty ThisMessage { |
||||||
|
get { return this; } |
||||||
|
} |
||||||
|
|
||||||
|
public static pbd::MessageDescriptor Descriptor { |
||||||
|
get { return global::grpc.testing.Proto.Empty.internal__static_grpc_testing_Empty__Descriptor; } |
||||||
|
} |
||||||
|
|
||||||
|
protected override pb::FieldAccess.FieldAccessorTable<Empty, Empty.Builder> InternalFieldAccessors { |
||||||
|
get { return global::grpc.testing.Proto.Empty.internal__static_grpc_testing_Empty__FieldAccessorTable; } |
||||||
|
} |
||||||
|
|
||||||
|
public override bool IsInitialized { |
||||||
|
get { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override void WriteTo(pb::ICodedOutputStream output) { |
||||||
|
int size = SerializedSize; |
||||||
|
string[] field_names = _emptyFieldNames; |
||||||
|
UnknownFields.WriteTo(output); |
||||||
|
} |
||||||
|
|
||||||
|
private int memoizedSerializedSize = -1; |
||||||
|
public override int SerializedSize { |
||||||
|
get { |
||||||
|
int size = memoizedSerializedSize; |
||||||
|
if (size != -1) return size; |
||||||
|
|
||||||
|
size = 0; |
||||||
|
size += UnknownFields.SerializedSize; |
||||||
|
memoizedSerializedSize = size; |
||||||
|
return size; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static Empty ParseFrom(pb::ByteString data) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseFrom(byte[] data) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseFrom(global::System.IO.Stream input) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseDelimitedFrom(global::System.IO.Stream input) { |
||||||
|
return CreateBuilder().MergeDelimitedFrom(input).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { |
||||||
|
return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseFrom(pb::ICodedInputStream input) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); |
||||||
|
} |
||||||
|
public static Empty ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { |
||||||
|
return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); |
||||||
|
} |
||||||
|
private Empty MakeReadOnly() { |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public static Builder CreateBuilder() { return new Builder(); } |
||||||
|
public override Builder ToBuilder() { return CreateBuilder(this); } |
||||||
|
public override Builder CreateBuilderForType() { return new Builder(); } |
||||||
|
public static Builder CreateBuilder(Empty prototype) { |
||||||
|
return new Builder(prototype); |
||||||
|
} |
||||||
|
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] |
||||||
|
public sealed partial class Builder : pb::GeneratedBuilder<Empty, Builder> { |
||||||
|
protected override Builder ThisBuilder { |
||||||
|
get { return this; } |
||||||
|
} |
||||||
|
public Builder() { |
||||||
|
result = DefaultInstance; |
||||||
|
resultIsReadOnly = true; |
||||||
|
} |
||||||
|
internal Builder(Empty cloneFrom) { |
||||||
|
result = cloneFrom; |
||||||
|
resultIsReadOnly = true; |
||||||
|
} |
||||||
|
|
||||||
|
private bool resultIsReadOnly; |
||||||
|
private Empty result; |
||||||
|
|
||||||
|
private Empty PrepareBuilder() { |
||||||
|
if (resultIsReadOnly) { |
||||||
|
Empty original = result; |
||||||
|
result = new Empty(); |
||||||
|
resultIsReadOnly = false; |
||||||
|
MergeFrom(original); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool IsInitialized { |
||||||
|
get { return result.IsInitialized; } |
||||||
|
} |
||||||
|
|
||||||
|
protected override Empty MessageBeingBuilt { |
||||||
|
get { return PrepareBuilder(); } |
||||||
|
} |
||||||
|
|
||||||
|
public override Builder Clear() { |
||||||
|
result = DefaultInstance; |
||||||
|
resultIsReadOnly = true; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public override Builder Clone() { |
||||||
|
if (resultIsReadOnly) { |
||||||
|
return new Builder(result); |
||||||
|
} else { |
||||||
|
return new Builder().MergeFrom(result); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override pbd::MessageDescriptor DescriptorForType { |
||||||
|
get { return global::grpc.testing.Empty.Descriptor; } |
||||||
|
} |
||||||
|
|
||||||
|
public override Empty DefaultInstanceForType { |
||||||
|
get { return global::grpc.testing.Empty.DefaultInstance; } |
||||||
|
} |
||||||
|
|
||||||
|
public override Empty BuildPartial() { |
||||||
|
if (resultIsReadOnly) { |
||||||
|
return result; |
||||||
|
} |
||||||
|
resultIsReadOnly = true; |
||||||
|
return result.MakeReadOnly(); |
||||||
|
} |
||||||
|
|
||||||
|
public override Builder MergeFrom(pb::IMessage other) { |
||||||
|
if (other is Empty) { |
||||||
|
return MergeFrom((Empty) other); |
||||||
|
} else { |
||||||
|
base.MergeFrom(other); |
||||||
|
return this; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override Builder MergeFrom(Empty other) { |
||||||
|
if (other == global::grpc.testing.Empty.DefaultInstance) return this; |
||||||
|
PrepareBuilder(); |
||||||
|
this.MergeUnknownFields(other.UnknownFields); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public override Builder MergeFrom(pb::ICodedInputStream input) { |
||||||
|
return MergeFrom(input, pb::ExtensionRegistry.Empty); |
||||||
|
} |
||||||
|
|
||||||
|
public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { |
||||||
|
PrepareBuilder(); |
||||||
|
pb::UnknownFieldSet.Builder unknownFields = null; |
||||||
|
uint tag; |
||||||
|
string field_name; |
||||||
|
while (input.ReadTag(out tag, out field_name)) { |
||||||
|
if(tag == 0 && field_name != null) { |
||||||
|
int field_ordinal = global::System.Array.BinarySearch(_emptyFieldNames, field_name, global::System.StringComparer.Ordinal); |
||||||
|
if(field_ordinal >= 0) |
||||||
|
tag = _emptyFieldTags[field_ordinal]; |
||||||
|
else { |
||||||
|
if (unknownFields == null) { |
||||||
|
unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); |
||||||
|
} |
||||||
|
ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
switch (tag) { |
||||||
|
case 0: { |
||||||
|
throw pb::InvalidProtocolBufferException.InvalidTag(); |
||||||
|
} |
||||||
|
default: { |
||||||
|
if (pb::WireFormat.IsEndGroupTag(tag)) { |
||||||
|
if (unknownFields != null) { |
||||||
|
this.UnknownFields = unknownFields.Build(); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
if (unknownFields == null) { |
||||||
|
unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); |
||||||
|
} |
||||||
|
ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (unknownFields != null) { |
||||||
|
this.UnknownFields = unknownFields.Build(); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
static Empty() { |
||||||
|
object.ReferenceEquals(global::grpc.testing.Proto.Empty.Descriptor, null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endregion |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
#endregion Designer generated code |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,170 @@ |
|||||||
|
using System; |
||||||
|
using System.Threading; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Reactive.Linq; |
||||||
|
using Google.GRPC.Core; |
||||||
|
|
||||||
|
namespace grpc.testing |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// TestService (this is handwritten version of code that will normally be generated). |
||||||
|
/// </summary> |
||||||
|
public class TestServiceGrpc |
||||||
|
{ |
||||||
|
readonly static Marshaller<Empty> emptyMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), Empty.ParseFrom); |
||||||
|
readonly static Marshaller<SimpleRequest> simpleRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), SimpleRequest.ParseFrom); |
||||||
|
readonly static Marshaller<SimpleResponse> simpleResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), SimpleResponse.ParseFrom); |
||||||
|
readonly static Marshaller<StreamingOutputCallRequest> streamingOutputCallRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingOutputCallRequest.ParseFrom); |
||||||
|
readonly static Marshaller<StreamingOutputCallResponse> streamingOutputCallResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingOutputCallResponse.ParseFrom); |
||||||
|
readonly static Marshaller<StreamingInputCallRequest> streamingInputCallRequestMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingInputCallRequest.ParseFrom); |
||||||
|
readonly static Marshaller<StreamingInputCallResponse> streamingInputCallResponseMarshaller = Marshallers.Create((arg) => arg.ToByteArray(), StreamingInputCallResponse.ParseFrom); |
||||||
|
|
||||||
|
readonly static Method<Empty, Empty> emptyCallMethod = new Method<Empty, Empty>( |
||||||
|
MethodType.Unary, |
||||||
|
"/grpc.testing.TestService/EmptyCall", |
||||||
|
emptyMarshaller, |
||||||
|
emptyMarshaller |
||||||
|
); |
||||||
|
readonly static Method<SimpleRequest, SimpleResponse> unaryCallMethod = new Method<SimpleRequest, SimpleResponse>( |
||||||
|
MethodType.Unary, |
||||||
|
"/grpc.testing.TestService/UnaryCall", |
||||||
|
simpleRequestMarshaller, |
||||||
|
simpleResponseMarshaller |
||||||
|
); |
||||||
|
readonly static Method<StreamingOutputCallRequest, StreamingOutputCallResponse> streamingOutputCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>( |
||||||
|
MethodType.ServerStreaming, |
||||||
|
"/grpc.testing.TestService/StreamingOutputCall", |
||||||
|
streamingOutputCallRequestMarshaller, |
||||||
|
streamingOutputCallResponseMarshaller |
||||||
|
); |
||||||
|
readonly static Method<StreamingInputCallRequest, StreamingInputCallResponse> streamingInputCallMethod = new Method<StreamingInputCallRequest, StreamingInputCallResponse>( |
||||||
|
MethodType.ClientStreaming, |
||||||
|
"/grpc.testing.TestService/StreamingInputCall", |
||||||
|
streamingInputCallRequestMarshaller, |
||||||
|
streamingInputCallResponseMarshaller |
||||||
|
); |
||||||
|
readonly static Method<StreamingOutputCallRequest, StreamingOutputCallResponse> fullDuplexCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>( |
||||||
|
MethodType.DuplexStreaming, |
||||||
|
"/grpc.testing.TestService/FullDuplexCall", |
||||||
|
streamingOutputCallRequestMarshaller, |
||||||
|
streamingOutputCallResponseMarshaller |
||||||
|
); |
||||||
|
readonly static Method<StreamingOutputCallRequest, StreamingOutputCallResponse> halfDuplexCallMethod = new Method<StreamingOutputCallRequest, StreamingOutputCallResponse>( |
||||||
|
MethodType.DuplexStreaming, |
||||||
|
"/grpc.testing.TestService/HalfDuplexCall", |
||||||
|
streamingOutputCallRequestMarshaller, |
||||||
|
streamingOutputCallResponseMarshaller |
||||||
|
); |
||||||
|
|
||||||
|
public interface ITestServiceClient |
||||||
|
{ |
||||||
|
Empty EmptyCall(Empty request, CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
Task<Empty> EmptyCallAsync(Empty request, CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
SimpleResponse UnaryCall(SimpleRequest request, CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
Task<SimpleResponse> UnaryCallAsync(SimpleRequest request, CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
Task StreamingOutputCall(StreamingOutputCallRequest request, IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
ClientStreamingAsyncResult<StreamingInputCallRequest, StreamingInputCallResponse> StreamingInputCall(CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
IObserver<StreamingOutputCallRequest> FullDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)); |
||||||
|
|
||||||
|
IObserver<StreamingOutputCallRequest> HalfDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)); |
||||||
|
} |
||||||
|
|
||||||
|
public class TestServiceClientStub : ITestServiceClient |
||||||
|
{ |
||||||
|
readonly Channel channel; |
||||||
|
|
||||||
|
public TestServiceClientStub(Channel channel) |
||||||
|
{ |
||||||
|
this.channel = channel; |
||||||
|
} |
||||||
|
|
||||||
|
public Empty EmptyCall(Empty request, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<Empty, Empty>(emptyCallMethod, channel); |
||||||
|
return Calls.BlockingUnaryCall(call, request, token); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<Empty> EmptyCallAsync(Empty request, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<Empty, Empty>(emptyCallMethod, channel); |
||||||
|
return Calls.AsyncUnaryCall(call, request, token); |
||||||
|
} |
||||||
|
|
||||||
|
public SimpleResponse UnaryCall(SimpleRequest request, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<SimpleRequest, SimpleResponse>(unaryCallMethod, channel); |
||||||
|
return Calls.BlockingUnaryCall(call, request, token); |
||||||
|
} |
||||||
|
|
||||||
|
public Task<SimpleResponse> UnaryCallAsync(SimpleRequest request, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<SimpleRequest, SimpleResponse>(unaryCallMethod, channel); |
||||||
|
return Calls.AsyncUnaryCall(call, request, token); |
||||||
|
} |
||||||
|
|
||||||
|
public Task StreamingOutputCall(StreamingOutputCallRequest request, IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)) { |
||||||
|
var call = new Google.GRPC.Core.Call<StreamingOutputCallRequest, StreamingOutputCallResponse>(streamingOutputCallMethod, channel); |
||||||
|
return Calls.AsyncServerStreamingCall(call, request, responseObserver, token); |
||||||
|
} |
||||||
|
|
||||||
|
public ClientStreamingAsyncResult<StreamingInputCallRequest, StreamingInputCallResponse> StreamingInputCall(CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<StreamingInputCallRequest, StreamingInputCallResponse>(streamingInputCallMethod, channel); |
||||||
|
return Calls.AsyncClientStreamingCall(call, token); |
||||||
|
} |
||||||
|
|
||||||
|
public IObserver<StreamingOutputCallRequest> FullDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<StreamingOutputCallRequest, StreamingOutputCallResponse>(fullDuplexCallMethod, channel); |
||||||
|
return Calls.DuplexStreamingCall(call, responseObserver, token); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public IObserver<StreamingOutputCallRequest> HalfDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver, CancellationToken token = default(CancellationToken)) |
||||||
|
{ |
||||||
|
var call = new Google.GRPC.Core.Call<StreamingOutputCallRequest, StreamingOutputCallResponse>(halfDuplexCallMethod, channel); |
||||||
|
return Calls.DuplexStreamingCall(call, responseObserver, token); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// server-side interface |
||||||
|
public interface ITestService |
||||||
|
{ |
||||||
|
void EmptyCall(Empty request, IObserver<Empty> responseObserver); |
||||||
|
|
||||||
|
void UnaryCall(SimpleRequest request, IObserver<SimpleResponse> responseObserver); |
||||||
|
|
||||||
|
void StreamingOutputCall(StreamingOutputCallRequest request, IObserver<StreamingOutputCallResponse> responseObserver); |
||||||
|
|
||||||
|
IObserver<StreamingInputCallRequest> StreamingInputCall(IObserver<StreamingInputCallResponse> responseObserver); |
||||||
|
|
||||||
|
IObserver<StreamingOutputCallRequest> FullDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver); |
||||||
|
|
||||||
|
IObserver<StreamingOutputCallRequest> HalfDuplexCall(IObserver<StreamingOutputCallResponse> responseObserver); |
||||||
|
} |
||||||
|
|
||||||
|
public static ServerServiceDefinition BindService(ITestService serviceImpl) |
||||||
|
{ |
||||||
|
return ServerServiceDefinition.CreateBuilder("/grpc.testing.TestService/") |
||||||
|
.AddMethod(emptyCallMethod, serviceImpl.EmptyCall) |
||||||
|
.AddMethod(unaryCallMethod, serviceImpl.UnaryCall) |
||||||
|
.AddMethod(streamingOutputCallMethod, serviceImpl.StreamingOutputCall) |
||||||
|
.AddMethod(streamingInputCallMethod, serviceImpl.StreamingInputCall) |
||||||
|
.AddMethod(fullDuplexCallMethod, serviceImpl.FullDuplexCall) |
||||||
|
.AddMethod(halfDuplexCallMethod, serviceImpl.HalfDuplexCall) |
||||||
|
.Build(); |
||||||
|
} |
||||||
|
|
||||||
|
public static ITestServiceClient NewStub(Channel channel) |
||||||
|
{ |
||||||
|
return new TestServiceClientStub(channel); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
syntax = "proto2"; |
||||||
|
|
||||||
|
package grpc.testing; |
||||||
|
|
||||||
|
// An empty message that you can re-use to avoid defining duplicated empty |
||||||
|
// messages in your project. A typical example is to use it as argument or the |
||||||
|
// return value of a service API. For instance: |
||||||
|
// |
||||||
|
// service Foo { |
||||||
|
// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { }; |
||||||
|
// }; |
||||||
|
// |
||||||
|
message Empty {} |
@ -0,0 +1,102 @@ |
|||||||
|
// Message definitions to be used by integration test service definitions. |
||||||
|
|
||||||
|
syntax = "proto2"; |
||||||
|
|
||||||
|
package grpc.testing; |
||||||
|
|
||||||
|
// The type of payload that should be returned. |
||||||
|
enum PayloadType { |
||||||
|
// Compressable text format. |
||||||
|
COMPRESSABLE = 0; |
||||||
|
|
||||||
|
// Uncompressable binary format. |
||||||
|
UNCOMPRESSABLE = 1; |
||||||
|
|
||||||
|
// Randomly chosen from all other formats defined in this enum. |
||||||
|
RANDOM = 2; |
||||||
|
} |
||||||
|
|
||||||
|
// A block of data, to simply increase gRPC message size. |
||||||
|
message Payload { |
||||||
|
// The type of data in body. |
||||||
|
optional PayloadType type = 1; |
||||||
|
// Primary contents of payload. |
||||||
|
optional bytes body = 2; |
||||||
|
} |
||||||
|
|
||||||
|
// Unary request. |
||||||
|
message SimpleRequest { |
||||||
|
// Desired payload type in the response from the server. |
||||||
|
// If response_type is RANDOM, server randomly chooses one from other formats. |
||||||
|
optional PayloadType response_type = 1; |
||||||
|
|
||||||
|
// Desired payload size in the response from the server. |
||||||
|
// If response_type is COMPRESSABLE, this denotes the size before compression. |
||||||
|
optional int32 response_size = 2; |
||||||
|
|
||||||
|
// Optional input payload sent along with the request. |
||||||
|
optional Payload payload = 3; |
||||||
|
|
||||||
|
// Whether SimpleResponse should include username. |
||||||
|
optional bool fill_username = 4; |
||||||
|
|
||||||
|
// Whether SimpleResponse should include OAuth scope. |
||||||
|
optional bool fill_oauth_scope = 5; |
||||||
|
} |
||||||
|
|
||||||
|
// Unary response, as configured by the request. |
||||||
|
message SimpleResponse { |
||||||
|
// Payload to increase message size. |
||||||
|
optional Payload payload = 1; |
||||||
|
// The user the request came from, for verifying authentication was |
||||||
|
// successful when the client expected it. |
||||||
|
optional string username = 2; |
||||||
|
// OAuth scope. |
||||||
|
optional string oauth_scope = 3; |
||||||
|
} |
||||||
|
|
||||||
|
// Client-streaming request. |
||||||
|
message StreamingInputCallRequest { |
||||||
|
// Optional input payload sent along with the request. |
||||||
|
optional Payload payload = 1; |
||||||
|
|
||||||
|
// Not expecting any payload from the response. |
||||||
|
} |
||||||
|
|
||||||
|
// Client-streaming response. |
||||||
|
message StreamingInputCallResponse { |
||||||
|
// Aggregated size of payloads received from the client. |
||||||
|
optional int32 aggregated_payload_size = 1; |
||||||
|
} |
||||||
|
|
||||||
|
// Configuration for a particular response. |
||||||
|
message ResponseParameters { |
||||||
|
// Desired payload sizes in responses from the server. |
||||||
|
// If response_type is COMPRESSABLE, this denotes the size before compression. |
||||||
|
optional int32 size = 1; |
||||||
|
|
||||||
|
// Desired interval between consecutive responses in the response stream in |
||||||
|
// microseconds. |
||||||
|
optional int32 interval_us = 2; |
||||||
|
} |
||||||
|
|
||||||
|
// Server-streaming request. |
||||||
|
message StreamingOutputCallRequest { |
||||||
|
// Desired payload type in the response from the server. |
||||||
|
// If response_type is RANDOM, the payload from each response in the stream |
||||||
|
// might be of different types. This is to simulate a mixed type of payload |
||||||
|
// stream. |
||||||
|
optional PayloadType response_type = 1; |
||||||
|
|
||||||
|
// Configuration for each expected response message. |
||||||
|
repeated ResponseParameters response_parameters = 2; |
||||||
|
|
||||||
|
// Optional input payload sent along with the request. |
||||||
|
optional Payload payload = 3; |
||||||
|
} |
||||||
|
|
||||||
|
// Server-streaming response, as configured by the request and parameters. |
||||||
|
message StreamingOutputCallResponse { |
||||||
|
// Payload to increase response size. |
||||||
|
optional Payload payload = 1; |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
// An integration test service that covers all the method signature permutations |
||||||
|
// of unary/streaming requests/responses. |
||||||
|
syntax = "proto2"; |
||||||
|
|
||||||
|
import "empty.proto"; |
||||||
|
import "messages.proto"; |
||||||
|
|
||||||
|
package grpc.testing; |
||||||
|
|
||||||
|
// A simple service to test the various types of RPCs and experiment with |
||||||
|
// performance with various types of payload. |
||||||
|
service TestService { |
||||||
|
// One empty request followed by one empty response. |
||||||
|
rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty); |
||||||
|
|
||||||
|
// One request followed by one response. |
||||||
|
// The server returns the client payload as-is. |
||||||
|
rpc UnaryCall(SimpleRequest) returns (SimpleResponse); |
||||||
|
|
||||||
|
// One request followed by a sequence of responses (streamed download). |
||||||
|
// The server returns the payload with client desired type and sizes. |
||||||
|
rpc StreamingOutputCall(StreamingOutputCallRequest) |
||||||
|
returns (stream StreamingOutputCallResponse); |
||||||
|
|
||||||
|
// A sequence of requests followed by one response (streamed upload). |
||||||
|
// The server returns the aggregated size of client payload as the result. |
||||||
|
rpc StreamingInputCall(stream StreamingInputCallRequest) |
||||||
|
returns (StreamingInputCallResponse); |
||||||
|
|
||||||
|
// A sequence of requests with each request served by the server immediately. |
||||||
|
// As one request could lead to multiple responses, this interface |
||||||
|
// demonstrates the idea of full duplexing. |
||||||
|
rpc FullDuplexCall(stream StreamingOutputCallRequest) |
||||||
|
returns (stream StreamingOutputCallResponse); |
||||||
|
|
||||||
|
// A sequence of requests followed by a sequence of responses. |
||||||
|
// The server buffers all the client requests and then serves them in order. A |
||||||
|
// stream of responses are returned to the client when the server starts with |
||||||
|
// first request. |
||||||
|
rpc HalfDuplexCall(stream StreamingOutputCallRequest) |
||||||
|
returns (stream StreamingOutputCallResponse); |
||||||
|
} |
@ -1,2 +1,3 @@ |
|||||||
test-results |
test-results |
||||||
bin |
bin |
||||||
|
obj |
||||||
|
@ -1 +1,2 @@ |
|||||||
bin |
bin |
||||||
|
obj |
@ -0,0 +1,50 @@ |
|||||||
|
using System; |
||||||
|
using System.Threading.Tasks; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Collections.Concurrent; |
||||||
|
|
||||||
|
namespace Google.GRPC.Core.Utils |
||||||
|
{ |
||||||
|
/// <summary> |
||||||
|
/// Observer that allows us to await incoming messages one-by-one. |
||||||
|
/// The implementation is not ideal and class will be probably replaced |
||||||
|
/// by something more versatile in the future. |
||||||
|
/// </summary> |
||||||
|
public class RecordingQueue<T> : IObserver<T> |
||||||
|
{ |
||||||
|
readonly BlockingCollection<T> queue = new BlockingCollection<T>(); |
||||||
|
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(); |
||||||
|
|
||||||
|
public void OnCompleted() |
||||||
|
{ |
||||||
|
tcs.SetResult(null); |
||||||
|
} |
||||||
|
|
||||||
|
public void OnError(Exception error) |
||||||
|
{ |
||||||
|
tcs.SetException(error); |
||||||
|
} |
||||||
|
|
||||||
|
public void OnNext(T value) |
||||||
|
{ |
||||||
|
queue.Add(value); |
||||||
|
} |
||||||
|
|
||||||
|
public BlockingCollection<T> Queue |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return queue; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public Task Finished |
||||||
|
{ |
||||||
|
get |
||||||
|
{ |
||||||
|
return tcs.Task; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -1,2 +1,3 @@ |
|||||||
test-results |
test-results |
||||||
bin |
bin |
||||||
|
obj |
@ -1 +1,2 @@ |
|||||||
bin |
bin |
||||||
|
obj |
@ -0,0 +1,303 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using NUnit.Framework; |
||||||
|
using System.Text.RegularExpressions; |
||||||
|
using Google.GRPC.Core; |
||||||
|
using Google.GRPC.Core.Utils; |
||||||
|
using Google.ProtocolBuffers; |
||||||
|
using grpc.testing; |
||||||
|
|
||||||
|
namespace Google.GRPC.Interop |
||||||
|
{ |
||||||
|
class Client |
||||||
|
{ |
||||||
|
private class ClientOptions |
||||||
|
{ |
||||||
|
public bool help; |
||||||
|
public string serverHost; |
||||||
|
public string serverHostOverride; |
||||||
|
public int? serverPort; |
||||||
|
public string testCase; |
||||||
|
public bool useTls; |
||||||
|
public bool useTestCa; |
||||||
|
} |
||||||
|
|
||||||
|
ClientOptions options; |
||||||
|
|
||||||
|
private Client(ClientOptions options) |
||||||
|
{ |
||||||
|
this.options = options; |
||||||
|
} |
||||||
|
|
||||||
|
public static void Main(string[] args) |
||||||
|
{ |
||||||
|
Console.WriteLine("gRPC C# interop testing client"); |
||||||
|
ClientOptions options = ParseArguments(args); |
||||||
|
|
||||||
|
if (options.serverHost == null || !options.serverPort.HasValue || options.testCase == null) |
||||||
|
{ |
||||||
|
Console.WriteLine("Missing required argument."); |
||||||
|
Console.WriteLine(); |
||||||
|
options.help = true; |
||||||
|
} |
||||||
|
|
||||||
|
if (options.help) |
||||||
|
{ |
||||||
|
Console.WriteLine("Usage:"); |
||||||
|
Console.WriteLine(" --server_host=HOSTNAME"); |
||||||
|
Console.WriteLine(" --server_host_override=HOSTNAME"); |
||||||
|
Console.WriteLine(" --server_port=PORT"); |
||||||
|
Console.WriteLine(" --test_case=TESTCASE"); |
||||||
|
Console.WriteLine(" --use_tls=BOOLEAN"); |
||||||
|
Console.WriteLine(" --use_test_ca=BOOLEAN"); |
||||||
|
Console.WriteLine(); |
||||||
|
Environment.Exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
var interopClient = new Client(options); |
||||||
|
interopClient.Run(); |
||||||
|
} |
||||||
|
|
||||||
|
private void Run() |
||||||
|
{ |
||||||
|
string addr = string.Format("{0}:{1}", options.serverHost, options.serverPort); |
||||||
|
using (Channel channel = new Channel(addr)) |
||||||
|
{ |
||||||
|
TestServiceGrpc.ITestServiceClient client = new TestServiceGrpc.TestServiceClientStub(channel); |
||||||
|
|
||||||
|
RunTestCase(options.testCase, client); |
||||||
|
} |
||||||
|
|
||||||
|
GrpcEnvironment.Shutdown(); |
||||||
|
} |
||||||
|
|
||||||
|
private void RunTestCase(string testCase, TestServiceGrpc.ITestServiceClient client) |
||||||
|
{ |
||||||
|
switch (testCase) |
||||||
|
{ |
||||||
|
case "empty_unary": |
||||||
|
RunEmptyUnary(client); |
||||||
|
break; |
||||||
|
case "large_unary": |
||||||
|
RunLargeUnary(client); |
||||||
|
break; |
||||||
|
case "client_streaming": |
||||||
|
RunClientStreaming(client); |
||||||
|
break; |
||||||
|
case "server_streaming": |
||||||
|
RunServerStreaming(client); |
||||||
|
break; |
||||||
|
case "ping_pong": |
||||||
|
RunPingPong(client); |
||||||
|
break; |
||||||
|
case "empty_stream": |
||||||
|
RunEmptyStream(client); |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new ArgumentException("Unknown test case " + testCase); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void RunEmptyUnary(TestServiceGrpc.ITestServiceClient client) |
||||||
|
{ |
||||||
|
Console.WriteLine("running empty_unary"); |
||||||
|
var response = client.EmptyCall(Empty.DefaultInstance); |
||||||
|
Assert.IsNotNull(response); |
||||||
|
Console.WriteLine("Passed!"); |
||||||
|
} |
||||||
|
|
||||||
|
private void RunLargeUnary(TestServiceGrpc.ITestServiceClient client) |
||||||
|
{ |
||||||
|
Console.WriteLine("running large_unary"); |
||||||
|
var request = SimpleRequest.CreateBuilder() |
||||||
|
.SetResponseType(PayloadType.COMPRESSABLE) |
||||||
|
.SetResponseSize(314159) |
||||||
|
.SetPayload(CreateZerosPayload(271828)) |
||||||
|
.Build(); |
||||||
|
|
||||||
|
var response = client.UnaryCall(request); |
||||||
|
|
||||||
|
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); |
||||||
|
Assert.AreEqual(314159, response.Payload.Body.Length); |
||||||
|
Console.WriteLine("Passed!"); |
||||||
|
} |
||||||
|
|
||||||
|
private void RunClientStreaming(TestServiceGrpc.ITestServiceClient client) |
||||||
|
{ |
||||||
|
Console.WriteLine("running client_streaming"); |
||||||
|
|
||||||
|
var bodySizes = new List<int>{27182, 8, 1828, 45904}; |
||||||
|
|
||||||
|
var context = client.StreamingInputCall(); |
||||||
|
foreach (var size in bodySizes) |
||||||
|
{ |
||||||
|
context.Inputs.OnNext( |
||||||
|
StreamingInputCallRequest.CreateBuilder().SetPayload(CreateZerosPayload(size)).Build()); |
||||||
|
} |
||||||
|
context.Inputs.OnCompleted(); |
||||||
|
|
||||||
|
var response = context.Task.Result; |
||||||
|
Assert.AreEqual(74922, response.AggregatedPayloadSize); |
||||||
|
Console.WriteLine("Passed!"); |
||||||
|
} |
||||||
|
|
||||||
|
private void RunServerStreaming(TestServiceGrpc.ITestServiceClient client) |
||||||
|
{ |
||||||
|
Console.WriteLine("running server_streaming"); |
||||||
|
|
||||||
|
var bodySizes = new List<int>{31415, 9, 2653, 58979}; |
||||||
|
|
||||||
|
var request = StreamingOutputCallRequest.CreateBuilder() |
||||||
|
.SetResponseType(PayloadType.COMPRESSABLE) |
||||||
|
.AddRangeResponseParameters(bodySizes.ConvertAll( |
||||||
|
(size) => ResponseParameters.CreateBuilder().SetSize(size).Build())) |
||||||
|
.Build(); |
||||||
|
|
||||||
|
var recorder = new RecordingObserver<StreamingOutputCallResponse>(); |
||||||
|
client.StreamingOutputCall(request, recorder); |
||||||
|
|
||||||
|
var responseList = recorder.ToList().Result; |
||||||
|
|
||||||
|
foreach (var res in responseList) |
||||||
|
{ |
||||||
|
Assert.AreEqual(PayloadType.COMPRESSABLE, res.Payload.Type); |
||||||
|
} |
||||||
|
CollectionAssert.AreEqual(bodySizes, responseList.ConvertAll((item) => item.Payload.Body.Length)); |
||||||
|
Console.WriteLine("Passed!"); |
||||||
|
} |
||||||
|
|
||||||
|
private void RunPingPong(TestServiceGrpc.ITestServiceClient client) |
||||||
|
{ |
||||||
|
Console.WriteLine("running ping_pong"); |
||||||
|
|
||||||
|
var recorder = new RecordingQueue<StreamingOutputCallResponse>(); |
||||||
|
var inputs = client.FullDuplexCall(recorder); |
||||||
|
|
||||||
|
StreamingOutputCallResponse response; |
||||||
|
|
||||||
|
inputs.OnNext(StreamingOutputCallRequest.CreateBuilder() |
||||||
|
.SetResponseType(PayloadType.COMPRESSABLE) |
||||||
|
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(31415)) |
||||||
|
.SetPayload(CreateZerosPayload(27182)).Build()); |
||||||
|
|
||||||
|
response = recorder.Queue.Take(); |
||||||
|
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); |
||||||
|
Assert.AreEqual(31415, response.Payload.Body.Length); |
||||||
|
|
||||||
|
inputs.OnNext(StreamingOutputCallRequest.CreateBuilder() |
||||||
|
.SetResponseType(PayloadType.COMPRESSABLE) |
||||||
|
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(9)) |
||||||
|
.SetPayload(CreateZerosPayload(8)).Build()); |
||||||
|
|
||||||
|
response = recorder.Queue.Take(); |
||||||
|
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); |
||||||
|
Assert.AreEqual(9, response.Payload.Body.Length); |
||||||
|
|
||||||
|
inputs.OnNext(StreamingOutputCallRequest.CreateBuilder() |
||||||
|
.SetResponseType(PayloadType.COMPRESSABLE) |
||||||
|
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(2635)) |
||||||
|
.SetPayload(CreateZerosPayload(1828)).Build()); |
||||||
|
|
||||||
|
response = recorder.Queue.Take(); |
||||||
|
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); |
||||||
|
Assert.AreEqual(2653, response.Payload.Body.Length); |
||||||
|
|
||||||
|
|
||||||
|
inputs.OnNext(StreamingOutputCallRequest.CreateBuilder() |
||||||
|
.SetResponseType(PayloadType.COMPRESSABLE) |
||||||
|
.AddResponseParameters(ResponseParameters.CreateBuilder().SetSize(58979)) |
||||||
|
.SetPayload(CreateZerosPayload(45904)).Build()); |
||||||
|
|
||||||
|
response = recorder.Queue.Take(); |
||||||
|
Assert.AreEqual(PayloadType.COMPRESSABLE, response.Payload.Type); |
||||||
|
Assert.AreEqual(58979, response.Payload.Body.Length); |
||||||
|
|
||||||
|
recorder.Finished.Wait(); |
||||||
|
Assert.AreEqual(0, recorder.Queue.Count); |
||||||
|
|
||||||
|
Console.WriteLine("Passed!"); |
||||||
|
} |
||||||
|
|
||||||
|
private void RunEmptyStream(TestServiceGrpc.ITestServiceClient client) |
||||||
|
{ |
||||||
|
Console.WriteLine("running empty_stream"); |
||||||
|
|
||||||
|
var recorder = new RecordingObserver<StreamingOutputCallResponse>(); |
||||||
|
var inputs = client.FullDuplexCall(recorder); |
||||||
|
inputs.OnCompleted(); |
||||||
|
|
||||||
|
var responseList = recorder.ToList().Result; |
||||||
|
Assert.AreEqual(0, responseList.Count); |
||||||
|
|
||||||
|
Console.WriteLine("Passed!"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private Payload CreateZerosPayload(int size) { |
||||||
|
return Payload.CreateBuilder().SetBody(ByteString.CopyFrom(new byte[size])).Build(); |
||||||
|
} |
||||||
|
|
||||||
|
private static ClientOptions ParseArguments(string[] args) |
||||||
|
{ |
||||||
|
var options = new ClientOptions(); |
||||||
|
foreach(string arg in args) |
||||||
|
{ |
||||||
|
ParseArgument(arg, options); |
||||||
|
if (options.help) |
||||||
|
{ |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return options; |
||||||
|
} |
||||||
|
|
||||||
|
private static void ParseArgument(string arg, ClientOptions options) |
||||||
|
{ |
||||||
|
Match match; |
||||||
|
match = Regex.Match(arg, "--server_host=(.*)"); |
||||||
|
if (match.Success) |
||||||
|
{ |
||||||
|
options.serverHost = match.Groups[1].Value.Trim(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
match = Regex.Match(arg, "--server_host_override=(.*)"); |
||||||
|
if (match.Success) |
||||||
|
{ |
||||||
|
options.serverHostOverride = match.Groups[1].Value.Trim(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
match = Regex.Match(arg, "--server_port=(.*)"); |
||||||
|
if (match.Success) |
||||||
|
{ |
||||||
|
options.serverPort = int.Parse(match.Groups[1].Value.Trim()); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
match = Regex.Match(arg, "--test_case=(.*)"); |
||||||
|
if (match.Success) |
||||||
|
{ |
||||||
|
options.testCase = match.Groups[1].Value.Trim(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
match = Regex.Match(arg, "--use_tls=(.*)"); |
||||||
|
if (match.Success) |
||||||
|
{ |
||||||
|
options.useTls = bool.Parse(match.Groups[1].Value.Trim()); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
match = Regex.Match(arg, "--use_test_ca=(.*)"); |
||||||
|
if (match.Success) |
||||||
|
{ |
||||||
|
options.useTestCa = bool.Parse(match.Groups[1].Value.Trim()); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
Console.WriteLine(string.Format("Unrecognized argument \"{0}\"", arg)); |
||||||
|
options.help = true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
using System.Reflection; |
||||||
|
using System.Runtime.CompilerServices; |
||||||
|
|
||||||
|
// Information about this assembly is defined by the following attributes. |
||||||
|
// Change them to the values specific to your project. |
||||||
|
[assembly: AssemblyTitle("InteropClient")] |
||||||
|
[assembly: AssemblyDescription("")] |
||||||
|
[assembly: AssemblyConfiguration("")] |
||||||
|
[assembly: AssemblyCompany("")] |
||||||
|
[assembly: AssemblyProduct("")] |
||||||
|
[assembly: AssemblyCopyright("jtattermusch")] |
||||||
|
[assembly: AssemblyTrademark("")] |
||||||
|
[assembly: AssemblyCulture("")] |
||||||
|
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". |
||||||
|
// The form "{Major}.{Minor}.*" will automatically update the build and revision, |
||||||
|
// and "{Major}.{Minor}.{Build}.*" will update just the revision. |
||||||
|
[assembly: AssemblyVersion("1.0.*")] |
||||||
|
// The following attributes are used to specify the signing key for the assembly, |
||||||
|
// if desired. See the Mono documentation for more information about signing. |
||||||
|
//[assembly: AssemblyDelaySign(false)] |
||||||
|
//[assembly: AssemblyKeyFile("")] |
||||||
|
|
@ -0,0 +1,2 @@ |
|||||||
|
bin |
||||||
|
obj |
Binary file not shown.
@ -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; |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue