blob: d5b06e7b0b235432e08fae6008bea7d91e6347ba [file] [log] [blame]
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +01001/*
2 *
Craig Tiller06059952015-02-18 08:34:56 -08003 * Copyright 2015, Google Inc.
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +01004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include <grpc/support/port_platform.h>
35
36#ifdef GPR_WINSOCK_SOCKET
37
38#include "src/core/iomgr/sockaddr_win32.h"
39
40#include <grpc/support/alloc.h>
41#include <grpc/support/log.h>
42#include <grpc/support/log_win32.h>
43#include <grpc/support/slice_buffer.h>
44#include <grpc/support/useful.h>
45
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010046#include "src/core/iomgr/alarm.h"
Nicolas Noble45e67a32015-02-09 16:20:49 -080047#include "src/core/iomgr/iocp_windows.h"
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010048#include "src/core/iomgr/sockaddr.h"
49#include "src/core/iomgr/sockaddr_utils.h"
Nicolas Noble45e67a32015-02-09 16:20:49 -080050#include "src/core/iomgr/socket_windows.h"
51#include "src/core/iomgr/tcp_client.h"
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010052
53static int set_non_block(SOCKET sock) {
54 int status;
55 unsigned long param = 1;
56 DWORD ret;
57 status = WSAIoctl(sock, FIONBIO, &param, sizeof(param), NULL, 0, &ret,
58 NULL, NULL);
59 return status == 0;
60}
61
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +010062static int set_dualstack(SOCKET sock) {
63 int status;
64 unsigned long param = 0;
Nicolas "Pixel" Noble0f3ec822015-02-05 19:40:38 +010065 status = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
66 (const char *) &param, sizeof(param));
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +010067 return status == 0;
68}
69
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010070int grpc_tcp_prepare_socket(SOCKET sock) {
71 if (!set_non_block(sock))
72 return 0;
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +010073 if (!set_dualstack(sock))
74 return 0;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010075 return 1;
76}
77
78typedef struct grpc_tcp {
79 grpc_endpoint base;
80 grpc_winsocket *socket;
81 gpr_refcount refcount;
82
83 grpc_endpoint_read_cb read_cb;
84 void *read_user_data;
85 gpr_slice read_slice;
86 int outstanding_read;
87
88 grpc_endpoint_write_cb write_cb;
89 void *write_user_data;
90 gpr_slice_buffer write_slices;
91 int outstanding_write;
92
93} grpc_tcp;
94
95static void tcp_ref(grpc_tcp *tcp) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +010096 gpr_ref(&tcp->refcount);
97}
98
99static void tcp_unref(grpc_tcp *tcp) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100100 if (gpr_unref(&tcp->refcount)) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100101 gpr_slice_buffer_destroy(&tcp->write_slices);
102 grpc_winsocket_orphan(tcp->socket);
103 gpr_free(tcp);
104 }
105}
106
107static void on_read(void *tcpp, int success) {
108 grpc_tcp *tcp = (grpc_tcp *) tcpp;
109 grpc_winsocket *socket = tcp->socket;
110 gpr_slice sub;
111 gpr_slice *slice = NULL;
112 size_t nslices = 0;
113 grpc_endpoint_cb_status status;
114 grpc_endpoint_read_cb cb = tcp->read_cb;
115 grpc_winsocket_callback_info *info = &socket->read_info;
116 void *opaque = tcp->read_user_data;
117
118 GPR_ASSERT(tcp->outstanding_read);
119
120 if (!success) {
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +0100121 tcp_unref(tcp);
Nicolas Noble45e67a32015-02-09 16:20:49 -0800122 cb(opaque, NULL, 0, GRPC_ENDPOINT_CB_SHUTDOWN);
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +0100123 return;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100124 }
125
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100126 tcp->outstanding_read = 0;
127
128 if (socket->read_info.wsa_error != 0) {
129 char *utf8_message = gpr_format_message(info->wsa_error);
Jan Tattermusch638c1ee2015-04-21 17:14:44 -0700130 gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100131 gpr_free(utf8_message);
132 status = GRPC_ENDPOINT_CB_ERROR;
Nicolas "Pixel" Noble404fc6a2015-05-02 02:34:39 -0700133 socket->closed_early = 1;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100134 } else {
135 if (info->bytes_transfered != 0) {
136 sub = gpr_slice_sub(tcp->read_slice, 0, info->bytes_transfered);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100137 status = GRPC_ENDPOINT_CB_OK;
138 slice = &sub;
139 nslices = 1;
140 } else {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100141 gpr_slice_unref(tcp->read_slice);
142 status = GRPC_ENDPOINT_CB_EOF;
143 }
144 }
145 tcp_unref(tcp);
146 cb(opaque, slice, nslices, status);
147}
148
149static void win_notify_on_read(grpc_endpoint *ep,
150 grpc_endpoint_read_cb cb, void *arg) {
151 grpc_tcp *tcp = (grpc_tcp *) ep;
152 grpc_winsocket *handle = tcp->socket;
153 grpc_winsocket_callback_info *info = &handle->read_info;
154 int status;
155 DWORD bytes_read = 0;
156 DWORD flags = 0;
157 int error;
158 WSABUF buffer;
159
160 GPR_ASSERT(!tcp->outstanding_read);
161 tcp_ref(tcp);
162 tcp->outstanding_read = 1;
163 tcp->read_cb = cb;
164 tcp->read_user_data = arg;
165
166 tcp->read_slice = gpr_slice_malloc(8192);
167
168 buffer.len = GPR_SLICE_LENGTH(tcp->read_slice);
Nicolas Noblecfd60732015-03-18 16:27:43 -0700169 buffer.buf = (char *)GPR_SLICE_START_PTR(tcp->read_slice);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100170
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100171 status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags,
172 NULL, NULL);
173 info->wsa_error = status == 0 ? 0 : WSAGetLastError();
174
175 if (info->wsa_error != WSAEWOULDBLOCK) {
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100176 info->bytes_transfered = bytes_read;
177 /* This might heavily recurse. */
178 on_read(tcp, 1);
179 return;
180 }
181
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100182 memset(&tcp->socket->read_info.overlapped, 0, sizeof(OVERLAPPED));
183 status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags,
184 &info->overlapped, NULL);
185
186 if (status == 0) {
Nicolas Noble45e67a32015-02-09 16:20:49 -0800187 grpc_socket_notify_on_read(tcp->socket, on_read, tcp);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100188 return;
189 }
190
191 error = WSAGetLastError();
192
193 if (error != WSA_IO_PENDING) {
194 char *utf8_message = gpr_format_message(WSAGetLastError());
195 __debugbreak();
196 gpr_log(GPR_ERROR, "WSARecv error: %s", utf8_message);
197 gpr_free(utf8_message);
198 /* would the IO completion port be called anyway... ? Let's assume not. */
199 tcp->outstanding_read = 0;
200 tcp_unref(tcp);
201 cb(arg, NULL, 0, GRPC_ENDPOINT_CB_ERROR);
202 return;
203 }
204
Nicolas Noble45e67a32015-02-09 16:20:49 -0800205 grpc_socket_notify_on_read(tcp->socket, on_read, tcp);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100206}
207
208static void on_write(void *tcpp, int success) {
209 grpc_tcp *tcp = (grpc_tcp *) tcpp;
210 grpc_winsocket *handle = tcp->socket;
211 grpc_winsocket_callback_info *info = &handle->write_info;
212 grpc_endpoint_cb_status status = GRPC_ENDPOINT_CB_OK;
213 grpc_endpoint_write_cb cb = tcp->write_cb;
214 void *opaque = tcp->write_user_data;
215
216 GPR_ASSERT(tcp->outstanding_write);
217
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100218 if (!success) {
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +0100219 tcp_unref(tcp);
Nicolas "Pixel" Nobleba410fa2015-02-05 00:54:15 +0100220 cb(opaque, GRPC_ENDPOINT_CB_SHUTDOWN);
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +0100221 return;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100222 }
223
224 if (info->wsa_error != 0) {
225 char *utf8_message = gpr_format_message(info->wsa_error);
226 gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message);
227 gpr_free(utf8_message);
228 status = GRPC_ENDPOINT_CB_ERROR;
Nicolas "Pixel" Noble404fc6a2015-05-02 02:34:39 -0700229 tcp->socket->closed_early = 1;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100230 } else {
231 GPR_ASSERT(info->bytes_transfered == tcp->write_slices.length);
232 }
233
234 gpr_slice_buffer_reset_and_unref(&tcp->write_slices);
235 tcp->outstanding_write = 0;
236
237 tcp_unref(tcp);
238 cb(opaque, status);
239}
240
241static grpc_endpoint_write_status win_write(grpc_endpoint *ep,
242 gpr_slice *slices, size_t nslices,
243 grpc_endpoint_write_cb cb,
244 void *arg) {
245 grpc_tcp *tcp = (grpc_tcp *) ep;
246 grpc_winsocket *socket = tcp->socket;
247 grpc_winsocket_callback_info *info = &socket->write_info;
248 unsigned i;
249 DWORD bytes_sent;
250 int status;
251 WSABUF local_buffers[16];
252 WSABUF *allocated = NULL;
253 WSABUF *buffers = local_buffers;
254
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100255 GPR_ASSERT(!tcp->outstanding_write);
256 tcp_ref(tcp);
257
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100258 tcp->outstanding_write = 1;
259 tcp->write_cb = cb;
260 tcp->write_user_data = arg;
261 gpr_slice_buffer_addn(&tcp->write_slices, slices, nslices);
262
263 if (tcp->write_slices.count > GPR_ARRAY_SIZE(local_buffers)) {
264 buffers = (WSABUF *) gpr_malloc(sizeof(WSABUF) * tcp->write_slices.count);
265 allocated = buffers;
266 }
267
268 for (i = 0; i < tcp->write_slices.count; i++) {
269 buffers[i].len = GPR_SLICE_LENGTH(tcp->write_slices.slices[i]);
Nicolas Noblecfd60732015-03-18 16:27:43 -0700270 buffers[i].buf = (char *)GPR_SLICE_START_PTR(tcp->write_slices.slices[i]);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100271 }
272
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100273 status = WSASend(socket->socket, buffers, tcp->write_slices.count,
274 &bytes_sent, 0, NULL, NULL);
275 info->wsa_error = status == 0 ? 0 : WSAGetLastError();
276
277 if (info->wsa_error != WSAEWOULDBLOCK) {
278 grpc_endpoint_write_status ret = GRPC_ENDPOINT_WRITE_ERROR;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100279 if (status == 0) {
280 ret = GRPC_ENDPOINT_WRITE_DONE;
281 GPR_ASSERT(bytes_sent == tcp->write_slices.length);
Nicolas "Pixel" Nobleee0c96c2015-02-04 23:35:41 +0100282 } else {
283 char *utf8_message = gpr_format_message(info->wsa_error);
284 gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message);
285 gpr_free(utf8_message);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100286 }
287 if (allocated) gpr_free(allocated);
288 gpr_slice_buffer_reset_and_unref(&tcp->write_slices);
289 tcp->outstanding_write = 0;
290 tcp_unref(tcp);
291 return ret;
292 }
293
zeliard3874ad02015-04-30 16:05:45 +0900294 memset(&socket->write_info.overlapped, 0, sizeof(OVERLAPPED));
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100295 status = WSASend(socket->socket, buffers, tcp->write_slices.count,
296 &bytes_sent, 0, &socket->write_info.overlapped, NULL);
297 if (allocated) gpr_free(allocated);
298
299 if (status != 0) {
300 int error = WSAGetLastError();
301 if (error != WSA_IO_PENDING) {
302 char *utf8_message = gpr_format_message(WSAGetLastError());
303 __debugbreak();
304 gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message);
305 gpr_free(utf8_message);
306 /* would the IO completion port be called anyway ? Let's assume not. */
307 tcp->outstanding_write = 0;
308 tcp_unref(tcp);
309 return GRPC_ENDPOINT_WRITE_ERROR;
310 }
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100311 }
312
Nicolas Noble45e67a32015-02-09 16:20:49 -0800313 grpc_socket_notify_on_write(socket, on_write, tcp);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100314 return GRPC_ENDPOINT_WRITE_PENDING;
315}
316
317static void win_add_to_pollset(grpc_endpoint *ep, grpc_pollset *pollset) {
318 grpc_tcp *tcp = (grpc_tcp *) ep;
Nicolas Noble45e67a32015-02-09 16:20:49 -0800319 grpc_iocp_add_socket(tcp->socket);
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100320}
321
322static void win_shutdown(grpc_endpoint *ep) {
323 grpc_tcp *tcp = (grpc_tcp *) ep;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100324 grpc_winsocket_shutdown(tcp->socket);
325}
326
327static void win_destroy(grpc_endpoint *ep) {
328 grpc_tcp *tcp = (grpc_tcp *) ep;
Nicolas "Pixel" Noble21f627a2015-02-04 01:31:14 +0100329 tcp_unref(tcp);
330}
331
332static grpc_endpoint_vtable vtable = {
333 win_notify_on_read, win_write, win_add_to_pollset, win_shutdown, win_destroy
334};
335
336grpc_endpoint *grpc_tcp_create(grpc_winsocket *socket) {
337 grpc_tcp *tcp = (grpc_tcp *) gpr_malloc(sizeof(grpc_tcp));
338 memset(tcp, 0, sizeof(grpc_tcp));
339 tcp->base.vtable = &vtable;
340 tcp->socket = socket;
341 gpr_slice_buffer_init(&tcp->write_slices);
342 gpr_ref_init(&tcp->refcount, 1);
343 return &tcp->base;
344}
345
Craig Tiller190d3602015-02-18 09:23:38 -0800346#endif /* GPR_WINSOCK_SOCKET */