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