mirror of https://github.com/lukechilds/node.git
Ben Noordhuis
14 years ago
42 changed files with 6505 additions and 109 deletions
@ -0,0 +1,576 @@ |
|||||
|
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
|
* |
||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
* of this software and associated documentation files (the "Software"), to |
||||
|
* deal in the Software without restriction, including without limitation the |
||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
||||
|
* sell copies of the Software, and to permit persons to whom the Software is |
||||
|
* furnished to do so, subject to the following conditions: |
||||
|
* |
||||
|
* The above copyright notice and this permission notice shall be included in |
||||
|
* all copies or substantial portions of the Software. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
||||
|
* IN THE SOFTWARE. |
||||
|
*/ |
||||
|
|
||||
|
#include <assert.h> |
||||
|
|
||||
|
#include "uv.h" |
||||
|
#include "../uv-common.h" |
||||
|
#include "internal.h" |
||||
|
#include <stdio.h> |
||||
|
|
||||
|
#if 0 |
||||
|
/*
|
||||
|
* Threshold of active udp streams for which to preallocate udp read buffers. |
||||
|
*/ |
||||
|
const unsigned int uv_active_udp_streams_threshold = 0; |
||||
|
|
||||
|
/* A zero-size buffer for use by uv_udp_read */ |
||||
|
static char uv_zero_[] = ""; |
||||
|
#endif |
||||
|
|
||||
|
/* Counter to keep track of active udp streams */ |
||||
|
static unsigned int active_udp_streams = 0; |
||||
|
|
||||
|
|
||||
|
static int uv_udp_set_socket(uv_udp_t* handle, SOCKET socket) { |
||||
|
DWORD yes = 1; |
||||
|
|
||||
|
assert(handle->socket == INVALID_SOCKET); |
||||
|
|
||||
|
/* Set the socket to nonblocking mode */ |
||||
|
if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) { |
||||
|
uv_set_sys_error(WSAGetLastError()); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
/* Make the socket non-inheritable */ |
||||
|
if (!SetHandleInformation((HANDLE)socket, HANDLE_FLAG_INHERIT, 0)) { |
||||
|
uv_set_sys_error(GetLastError()); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
/* Associate it with the I/O completion port. */ |
||||
|
/* Use uv_handle_t pointer as completion key. */ |
||||
|
if (CreateIoCompletionPort((HANDLE)socket, |
||||
|
LOOP->iocp, |
||||
|
(ULONG_PTR)socket, |
||||
|
0) == NULL) { |
||||
|
uv_set_sys_error(GetLastError()); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
if (pSetFileCompletionNotificationModes) { |
||||
|
if (!pSetFileCompletionNotificationModes((HANDLE)socket, FILE_SKIP_SET_EVENT_ON_HANDLE | |
||||
|
FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) { |
||||
|
uv_set_sys_error(GetLastError()); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP; |
||||
|
} |
||||
|
|
||||
|
handle->socket = socket; |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
int uv_udp_init(uv_udp_t* handle) { |
||||
|
handle->type = UV_UDP; |
||||
|
handle->socket = INVALID_SOCKET; |
||||
|
handle->reqs_pending = 0; |
||||
|
handle->flags = 0; |
||||
|
|
||||
|
uv_req_init((uv_req_t*) &(handle->recv_req)); |
||||
|
handle->recv_req.type = UV_UDP_RECV; |
||||
|
handle->recv_req.data = handle; |
||||
|
|
||||
|
uv_ref(); |
||||
|
|
||||
|
uv_counters()->handle_init++; |
||||
|
uv_counters()->udp_init++; |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void uv_udp_endgame(uv_udp_t* handle) { |
||||
|
if (handle->flags & UV_HANDLE_CLOSING && |
||||
|
handle->reqs_pending == 0) { |
||||
|
assert(!(handle->flags & UV_HANDLE_CLOSED)); |
||||
|
handle->flags |= UV_HANDLE_CLOSED; |
||||
|
|
||||
|
if (handle->close_cb) { |
||||
|
handle->close_cb((uv_handle_t*)handle); |
||||
|
} |
||||
|
|
||||
|
uv_unref(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static int uv__bind(uv_udp_t* handle, int domain, struct sockaddr* addr, |
||||
|
int addrsize, unsigned int flags) { |
||||
|
DWORD err; |
||||
|
int r; |
||||
|
SOCKET sock; |
||||
|
|
||||
|
if ((flags & UV_UDP_IPV6ONLY) && domain != AF_INET6) { |
||||
|
/* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */ |
||||
|
uv_set_sys_error(UV_EINVAL); |
||||
|
} |
||||
|
|
||||
|
if (handle->socket == INVALID_SOCKET) { |
||||
|
sock = socket(domain, SOCK_DGRAM, 0); |
||||
|
if (sock == INVALID_SOCKET) { |
||||
|
uv_set_sys_error(WSAGetLastError()); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
if (uv_udp_set_socket(handle, sock) == -1) { |
||||
|
closesocket(sock); |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (domain == AF_INET6 && !(flags & UV_UDP_IPV6ONLY)) { |
||||
|
DWORD off = 0; |
||||
|
/* On windows IPV6ONLY is on by default. */ |
||||
|
/* If the user doesn't specify it libuv turns it off. */ |
||||
|
|
||||
|
/* TODO: how to handle errors? This may fail if there is no ipv4 stack */ |
||||
|
/* available, or when run on XP/2003 which have no support for dualstack */ |
||||
|
/* sockets. For now we're silently ignoring the error. */ |
||||
|
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*) &off, sizeof off); |
||||
|
} |
||||
|
|
||||
|
r = bind(handle->socket, addr, addrsize); |
||||
|
|
||||
|
if (r == SOCKET_ERROR) { |
||||
|
err = WSAGetLastError(); |
||||
|
uv_set_sys_error(WSAGetLastError()); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
handle->flags |= UV_HANDLE_BOUND; |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
int uv_udp_bind(uv_udp_t* handle, struct sockaddr_in addr, unsigned int flags) { |
||||
|
if (addr.sin_family != AF_INET) { |
||||
|
uv_set_sys_error(WSAEFAULT); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
return uv__bind(handle, |
||||
|
AF_INET, |
||||
|
(struct sockaddr*) &addr, |
||||
|
sizeof(struct sockaddr_in), |
||||
|
flags); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
int uv_udp_bind6(uv_udp_t* handle, struct sockaddr_in6 addr, unsigned int flags) { |
||||
|
if (addr.sin6_family != AF_INET6) { |
||||
|
uv_set_sys_error(WSAEFAULT); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
if (uv_allow_ipv6) { |
||||
|
handle->flags |= UV_HANDLE_IPV6; |
||||
|
return uv__bind(handle, |
||||
|
AF_INET6, |
||||
|
(struct sockaddr*) &addr, |
||||
|
sizeof(struct sockaddr_in6), |
||||
|
flags); |
||||
|
} else { |
||||
|
uv_new_sys_error(WSAEAFNOSUPPORT); |
||||
|
return -1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
int uv_udp_getsockname(uv_udp_t* handle, struct sockaddr* name, int* namelen) { |
||||
|
int result; |
||||
|
|
||||
|
if (handle->flags & UV_HANDLE_SHUTTING) { |
||||
|
uv_set_sys_error(WSAESHUTDOWN); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
result = getsockname(handle->socket, name, namelen); |
||||
|
if (result != 0) { |
||||
|
uv_set_sys_error(WSAGetLastError()); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void uv_udp_queue_recv(uv_udp_t* handle) { |
||||
|
uv_req_t* req; |
||||
|
uv_buf_t buf; |
||||
|
DWORD bytes, flags; |
||||
|
int result; |
||||
|
|
||||
|
assert(handle->flags & UV_HANDLE_READING); |
||||
|
assert(!(handle->flags & UV_HANDLE_READ_PENDING)); |
||||
|
|
||||
|
req = &handle->recv_req; |
||||
|
memset(&req->overlapped, 0, sizeof(req->overlapped)); |
||||
|
|
||||
|
/*
|
||||
|
* Preallocate a read buffer if the number of active streams is below |
||||
|
* the threshold. |
||||
|
*/ |
||||
|
#if 0 |
||||
|
if (active_udp_streams < uv_active_udp_streams_threshold) { |
||||
|
handle->flags &= ~UV_HANDLE_ZERO_READ; |
||||
|
#endif |
||||
|
handle->recv_buffer = handle->alloc_cb((uv_handle_t*) handle, 65536); |
||||
|
assert(handle->recv_buffer.len > 0); |
||||
|
|
||||
|
buf = handle->recv_buffer; |
||||
|
memset(&handle->recv_from, 0, sizeof handle->recv_from); |
||||
|
handle->recv_from_len = sizeof handle->recv_from; |
||||
|
flags = 0; |
||||
|
|
||||
|
result = WSARecvFrom(handle->socket, |
||||
|
(WSABUF*) &buf, |
||||
|
1, |
||||
|
&bytes, |
||||
|
&flags, |
||||
|
(struct sockaddr*) &handle->recv_from, |
||||
|
&handle->recv_from_len, |
||||
|
&req->overlapped, |
||||
|
NULL); |
||||
|
|
||||
|
if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { |
||||
|
/* Process the req without IOCP. */ |
||||
|
handle->flags |= UV_HANDLE_READ_PENDING; |
||||
|
req->overlapped.InternalHigh = bytes; |
||||
|
handle->reqs_pending++; |
||||
|
uv_insert_pending_req(req); |
||||
|
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { |
||||
|
/* The req will be processed with IOCP. */ |
||||
|
handle->flags |= UV_HANDLE_READ_PENDING; |
||||
|
handle->reqs_pending++; |
||||
|
} else { |
||||
|
/* Make this req pending reporting an error. */ |
||||
|
SET_REQ_ERROR(req, WSAGetLastError()); |
||||
|
uv_insert_pending_req(req); |
||||
|
handle->reqs_pending++; |
||||
|
} |
||||
|
#if 0 |
||||
|
} else { |
||||
|
handle->flags |= UV_HANDLE_ZERO_READ; |
||||
|
|
||||
|
buf.base = (char*) uv_zero_; |
||||
|
buf.len = 0; |
||||
|
flags = MSG_PARTIAL; |
||||
|
|
||||
|
result = WSARecv(handle->socket, |
||||
|
(WSABUF*) &buf, |
||||
|
1, |
||||
|
&bytes, |
||||
|
&flags, |
||||
|
&req->overlapped, |
||||
|
NULL); |
||||
|
|
||||
|
if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { |
||||
|
/* Process the req without IOCP. */ |
||||
|
handle->flags |= UV_HANDLE_READ_PENDING; |
||||
|
req->overlapped.InternalHigh = bytes; |
||||
|
handle->reqs_pending++; |
||||
|
uv_insert_pending_req(req); |
||||
|
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { |
||||
|
/* The req will be processed with IOCP. */ |
||||
|
handle->flags |= UV_HANDLE_READ_PENDING; |
||||
|
handle->reqs_pending++; |
||||
|
} else { |
||||
|
/* Make this req pending reporting an error. */ |
||||
|
SET_REQ_ERROR(req, WSAGetLastError()); |
||||
|
uv_insert_pending_req(req); |
||||
|
handle->reqs_pending++; |
||||
|
} |
||||
|
} |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
|
||||
|
int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb) { |
||||
|
if (handle->flags & UV_HANDLE_READING) { |
||||
|
uv_set_sys_error(WSAEALREADY); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
if (!(handle->flags & UV_HANDLE_BOUND) && |
||||
|
uv_udp_bind(handle, uv_addr_ip4_any_, 0) < 0) { |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
handle->flags |= UV_HANDLE_READING; |
||||
|
active_udp_streams++; |
||||
|
|
||||
|
handle->recv_cb = recv_cb; |
||||
|
handle->alloc_cb = alloc_cb; |
||||
|
|
||||
|
/* If reading was stopped and then started again, there could stell be a */ |
||||
|
/* recv request pending. */ |
||||
|
if (!(handle->flags & UV_HANDLE_READ_PENDING)) |
||||
|
uv_udp_queue_recv(handle); |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
int uv_udp_recv_stop(uv_udp_t* handle) { |
||||
|
if (handle->flags & UV_HANDLE_READING) { |
||||
|
handle->flags &= ~UV_HANDLE_READING; |
||||
|
active_udp_streams--; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
int uv_udp_connect6(uv_connect_t* req, uv_udp_t* handle, |
||||
|
struct sockaddr_in6 address, uv_connect_cb cb) { |
||||
|
int addrsize = sizeof(struct sockaddr_in6); |
||||
|
BOOL success; |
||||
|
DWORD bytes; |
||||
|
|
||||
|
if (!uv_allow_ipv6) { |
||||
|
uv_new_sys_error(WSAEAFNOSUPPORT); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
if (address.sin6_family != AF_INET6) { |
||||
|
uv_set_sys_error(WSAEFAULT); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
if (!(handle->flags & UV_HANDLE_BOUND) && |
||||
|
uv_udp_bind6(handle, uv_addr_ip6_any_, 0) < 0) |
||||
|
return -1; |
||||
|
|
||||
|
uv_req_init((uv_req_t*) req); |
||||
|
req->type = UV_CONNECT; |
||||
|
req->handle = (uv_stream_t*) handle; |
||||
|
req->cb = cb; |
||||
|
memset(&req->overlapped, 0, sizeof(req->overlapped)); |
||||
|
|
||||
|
success = pConnectEx6(handle->socket, |
||||
|
(struct sockaddr*) &address, |
||||
|
addrsize, |
||||
|
NULL, |
||||
|
0, |
||||
|
&bytes, |
||||
|
&req->overlapped); |
||||
|
|
||||
|
if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { |
||||
|
handle->reqs_pending++; |
||||
|
uv_insert_pending_req((uv_req_t*)req); |
||||
|
} else if (UV_SUCCEEDED_WITH_IOCP(success)) { |
||||
|
handle->reqs_pending++; |
||||
|
} else { |
||||
|
uv_set_sys_error(WSAGetLastError()); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], |
||||
|
int bufcnt, struct sockaddr* addr, int addr_len, uv_udp_send_cb cb) { |
||||
|
DWORD result, bytes; |
||||
|
|
||||
|
uv_req_init((uv_req_t*) req); |
||||
|
req->type = UV_UDP_SEND; |
||||
|
req->handle = handle; |
||||
|
req->cb = cb; |
||||
|
memset(&req->overlapped, 0, sizeof(req->overlapped)); |
||||
|
|
||||
|
result = WSASendTo(handle->socket, |
||||
|
(WSABUF*)bufs, |
||||
|
bufcnt, |
||||
|
&bytes, |
||||
|
0, |
||||
|
addr, |
||||
|
addr_len, |
||||
|
&req->overlapped, |
||||
|
NULL); |
||||
|
|
||||
|
if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { |
||||
|
/* Request completed immediately. */ |
||||
|
req->queued_bytes = 0; |
||||
|
handle->reqs_pending++; |
||||
|
uv_insert_pending_req((uv_req_t*)req); |
||||
|
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { |
||||
|
/* Request queued by the kernel. */ |
||||
|
req->queued_bytes = uv_count_bufs(bufs, bufcnt); |
||||
|
handle->reqs_pending++; |
||||
|
} else { |
||||
|
/* Send failed due to an error. */ |
||||
|
uv_set_sys_error(WSAGetLastError()); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
int uv_udp_send(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], |
||||
|
int bufcnt, struct sockaddr_in addr, uv_udp_send_cb cb) { |
||||
|
|
||||
|
if (!(handle->flags & UV_HANDLE_BOUND) && |
||||
|
uv_udp_bind(handle, uv_addr_ip4_any_, 0) < 0) { |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
return uv__udp_send(req, |
||||
|
handle, |
||||
|
bufs, |
||||
|
bufcnt, |
||||
|
(struct sockaddr*) &addr, |
||||
|
sizeof addr, |
||||
|
cb); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
int uv_udp_send6(uv_udp_send_t* req, uv_udp_t* handle, uv_buf_t bufs[], |
||||
|
int bufcnt, struct sockaddr_in6 addr, uv_udp_send_cb cb) { |
||||
|
|
||||
|
if (!(handle->flags & UV_HANDLE_BOUND) && |
||||
|
uv_udp_bind6(handle, uv_addr_ip6_any_, 0) < 0) { |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
return uv__udp_send(req, |
||||
|
handle, |
||||
|
bufs, |
||||
|
bufcnt, |
||||
|
(struct sockaddr*) &addr, |
||||
|
sizeof addr, |
||||
|
cb); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void uv_process_udp_recv_req(uv_udp_t* handle, uv_req_t* req) { |
||||
|
uv_buf_t buf; |
||||
|
int partial; |
||||
|
|
||||
|
assert(handle->type == UV_UDP); |
||||
|
|
||||
|
handle->flags &= ~UV_HANDLE_READ_PENDING; |
||||
|
|
||||
|
if (!REQ_SUCCESS(req) && |
||||
|
GET_REQ_STATUS(req) != STATUS_RECEIVE_EXPEDITED) { |
||||
|
/* An error occurred doing the read. */ |
||||
|
if ((handle->flags & UV_HANDLE_READING)) { |
||||
|
LOOP->last_error = GET_REQ_UV_SOCK_ERROR(req); |
||||
|
uv_udp_recv_stop(handle); |
||||
|
#if 0 |
||||
|
buf = (handle->flags & UV_HANDLE_ZERO_READ) ? |
||||
|
uv_buf_init(NULL, 0) : handle->recv_buffer; |
||||
|
#else |
||||
|
buf = handle->recv_buffer; |
||||
|
#endif |
||||
|
handle->recv_cb(handle, -1, buf, NULL, 0); |
||||
|
} |
||||
|
goto done; |
||||
|
} |
||||
|
|
||||
|
#if 0 |
||||
|
if (!(handle->flags & UV_HANDLE_ZERO_READ)) { |
||||
|
#endif |
||||
|
/* Successful read */ |
||||
|
partial = (GET_REQ_STATUS(req) == STATUS_RECEIVE_EXPEDITED); |
||||
|
handle->recv_cb(handle, |
||||
|
req->overlapped.InternalHigh, |
||||
|
handle->recv_buffer, |
||||
|
(struct sockaddr*) &handle->recv_from, |
||||
|
partial ? UV_UDP_PARTIAL : 0); |
||||
|
#if 0 |
||||
|
} else { |
||||
|
DWORD bytes, err, flags; |
||||
|
struct sockaddr_storage from; |
||||
|
int from_len; |
||||
|
|
||||
|
/* Do a nonblocking receive */ |
||||
|
/* TODO: try to read multiple datagrams at once. FIONREAD maybe? */ |
||||
|
buf = handle->alloc_cb((uv_handle_t*) handle, 65536); |
||||
|
assert(buf.len > 0); |
||||
|
|
||||
|
memset(&from, 0, sizeof from); |
||||
|
from_len = sizeof from; |
||||
|
flags = MSG_PARTIAL; |
||||
|
|
||||
|
if (WSARecvFrom(handle->socket, |
||||
|
(WSABUF*)&buf, |
||||
|
1, |
||||
|
&bytes, |
||||
|
&flags, |
||||
|
(struct sockaddr*) &from, |
||||
|
&from_len, |
||||
|
NULL, |
||||
|
NULL) != SOCKET_ERROR) { |
||||
|
|
||||
|
/* Message received */ |
||||
|
handle->recv_cb(handle, |
||||
|
bytes, |
||||
|
buf, |
||||
|
(struct sockaddr*) &from, |
||||
|
(flags & MSG_PARTIAL) ? UV_UDP_PARTIAL : 0); |
||||
|
} else { |
||||
|
err = WSAGetLastError(); |
||||
|
if (err == WSAEWOULDBLOCK) { |
||||
|
uv_set_sys_error(WSAEWOULDBLOCK); |
||||
|
handle->recv_cb(handle, 0, buf, NULL, 0); |
||||
|
} else { |
||||
|
/* Ouch! serious error. */ |
||||
|
uv_set_sys_error(err); |
||||
|
handle->recv_cb(handle, -1, buf, NULL, 0); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
done: |
||||
|
/* Post another read if still reading and not closing. */ |
||||
|
if ((handle->flags & UV_HANDLE_READING) && |
||||
|
!(handle->flags & UV_HANDLE_READ_PENDING)) { |
||||
|
uv_udp_queue_recv(handle); |
||||
|
} |
||||
|
|
||||
|
DECREASE_PENDING_REQ_COUNT(handle); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void uv_process_udp_send_req(uv_udp_t* handle, uv_udp_send_t* req) { |
||||
|
assert(handle->type == UV_UDP); |
||||
|
|
||||
|
if (req->cb) { |
||||
|
if (REQ_SUCCESS(req)) { |
||||
|
req->cb(req, 0); |
||||
|
} else { |
||||
|
LOOP->last_error = GET_REQ_UV_SOCK_ERROR(req); |
||||
|
req->cb(req, -1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
DECREASE_PENDING_REQ_COUNT(handle); |
||||
|
} |
||||
|
|
File diff suppressed because it is too large
@ -0,0 +1,247 @@ |
|||||
|
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
|
* |
||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
* of this software and associated documentation files (the "Software"), to |
||||
|
* deal in the Software without restriction, including without limitation the |
||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
||||
|
* sell copies of the Software, and to permit persons to whom the Software is |
||||
|
* furnished to do so, subject to the following conditions: |
||||
|
* |
||||
|
* The above copyright notice and this permission notice shall be included in |
||||
|
* all copies or substantial portions of the Software. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
||||
|
* IN THE SOFTWARE. |
||||
|
*/ |
||||
|
|
||||
|
#include "task.h" |
||||
|
#include "uv.h" |
||||
|
|
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
#define EXPECTED "RANG TANG DING DONG I AM THE JAPANESE SANDMAN" /* "Take eight!" */ |
||||
|
|
||||
|
#define TEST_DURATION 5000 /* ms */ |
||||
|
|
||||
|
#define MAX_SENDERS 1000 |
||||
|
#define MAX_RECEIVERS 1000 |
||||
|
|
||||
|
#define BASE_PORT 12345 |
||||
|
|
||||
|
#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) |
||||
|
|
||||
|
static int n_senders_; |
||||
|
static int n_receivers_; |
||||
|
static uv_udp_t senders[MAX_SENDERS]; |
||||
|
static uv_udp_t receivers[MAX_RECEIVERS]; |
||||
|
static uv_buf_t bufs[5]; |
||||
|
|
||||
|
static int send_cb_called; |
||||
|
static int recv_cb_called; |
||||
|
static int close_cb_called; |
||||
|
static int stopping = 0; |
||||
|
|
||||
|
typedef struct { |
||||
|
struct sockaddr_in addr; |
||||
|
} sender_state_t; |
||||
|
|
||||
|
|
||||
|
static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { |
||||
|
static char slab[65536]; |
||||
|
ASSERT(suggested_size <= sizeof slab); |
||||
|
return uv_buf_init(slab, sizeof slab); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void send_cb(uv_udp_send_t* req, int status) { |
||||
|
sender_state_t* ss; |
||||
|
int r; |
||||
|
|
||||
|
if (stopping) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ASSERT(req != NULL); |
||||
|
ASSERT(status == 0); |
||||
|
|
||||
|
ss = req->data; |
||||
|
|
||||
|
r = uv_udp_send(req, req->handle, bufs, ARRAY_SIZE(bufs), ss->addr, send_cb); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
req->data = ss; |
||||
|
|
||||
|
send_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void recv_cb(uv_udp_t* handle, |
||||
|
ssize_t nread, |
||||
|
uv_buf_t buf, |
||||
|
struct sockaddr* addr, |
||||
|
unsigned flags) { |
||||
|
if (nread == 0) |
||||
|
return; |
||||
|
|
||||
|
if (nread == -1) { |
||||
|
ASSERT(uv_last_error().code == UV_EINTR); /* FIXME change error code */ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ASSERT(addr->sa_family == AF_INET); |
||||
|
ASSERT(!memcmp(buf.base, EXPECTED, nread)); |
||||
|
|
||||
|
recv_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void close_cb(uv_handle_t* handle) { |
||||
|
ASSERT(handle != NULL); |
||||
|
close_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void timeout_cb(uv_timer_t* timer, int status) { |
||||
|
int i; |
||||
|
|
||||
|
stopping = 1; |
||||
|
|
||||
|
for (i = 0; i < n_senders_; i++) |
||||
|
uv_close((uv_handle_t*)&senders[i], close_cb); |
||||
|
|
||||
|
for (i = 0; i < n_receivers_; i++) |
||||
|
uv_close((uv_handle_t*)&receivers[i], close_cb); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static int do_packet_storm(int n_senders, int n_receivers) { |
||||
|
uv_timer_t timeout; |
||||
|
sender_state_t *ss; |
||||
|
uv_udp_send_t* req; |
||||
|
uv_udp_t* handle; |
||||
|
int i; |
||||
|
int r; |
||||
|
|
||||
|
ASSERT(n_senders <= MAX_SENDERS); |
||||
|
ASSERT(n_receivers <= MAX_RECEIVERS); |
||||
|
|
||||
|
uv_init(); |
||||
|
|
||||
|
n_senders_ = n_senders; |
||||
|
n_receivers_ = n_receivers; |
||||
|
|
||||
|
r = uv_timer_init(&timeout); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
r = uv_timer_start(&timeout, timeout_cb, TEST_DURATION, 0); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
/* Timer should not keep loop alive. */ |
||||
|
uv_unref(); |
||||
|
|
||||
|
for (i = 0; i < n_receivers; i++) { |
||||
|
struct sockaddr_in addr; |
||||
|
handle = &receivers[i]; |
||||
|
|
||||
|
r = uv_udp_init(handle); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
addr = uv_ip4_addr("0.0.0.0", BASE_PORT + i); |
||||
|
|
||||
|
r = uv_udp_bind(handle, addr, 0); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
r = uv_udp_recv_start(handle, alloc_cb, recv_cb); |
||||
|
ASSERT(r == 0); |
||||
|
} |
||||
|
|
||||
|
bufs[0] = uv_buf_init(EXPECTED + 0, 10); |
||||
|
bufs[1] = uv_buf_init(EXPECTED + 10, 10); |
||||
|
bufs[2] = uv_buf_init(EXPECTED + 20, 10); |
||||
|
bufs[3] = uv_buf_init(EXPECTED + 30, 10); |
||||
|
bufs[4] = uv_buf_init(EXPECTED + 40, 5); |
||||
|
|
||||
|
for (i = 0; i < n_senders; i++) { |
||||
|
handle = &senders[i]; |
||||
|
|
||||
|
r = uv_udp_init(handle); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
req = malloc(sizeof(*req) + sizeof(*ss)); |
||||
|
|
||||
|
ss = (void*)(req + 1); |
||||
|
ss->addr = uv_ip4_addr("127.0.0.1", BASE_PORT + (i % n_receivers)); |
||||
|
|
||||
|
r = uv_udp_send(req, handle, bufs, ARRAY_SIZE(bufs), ss->addr, send_cb); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
req->data = ss; |
||||
|
} |
||||
|
|
||||
|
uv_run(); |
||||
|
|
||||
|
printf("udp_packet_storm_%dv%d: %.0f/s received, %.0f/s sent\n", |
||||
|
n_receivers, |
||||
|
n_senders, |
||||
|
recv_cb_called / (TEST_DURATION / 1000.0), |
||||
|
send_cb_called / (TEST_DURATION / 1000.0)); |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
BENCHMARK_IMPL(udp_packet_storm_1v1) { |
||||
|
return do_packet_storm(1, 1); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
BENCHMARK_IMPL(udp_packet_storm_1v10) { |
||||
|
return do_packet_storm(1, 10); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
BENCHMARK_IMPL(udp_packet_storm_1v100) { |
||||
|
return do_packet_storm(1, 100); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
BENCHMARK_IMPL(udp_packet_storm_1v1000) { |
||||
|
return do_packet_storm(1, 1000); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
BENCHMARK_IMPL(udp_packet_storm_10v10) { |
||||
|
return do_packet_storm(10, 10); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
BENCHMARK_IMPL(udp_packet_storm_10v100) { |
||||
|
return do_packet_storm(10, 100); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
BENCHMARK_IMPL(udp_packet_storm_10v1000) { |
||||
|
return do_packet_storm(10, 1000); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
BENCHMARK_IMPL(udp_packet_storm_100v100) { |
||||
|
return do_packet_storm(100, 100); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
BENCHMARK_IMPL(udp_packet_storm_100v1000) { |
||||
|
return do_packet_storm(100, 1000); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
BENCHMARK_IMPL(udp_packet_storm_1000v1000) { |
||||
|
return do_packet_storm(1000, 1000); |
||||
|
} |
@ -0,0 +1,88 @@ |
|||||
|
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
|
* |
||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
* of this software and associated documentation files (the "Software"), to |
||||
|
* deal in the Software without restriction, including without limitation the |
||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
||||
|
* sell copies of the Software, and to permit persons to whom the Software is |
||||
|
* furnished to do so, subject to the following conditions: |
||||
|
* |
||||
|
* The above copyright notice and this permission notice shall be included in |
||||
|
* all copies or substantial portions of the Software. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
||||
|
* IN THE SOFTWARE. |
||||
|
*/ |
||||
|
|
||||
|
#include "uv.h" |
||||
|
#include "task.h" |
||||
|
|
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
#define CHECK_HANDLE(handle) \ |
||||
|
ASSERT((uv_udp_t*)(handle) == &handle_) |
||||
|
|
||||
|
#define CHECK_REQ(req) \ |
||||
|
ASSERT((req) == &req_); |
||||
|
|
||||
|
static uv_udp_t handle_; |
||||
|
static uv_udp_send_t req_; |
||||
|
|
||||
|
static int send_cb_called; |
||||
|
static int close_cb_called; |
||||
|
|
||||
|
|
||||
|
static void close_cb(uv_handle_t* handle) { |
||||
|
CHECK_HANDLE(handle); |
||||
|
close_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void send_cb(uv_udp_send_t* req, int status) { |
||||
|
CHECK_REQ(req); |
||||
|
CHECK_HANDLE(req->handle); |
||||
|
|
||||
|
ASSERT(status == -1); |
||||
|
ASSERT(uv_last_error().code == UV_EMSGSIZE); |
||||
|
|
||||
|
uv_close((uv_handle_t*)req->handle, close_cb); |
||||
|
send_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
TEST_IMPL(udp_dgram_too_big) { |
||||
|
char dgram[65536]; /* 64K MTU is unlikely, even on localhost */ |
||||
|
struct sockaddr_in addr; |
||||
|
uv_buf_t buf; |
||||
|
int r; |
||||
|
|
||||
|
memset(dgram, 42, sizeof dgram); /* silence valgrind */ |
||||
|
|
||||
|
uv_init(); |
||||
|
|
||||
|
r = uv_udp_init(&handle_); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
buf = uv_buf_init(dgram, sizeof dgram); |
||||
|
addr = uv_ip4_addr("127.0.0.1", TEST_PORT); |
||||
|
|
||||
|
r = uv_udp_send(&req_, &handle_, &buf, 1, addr, send_cb); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
ASSERT(close_cb_called == 0); |
||||
|
ASSERT(send_cb_called == 0); |
||||
|
|
||||
|
uv_run(); |
||||
|
|
||||
|
ASSERT(send_cb_called == 1); |
||||
|
ASSERT(close_cb_called == 1); |
||||
|
|
||||
|
return 0; |
||||
|
} |
@ -0,0 +1,141 @@ |
|||||
|
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
|
* |
||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
* of this software and associated documentation files (the "Software"), to |
||||
|
* deal in the Software without restriction, including without limitation the |
||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
||||
|
* sell copies of the Software, and to permit persons to whom the Software is |
||||
|
* furnished to do so, subject to the following conditions: |
||||
|
* |
||||
|
* The above copyright notice and this permission notice shall be included in |
||||
|
* all copies or substantial portions of the Software. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
||||
|
* IN THE SOFTWARE. |
||||
|
*/ |
||||
|
|
||||
|
#include "uv.h" |
||||
|
#include "task.h" |
||||
|
|
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
#define CHECK_HANDLE(handle) \ |
||||
|
ASSERT((uv_udp_t*)(handle) == &server1 \ |
||||
|
|| (uv_udp_t*)(handle) == &server2 \ |
||||
|
|| (uv_udp_t*)(handle) == &client) |
||||
|
|
||||
|
#define CHECK_REQ(req) \ |
||||
|
ASSERT((req) == &req_); |
||||
|
|
||||
|
static uv_udp_t client; |
||||
|
static uv_udp_t server1; |
||||
|
static uv_udp_t server2; |
||||
|
static uv_udp_send_t req_; |
||||
|
|
||||
|
static int send_cb_called; |
||||
|
static int recv_cb_called; |
||||
|
static int close_cb_called; |
||||
|
|
||||
|
|
||||
|
static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { |
||||
|
static char slab[65536]; |
||||
|
CHECK_HANDLE(handle); |
||||
|
return uv_buf_init(slab, sizeof slab); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void close_cb(uv_handle_t* handle) { |
||||
|
CHECK_HANDLE(handle); |
||||
|
close_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void send_cb(uv_udp_send_t* req, int status) { |
||||
|
CHECK_REQ(req); |
||||
|
CHECK_HANDLE(req->handle); |
||||
|
ASSERT(status == 0); |
||||
|
send_cb_called++; |
||||
|
} |
||||
|
|
||||
|
static void ipv4_recv_cb(uv_udp_t* handle, |
||||
|
ssize_t nread, |
||||
|
uv_buf_t buf, |
||||
|
struct sockaddr* addr, |
||||
|
unsigned flags) { |
||||
|
CHECK_HANDLE(handle); |
||||
|
ASSERT(nread >= 0); |
||||
|
|
||||
|
uv_close((uv_handle_t*)&server1, close_cb); |
||||
|
uv_close((uv_handle_t*)&server2, close_cb); |
||||
|
uv_close((uv_handle_t*)&client, close_cb); |
||||
|
|
||||
|
recv_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void ipv6_recv_cb(uv_udp_t* handle, |
||||
|
ssize_t nread, |
||||
|
uv_buf_t buf, |
||||
|
struct sockaddr* addr, |
||||
|
unsigned flags) { |
||||
|
ASSERT(0 && "this function should not have been called"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
TEST_IMPL(udp_ipv6_only) { |
||||
|
struct sockaddr_in6 addr6; |
||||
|
struct sockaddr_in addr; |
||||
|
uv_buf_t buf; |
||||
|
int r; |
||||
|
|
||||
|
uv_init(); |
||||
|
|
||||
|
addr = uv_ip4_addr("127.0.0.1", TEST_PORT); |
||||
|
addr6 = uv_ip6_addr(":::0", TEST_PORT); |
||||
|
|
||||
|
r = uv_udp_init(&server1); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
r = uv_udp_bind(&server1, addr, 0); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
r = uv_udp_recv_start(&server1, alloc_cb, ipv4_recv_cb); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
r = uv_udp_init(&server2); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
r = uv_udp_bind6(&server2, addr6, UV_UDP_IPV6ONLY); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
r = uv_udp_recv_start(&server2, alloc_cb, ipv6_recv_cb); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
r = uv_udp_init(&client); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
buf = uv_buf_init("PING", 4); |
||||
|
|
||||
|
/* This should fail but still call send_cb(). */ |
||||
|
r = uv_udp_send(&req_, &client, &buf, 1, addr, send_cb); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
ASSERT(close_cb_called == 0); |
||||
|
ASSERT(send_cb_called == 0); |
||||
|
ASSERT(recv_cb_called == 0); |
||||
|
|
||||
|
uv_run(); |
||||
|
|
||||
|
ASSERT(recv_cb_called == 1); |
||||
|
ASSERT(send_cb_called == 1); |
||||
|
ASSERT(close_cb_called == 3); |
||||
|
|
||||
|
return 0; |
||||
|
} |
@ -0,0 +1,158 @@ |
|||||
|
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
|
* |
||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
* of this software and associated documentation files (the "Software"), to |
||||
|
* deal in the Software without restriction, including without limitation the |
||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
||||
|
* sell copies of the Software, and to permit persons to whom the Software is |
||||
|
* furnished to do so, subject to the following conditions: |
||||
|
* |
||||
|
* The above copyright notice and this permission notice shall be included in |
||||
|
* all copies or substantial portions of the Software. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
||||
|
* IN THE SOFTWARE. |
||||
|
*/ |
||||
|
|
||||
|
#include "uv.h" |
||||
|
#include "task.h" |
||||
|
|
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
#define CHECK_HANDLE(handle) \ |
||||
|
ASSERT((uv_udp_t*)(handle) == &server \ |
||||
|
|| (uv_udp_t*)(handle) == &client \ |
||||
|
|| (uv_timer_t*)(handle) == &timeout) |
||||
|
|
||||
|
#define CHECK_REQ(req) \ |
||||
|
ASSERT((req) == &req_); |
||||
|
|
||||
|
static uv_udp_t client; |
||||
|
static uv_udp_t server; |
||||
|
static uv_udp_send_t req_; |
||||
|
static uv_timer_t timeout; |
||||
|
|
||||
|
static int send_cb_called; |
||||
|
static int recv_cb_called; |
||||
|
static int close_cb_called; |
||||
|
|
||||
|
|
||||
|
static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { |
||||
|
static char slab[65536]; |
||||
|
CHECK_HANDLE(handle); |
||||
|
return uv_buf_init(slab, sizeof slab); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void close_cb(uv_handle_t* handle) { |
||||
|
CHECK_HANDLE(handle); |
||||
|
close_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void send_cb(uv_udp_send_t* req, int status) { |
||||
|
CHECK_REQ(req); |
||||
|
CHECK_HANDLE(req->handle); |
||||
|
ASSERT(status == 0); |
||||
|
send_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void ipv6_recv_fail(uv_udp_t* handle, |
||||
|
ssize_t nread, |
||||
|
uv_buf_t buf, |
||||
|
struct sockaddr* addr, |
||||
|
unsigned flags) { |
||||
|
ASSERT(0 && "this function should not have been called"); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void ipv6_recv_ok(uv_udp_t* handle, |
||||
|
ssize_t nread, |
||||
|
uv_buf_t buf, |
||||
|
struct sockaddr* addr, |
||||
|
unsigned flags) { |
||||
|
CHECK_HANDLE(handle); |
||||
|
ASSERT(nread >= 0); |
||||
|
|
||||
|
if (nread) |
||||
|
recv_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void timeout_cb(uv_timer_t* timer, int status) { |
||||
|
uv_close((uv_handle_t*)&server, close_cb); |
||||
|
uv_close((uv_handle_t*)&client, close_cb); |
||||
|
uv_close((uv_handle_t*)&timeout, close_cb); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void do_test(uv_udp_recv_cb recv_cb, int bind_flags) { |
||||
|
struct sockaddr_in6 addr6; |
||||
|
struct sockaddr_in addr; |
||||
|
uv_buf_t buf; |
||||
|
int r; |
||||
|
|
||||
|
uv_init(); |
||||
|
|
||||
|
addr6 = uv_ip6_addr("::0", TEST_PORT); |
||||
|
|
||||
|
r = uv_udp_init(&server); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
r = uv_udp_bind6(&server, addr6, bind_flags); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
r = uv_udp_recv_start(&server, alloc_cb, recv_cb); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
r = uv_udp_init(&client); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
buf = uv_buf_init("PING", 4); |
||||
|
addr = uv_ip4_addr("127.0.0.1", TEST_PORT); |
||||
|
|
||||
|
r = uv_udp_send(&req_, &client, &buf, 1, addr, send_cb); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
r = uv_timer_init(&timeout); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
r = uv_timer_start(&timeout, timeout_cb, 500, 0); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
ASSERT(close_cb_called == 0); |
||||
|
ASSERT(send_cb_called == 0); |
||||
|
ASSERT(recv_cb_called == 0); |
||||
|
|
||||
|
uv_run(); |
||||
|
|
||||
|
ASSERT(close_cb_called == 3); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
TEST_IMPL(udp_dual_stack) { |
||||
|
do_test(ipv6_recv_ok, 0); |
||||
|
|
||||
|
ASSERT(recv_cb_called == 1); |
||||
|
ASSERT(send_cb_called == 1); |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
TEST_IMPL(udp_ipv6_only) { |
||||
|
do_test(ipv6_recv_fail, UV_UDP_IPV6ONLY); |
||||
|
|
||||
|
ASSERT(recv_cb_called == 0); |
||||
|
ASSERT(send_cb_called == 1); |
||||
|
|
||||
|
return 0; |
||||
|
} |
@ -0,0 +1,210 @@ |
|||||
|
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
|
* |
||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
* of this software and associated documentation files (the "Software"), to |
||||
|
* deal in the Software without restriction, including without limitation the |
||||
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
||||
|
* sell copies of the Software, and to permit persons to whom the Software is |
||||
|
* furnished to do so, subject to the following conditions: |
||||
|
* |
||||
|
* The above copyright notice and this permission notice shall be included in |
||||
|
* all copies or substantial portions of the Software. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
||||
|
* IN THE SOFTWARE. |
||||
|
*/ |
||||
|
|
||||
|
#include "uv.h" |
||||
|
#include "task.h" |
||||
|
|
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
#define CHECK_HANDLE(handle) \ |
||||
|
ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) |
||||
|
|
||||
|
static uv_udp_t server; |
||||
|
static uv_udp_t client; |
||||
|
|
||||
|
static int cl_send_cb_called; |
||||
|
static int cl_recv_cb_called; |
||||
|
|
||||
|
static int sv_send_cb_called; |
||||
|
static int sv_recv_cb_called; |
||||
|
|
||||
|
static int close_cb_called; |
||||
|
|
||||
|
|
||||
|
static uv_buf_t alloc_cb(uv_handle_t* handle, size_t suggested_size) { |
||||
|
static char slab[65536]; |
||||
|
|
||||
|
CHECK_HANDLE(handle); |
||||
|
ASSERT(suggested_size <= sizeof slab); |
||||
|
|
||||
|
return uv_buf_init(slab, sizeof slab); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void close_cb(uv_handle_t* handle) { |
||||
|
CHECK_HANDLE(handle); |
||||
|
close_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void cl_recv_cb(uv_udp_t* handle, |
||||
|
ssize_t nread, |
||||
|
uv_buf_t buf, |
||||
|
struct sockaddr* addr, |
||||
|
unsigned flags) { |
||||
|
CHECK_HANDLE(handle); |
||||
|
ASSERT(flags == 0); |
||||
|
|
||||
|
if (nread < 0) { |
||||
|
ASSERT(0 && "unexpected error"); |
||||
|
} |
||||
|
|
||||
|
if (nread == 0) { |
||||
|
/* Returning unused buffer */ |
||||
|
/* Don't count towards cl_recv_cb_called */ |
||||
|
ASSERT(addr == NULL); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ASSERT(addr != NULL); |
||||
|
ASSERT(nread == 4); |
||||
|
ASSERT(!memcmp("PONG", buf.base, nread)); |
||||
|
|
||||
|
cl_recv_cb_called++; |
||||
|
|
||||
|
uv_close((uv_handle_t*) handle, close_cb); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void cl_send_cb(uv_udp_send_t* req, int status) { |
||||
|
int r; |
||||
|
|
||||
|
ASSERT(req != NULL); |
||||
|
ASSERT(status == 0); |
||||
|
CHECK_HANDLE(req->handle); |
||||
|
|
||||
|
r = uv_udp_recv_start(req->handle, alloc_cb, cl_recv_cb); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
cl_send_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void sv_send_cb(uv_udp_send_t* req, int status) { |
||||
|
ASSERT(req != NULL); |
||||
|
ASSERT(status == 0); |
||||
|
CHECK_HANDLE(req->handle); |
||||
|
|
||||
|
uv_close((uv_handle_t*) req->handle, close_cb); |
||||
|
free(req); |
||||
|
|
||||
|
sv_send_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
static void sv_recv_cb(uv_udp_t* handle, |
||||
|
ssize_t nread, |
||||
|
uv_buf_t buf, |
||||
|
struct sockaddr* addr, |
||||
|
unsigned flags) { |
||||
|
uv_udp_send_t* req; |
||||
|
int r; |
||||
|
|
||||
|
if (nread < 0) { |
||||
|
ASSERT(0 && "unexpected error"); |
||||
|
} |
||||
|
|
||||
|
if (nread == 0) { |
||||
|
/* Returning unused buffer */ |
||||
|
/* Don't count towards sv_recv_cb_called */ |
||||
|
ASSERT(addr == NULL); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
CHECK_HANDLE(handle); |
||||
|
ASSERT(flags == 0); |
||||
|
|
||||
|
ASSERT(addr != NULL); |
||||
|
ASSERT(nread == 4); |
||||
|
ASSERT(!memcmp("PING", buf.base, nread)); |
||||
|
|
||||
|
/* FIXME? `uv_udp_recv_stop` does what it says: recv_cb is not called
|
||||
|
* anymore. That's problematic because the read buffer won't be returned |
||||
|
* either... Not sure I like that but it's consistent with `uv_read_stop`. |
||||
|
*/ |
||||
|
r = uv_udp_recv_stop(handle); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
req = malloc(sizeof *req); |
||||
|
ASSERT(req != NULL); |
||||
|
|
||||
|
buf = uv_buf_init("PONG", 4); |
||||
|
|
||||
|
r = uv_udp_send(req, |
||||
|
handle, |
||||
|
&buf, |
||||
|
1, |
||||
|
*(struct sockaddr_in*)addr, |
||||
|
sv_send_cb); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
sv_recv_cb_called++; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
TEST_IMPL(udp_send_and_recv) { |
||||
|
struct sockaddr_in addr; |
||||
|
uv_udp_send_t req; |
||||
|
uv_buf_t buf; |
||||
|
int r; |
||||
|
|
||||
|
addr = uv_ip4_addr("0.0.0.0", TEST_PORT); |
||||
|
|
||||
|
uv_init(); |
||||
|
|
||||
|
r = uv_udp_init(&server); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
r = uv_udp_bind(&server, addr, 0); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
addr = uv_ip4_addr("127.0.0.1", TEST_PORT); |
||||
|
|
||||
|
r = uv_udp_init(&client); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
/* client sends "PING", expects "PONG" */ |
||||
|
buf = uv_buf_init("PING", 4); |
||||
|
|
||||
|
r = uv_udp_send(&req, &client, &buf, 1, addr, cl_send_cb); |
||||
|
ASSERT(r == 0); |
||||
|
|
||||
|
ASSERT(close_cb_called == 0); |
||||
|
ASSERT(cl_send_cb_called == 0); |
||||
|
ASSERT(cl_recv_cb_called == 0); |
||||
|
ASSERT(sv_send_cb_called == 0); |
||||
|
ASSERT(sv_recv_cb_called == 0); |
||||
|
|
||||
|
uv_run(); |
||||
|
|
||||
|
ASSERT(cl_send_cb_called == 1); |
||||
|
ASSERT(cl_recv_cb_called == 1); |
||||
|
ASSERT(sv_send_cb_called == 1); |
||||
|
ASSERT(sv_recv_cb_called == 1); |
||||
|
ASSERT(close_cb_called == 2); |
||||
|
|
||||
|
return 0; |
||||
|
} |
Loading…
Reference in new issue