Browse Source

Merge branch 'v0.9.2-release'

v0.9.3-release
isaacs 13 years ago
parent
commit
7fcb7b4d9c
  1. 7
      AUTHORS
  2. 45
      ChangeLog
  3. 4
      deps/openssl/openssl.gyp
  4. 3
      deps/uv/include/uv-private/uv-darwin.h
  5. 3
      deps/uv/include/uv-private/uv-unix.h
  6. 21
      deps/uv/include/uv.h
  7. 56
      deps/uv/src/unix/fsevents.c
  8. 6
      deps/uv/src/unix/kqueue.c
  9. 3
      deps/uv/src/unix/linux/inotify.c
  10. 8
      deps/uv/src/unix/pipe.c
  11. 47
      deps/uv/src/unix/stream.c
  12. 2
      deps/uv/src/unix/sunos.c
  13. 26
      deps/uv/src/unix/tcp.c
  14. 68
      deps/uv/src/unix/udp.c
  15. 3
      deps/uv/src/win/fs-event.c
  16. 6
      deps/uv/src/win/pipe.c
  17. 15
      deps/uv/src/win/tcp.c
  18. 22
      deps/uv/src/win/udp.c
  19. 8
      deps/uv/test/benchmark-list.h
  20. 436
      deps/uv/test/benchmark-multi-accept.c
  21. 5
      deps/uv/test/dns-server.c
  22. 3
      deps/uv/test/test-fs-event.c
  23. 8
      deps/uv/test/test-list.h
  24. 175
      deps/uv/test/test-tcp-open.c
  25. 154
      deps/uv/test/test-udp-open.c
  26. 3
      deps/uv/uv.gyp
  27. 381
      lib/url.js
  28. 2
      src/node_version.h
  29. 14
      test/simple/test-fs-watch.js
  30. 30
      test/simple/test-url.js

7
AUTHORS

@ -357,3 +357,10 @@ Gil Pedersen <git@gpost.dk>
Tyler Neylon <tylerneylon@gmail.com>
Golo Roden <webmaster@goloroden.de>
Ron Korving <rkorving@wizcorp.jp>
Brandon Wilson <chlavois@gmail.com>
Ian Babrou <ibobrik@gmail.com>
Bearice Ren <bearice@gmail.com>
Ankur Oberoi <aoberoi@gmail.com>
Atsuya Takagi <atsuya.takagi@gmail.com>
Pooya Karimian <pkarimian@sencha.com>
Frédéric Germain <frederic.germain@gmail.com>

45
ChangeLog

@ -1,4 +1,47 @@
2012.08.28, Version 0.9.1 (Unstable)
2012.09.17, Version 0.9.2 (Unstable)
* http_parser: upgrade to ad3b631
* openssl: upgrade 1.0.1c
* darwin: use FSEvents to watch directory changes (Fedor Indutny)
* unix: support missing API on NetBSD (Shigeki Ohtsu)
* unix: fix EMFILE busy loop (Ben Noordhuis)
* windows: un-break writable tty handles (Bert Belder)
* windows: map WSAESHUTDOWN to UV_EPIPE (Bert Belder)
* windows: make spawn with custom environment work again (Bert Belder)
* windows: map ERROR_DIRECTORY to UV_ENOENT (Bert Belder)
* tls, https: validate server certificate by default (Ben Noordhuis)
* tls, https: throw exception on missing key/cert (Ben Noordhuis)
* tls: async session storage (Fedor Indutny)
* installer: don't install header files (Ben Noordhuis)
* buffer: implement Buffer.prototype.toJSON() (Nathan Rajlich)
* buffer: added support for writing NaN and Infinity (koichik)
* http: make http.ServerResponse emit 'end' (Ben Noordhuis)
* build: ./configure --ninja (Ben Noordhuis, Timothy J Fontaine)
* installer: fix --without-npm (Ben Noordhuis)
* cli: make -p equivalent to -pe (Ben Noordhuis)
* url: Go much faster by using Url class (isaacs)
2012.08.28, Version 0.9.1 (Unstable), e6ce259d2caf338fec991c2dd447de763ce99ab7
* buffer: Add Buffer.isEncoding(enc) to test for valid encoding values (isaacs)

4
deps/openssl/openssl.gyp

@ -18,6 +18,7 @@
'OPENSSL_NO_SCTP',
'OPENSSL_NO_SOCK',
# Work around brain dead SunOS linker.
'OPENSSL_NO_RDRAND',
'OPENSSL_NO_GOST',
'OPENSSL_NO_HW_PADLOCK',
'OPENSSL_NO_TTY',
@ -353,7 +354,7 @@
'openssl/crypto/engine/eng_list.c',
'openssl/crypto/engine/eng_openssl.c',
'openssl/crypto/engine/eng_pkey.c',
'openssl/crypto/engine/eng_rdrand.c',
#'openssl/crypto/engine/eng_rdrand.c',
'openssl/crypto/engine/eng_rsax.c',
'openssl/crypto/engine/eng_table.c',
'openssl/crypto/engine/tb_asnmth.c',
@ -557,7 +558,6 @@
'openssl/crypto/sha/sha_one.c',
'openssl/crypto/srp/srp_lib.c',
'openssl/crypto/srp/srp_vfy.c',
'openssl/crypto/srp/srptest.c',
'openssl/crypto/stack/stack.c',
'openssl/crypto/store/str_err.c',
'openssl/crypto/store/str_lib.c',

3
deps/uv/include/uv-private/uv-darwin.h

@ -41,6 +41,9 @@
ev_io event_watcher; \
int fflags; \
int fd; \
char* realpath; \
int realpath_len; \
int cf_flags; \
void* cf_eventstream; \
uv_async_t* cf_cb; \
ngx_queue_t cf_events; \

3
deps/uv/include/uv-private/uv-unix.h

@ -185,8 +185,7 @@ typedef struct {
int fd; \
UV_STREAM_PRIVATE_PLATFORM_FIELDS \
#define UV_TCP_PRIVATE_FIELDS \
uv_idle_t* idle_handle; /* for UV_TCP_SINGLE_ACCEPT handles */ \
#define UV_TCP_PRIVATE_FIELDS /* empty */
#define UV_UDP_PRIVATE_FIELDS \
int fd; \

21
deps/uv/include/uv.h

@ -599,6 +599,11 @@ struct uv_tcp_s {
UV_EXTERN int uv_tcp_init(uv_loop_t*, uv_tcp_t* handle);
/*
* Opens an existing file descriptor or SOCKET as a tcp handle.
*/
UV_EXTERN int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock);
/* Enable/disable Nagle's algorithm. */
UV_EXTERN int uv_tcp_nodelay(uv_tcp_t* handle, int enable);
@ -703,6 +708,11 @@ struct uv_udp_send_s {
*/
UV_EXTERN int uv_udp_init(uv_loop_t*, uv_udp_t* handle);
/*
* Opens an existing file descriptor or SOCKET as a udp handle.
*/
UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock);
/*
* Bind to a IPv4 address and port.
*
@ -940,7 +950,7 @@ UV_EXTERN int uv_pipe_init(uv_loop_t*, uv_pipe_t* handle, int ipc);
/*
* Opens an existing file descriptor or HANDLE as a pipe.
*/
UV_EXTERN void uv_pipe_open(uv_pipe_t*, uv_file file);
UV_EXTERN int uv_pipe_open(uv_pipe_t*, uv_file file);
UV_EXTERN int uv_pipe_bind(uv_pipe_t* handle, const char* name);
@ -1665,7 +1675,14 @@ enum uv_fs_event_flags {
* regular interval.
* This flag is currently not implemented yet on any backend.
*/
UV_FS_EVENT_STAT = 2
UV_FS_EVENT_STAT = 2,
/*
* By default, event watcher, when watching directory, is not registering
* (is ignoring) changes in it's subdirectories.
* This flag will override this behaviour on platforms that support it.
*/
UV_FS_EVENT_RECURSIVE = 3
};

56
deps/uv/src/unix/fsevents.c

@ -66,8 +66,13 @@ void uv__fsevents_cb(uv_async_t* cb, int status) {
handle = cb->data;
UV__FSEVENTS_WALK(handle, {
if (handle->fd != -1)
if (handle->fd != -1) {
#ifdef MAC_OS_X_VERSION_10_7
handle->cb(handle, event->path, event->events, 0);
#else
handle->cb(handle, NULL, event->events, 0);
#endif /* MAC_OS_X_VERSION_10_7 */
}
})
if ((handle->flags & (UV_CLOSING | UV_CLOSED)) == 0 && handle->fd == -1)
@ -84,6 +89,8 @@ void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,
size_t i;
int len;
char** paths;
char* path;
char* pos;
uv_fs_event_t* handle;
uv__fsevents_event_t* event;
ngx_queue_t add_list;
@ -99,17 +106,50 @@ void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,
kFSEventStreamEventFlagEventIdsWrapped |
kFSEventStreamEventFlagHistoryDone |
kFSEventStreamEventFlagMount |
kFSEventStreamEventFlagUnmount)) {
kFSEventStreamEventFlagUnmount |
kFSEventStreamEventFlagRootChanged)) {
continue;
}
/* TODO: Report errors */
len = strlen(paths[i]);
path = paths[i];
len = strlen(path);
/* Remove absolute path prefix */
if (strstr(path, handle->realpath) == path) {
path += handle->realpath_len;
len -= handle->realpath_len;
/* Skip back slash */
if (*path != 0) {
path++;
len--;
}
}
#ifdef MAC_OS_X_VERSION_10_7
/* Ignore events with path equal to directory itself */
if (len == 0)
continue;
#endif /* MAC_OS_X_VERSION_10_7 */
/* Do not emit events from subdirectories (without option set) */
pos = strchr(path, '/');
if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0 &&
pos != NULL &&
pos != path + 1)
continue;
#ifndef MAC_OS_X_VERSION_10_7
path = "";
len = 0;
#endif /* MAC_OS_X_VERSION_10_7 */
event = malloc(sizeof(*event) + len);
if (event == NULL)
break;
memcpy(event->path, paths[i], len + 1);
memcpy(event->path, path, len + 1);
if (eventFlags[i] & kFSEventStreamEventFlagItemModified)
event->events = UV_CHANGE;
@ -153,6 +193,11 @@ int uv__fsevents_init(uv_fs_event_t* handle) {
ctx.release = NULL;
ctx.copyDescription = NULL;
/* Get absolute path to file */
handle->realpath = realpath(handle->filename, NULL);
if (handle->realpath != NULL)
handle->realpath_len = strlen(handle->realpath);
/* Initialize paths array */
path = CFStringCreateWithCString(NULL,
handle->filename,
@ -220,6 +265,9 @@ int uv__fsevents_close(uv_fs_event_t* handle) {
uv_mutex_destroy(&handle->cf_mutex);
uv_sem_destroy(&handle->cf_sem);
free(handle->realpath);
handle->realpath = NULL;
handle->realpath_len = 0;
return 0;
}

6
deps/uv/src/unix/kqueue.c

@ -93,9 +93,6 @@ int uv_fs_event_init(uv_loop_t* loop,
struct stat statbuf;
#endif /* defined(__APPLE__) */
/* We don't support any flags yet. */
assert(!flags);
/* TODO open asynchronously - but how do we report back errors? */
if ((fd = open(filename, O_RDONLY)) == -1) {
uv__set_sys_error(loop, errno);
@ -112,6 +109,9 @@ int uv_fs_event_init(uv_loop_t* loop,
#if defined(__APPLE__)
/* Nullify field to perform checks later */
handle->cf_eventstream = NULL;
handle->realpath = NULL;
handle->realpath_len = 0;
handle->cf_flags = flags;
if (fstat(fd, &statbuf))
goto fallback;

3
deps/uv/src/unix/linux/inotify.c

@ -176,9 +176,6 @@ int uv_fs_event_init(uv_loop_t* loop,
int events;
int wd;
/* We don't support any flags yet. */
assert(!flags);
if (init_inotify(loop)) return -1;
events = UV__IN_ATTRIB

8
deps/uv/src/unix/pipe.c

@ -156,10 +156,10 @@ void uv__pipe_close(uv_pipe_t* handle) {
}
void uv_pipe_open(uv_pipe_t* handle, uv_file fd) {
uv__stream_open((uv_stream_t*)handle,
fd,
UV_STREAM_READABLE | UV_STREAM_WRITABLE);
int uv_pipe_open(uv_pipe_t* handle, uv_file fd) {
return uv__stream_open((uv_stream_t*)handle,
fd,
UV_STREAM_READABLE | UV_STREAM_WRITABLE);
}

47
deps/uv/src/unix/stream.c

@ -386,16 +386,6 @@ void uv__stream_destroy(uv_stream_t* stream) {
}
static void uv__next_accept(uv_idle_t* idle, int status) {
uv_stream_t* stream = idle->data;
uv_idle_stop(idle);
if (stream->accepted_fd == -1)
uv__io_start(stream->loop, &stream->read_watcher);
}
/* Implements a best effort approach to mitigating accept() EMFILE errors.
* We have a spare file descriptor stashed away that we close to get below
* the EMFILE limit. Next, we accept all pending connections and close them
@ -497,40 +487,17 @@ void uv__server_io(uv_loop_t* loop, uv__io_t* w, int events) {
stream->accepted_fd = fd;
stream->connection_cb(stream, 0);
if (stream->accepted_fd != -1 ||
(stream->type == UV_TCP && stream->flags == UV_TCP_SINGLE_ACCEPT)) {
if (stream->accepted_fd != -1) {
/* The user hasn't yet accepted called uv_accept() */
uv__io_stop(loop, &stream->read_watcher);
break;
return;
}
}
if (stream->fd != -1 &&
stream->accepted_fd == -1 &&
(stream->type == UV_TCP && stream->flags == UV_TCP_SINGLE_ACCEPT))
{
/* Defer the next accept() syscall to the next event loop tick.
* This lets us guarantee fair load balancing in in multi-process setups.
* The problem is as follows:
*
* 1. Multiple processes listen on the same socket.
* 2. The OS scheduler commonly gives preference to one process to
* avoid task switches.
* 3. That process therefore accepts most of the new connections,
* leading to a (sometimes very) unevenly distributed load.
*
* Here is how we mitigate this issue:
*
* 1. Accept a connection.
* 2. Start an idle watcher.
* 3. Don't accept new connections until the idle callback fires.
*
* This works because the callback only fires when there have been
* no recent events, i.e. none of the watched file descriptors have
* recently been readable or writable.
*/
uv_tcp_t* tcp = (uv_tcp_t*) stream;
uv_idle_start(tcp->idle_handle, uv__next_accept);
if (stream->type == UV_TCP && (stream->flags & UV_TCP_SINGLE_ACCEPT)) {
/* Give other processes a chance to accept connections. */
struct timespec timeout = { 0, 1 };
nanosleep(&timeout, NULL);
}
}
}

2
deps/uv/src/unix/sunos.c

@ -196,8 +196,6 @@ int uv_fs_event_init(uv_loop_t* loop,
int portfd;
int first_run = 0;
/* We don't support any flags yet. */
assert(!flags);
if (loop->fs_fd == -1) {
if ((portfd = port_create()) == -1) {
uv__set_sys_error(loop, errno);

26
deps/uv/src/unix/tcp.c

@ -30,7 +30,6 @@
int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) {
uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP);
tcp->idle_handle = NULL;
return 0;
}
@ -153,6 +152,13 @@ int uv__tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6 addr) {
}
int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) {
return uv__stream_open((uv_stream_t*)handle,
sock,
UV_STREAM_READABLE | UV_STREAM_WRITABLE);
}
int uv_tcp_getsockname(uv_tcp_t* handle, struct sockaddr* name,
int* namelen) {
socklen_t socklen;
@ -238,20 +244,9 @@ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
single_accept = (val == NULL) || (atoi(val) != 0); /* on by default */
}
if (!single_accept)
goto no_single_accept;
tcp->idle_handle = malloc(sizeof(*tcp->idle_handle));
if (tcp->idle_handle == NULL)
return uv__set_sys_error(tcp->loop, ENOMEM);
if (single_accept)
tcp->flags |= UV_TCP_SINGLE_ACCEPT;
if (uv_idle_init(tcp->loop, tcp->idle_handle))
abort();
tcp->idle_handle->flags |= UV__HANDLE_INTERNAL;
tcp->flags |= UV_TCP_SINGLE_ACCEPT;
no_single_accept:
if (maybe_new_socket(tcp, AF_INET, UV_STREAM_READABLE))
return -1;
@ -390,8 +385,5 @@ int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) {
void uv__tcp_close(uv_tcp_t* handle) {
if (handle->idle_handle)
uv_close((uv_handle_t*)handle->idle_handle, (uv_close_cb)free);
uv__stream_close((uv_stream_t*)handle);
}

68
deps/uv/src/unix/udp.c

@ -316,17 +316,15 @@ static int uv__bind(uv_udp_t* handle,
goto out;
}
/* Check for already active socket. */
if (handle->fd != -1) {
uv__set_artificial_error(handle->loop, UV_EALREADY);
goto out;
}
if ((fd = uv__socket(domain, SOCK_DGRAM, 0)) == -1) {
uv__set_sys_error(handle->loop, errno);
goto out;
if (handle->fd == -1) {
if ((fd = uv__socket(domain, SOCK_DGRAM, 0)) == -1) {
uv__set_sys_error(handle->loop, errno);
goto out;
}
handle->fd = fd;
}
fd = handle->fd;
yes = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) {
uv__set_sys_error(handle->loop, errno);
@ -367,12 +365,13 @@ static int uv__bind(uv_udp_t* handle,
goto out;
}
handle->fd = fd;
status = 0;
out:
if (status)
close(fd);
if (status) {
close(handle->fd);
handle->fd = -1;
}
errno = saved_errno;
return status;
@ -486,6 +485,51 @@ int uv__udp_bind6(uv_udp_t* handle, struct sockaddr_in6 addr, unsigned flags) {
}
int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
int saved_errno;
int status;
int yes;
saved_errno = errno;
status = -1;
/* Check for already active socket. */
if (handle->fd != -1) {
uv__set_artificial_error(handle->loop, UV_EALREADY);
goto out;
}
yes = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes) == -1) {
uv__set_sys_error(handle->loop, errno);
goto out;
}
/* On the BSDs, SO_REUSEADDR lets you reuse an address that's in the TIME_WAIT
* state (i.e. was until recently tied to a socket) while SO_REUSEPORT lets
* multiple processes bind to the same address. Yes, it's something of a
* misnomer but then again, SO_REUSEADDR was already taken.
*
* None of the above applies to Linux: SO_REUSEADDR implies SO_REUSEPORT on
* Linux and hence it does not have SO_REUSEPORT at all.
*/
#ifdef SO_REUSEPORT
yes = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof yes) == -1) {
uv__set_sys_error(handle->loop, errno);
goto out;
}
#endif
handle->fd = sock;
status = 0;
out:
errno = saved_errno;
return status;
}
int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr,
const char* interface_addr, uv_membership membership) {

3
deps/uv/src/win/fs-event.c

@ -138,9 +138,6 @@ int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle,
WCHAR* dir = NULL, *dir_to_watch, *filenamew = NULL;
WCHAR short_path[MAX_PATH];
/* We don't support any flags yet. */
assert(!flags);
uv_fs_event_init_handle(loop, handle, filename, cb);
/* Convert name to UTF16. */

6
deps/uv/src/win/pipe.c

@ -1634,12 +1634,13 @@ static void eof_timer_close_cb(uv_handle_t* handle) {
}
void uv_pipe_open(uv_pipe_t* pipe, uv_file file) {
int uv_pipe_open(uv_pipe_t* pipe, uv_file file) {
HANDLE os_handle = (HANDLE)_get_osfhandle(file);
if (os_handle == INVALID_HANDLE_VALUE ||
uv_set_pipe_handle(pipe->loop, pipe, os_handle, 0) == -1) {
return;
uv__set_sys_error(pipe->loop, WSAEINVAL);
return -1;
}
uv_pipe_connection_init(pipe);
@ -1651,4 +1652,5 @@ void uv_pipe_open(uv_pipe_t* pipe, uv_file file) {
pipe->ipc_pid = uv_parent_pid();
assert(pipe->ipc_pid != -1);
}
return 0;
}

15
deps/uv/src/win/tcp.c

@ -1378,3 +1378,18 @@ void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) {
uv_want_endgame(tcp->loop, (uv_handle_t*)tcp);
}
}
int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) {
/* Make the socket non-inheritable */
if (!SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0)) {
uv__set_sys_error(handle->loop, GetLastError());
return -1;
}
if (uv_tcp_set_socket(handle->loop, handle, sock, 0) == -1) {
return -1;
}
return 0;
}

22
deps/uv/src/win/udp.c

@ -653,6 +653,28 @@ int uv_udp_set_broadcast(uv_udp_t* handle, int value) {
}
int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
int r;
DWORD yes = 1;
if (uv_udp_set_socket(handle->loop, handle, sock) == -1) {
return -1;
}
r = setsockopt(handle->socket,
SOL_SOCKET,
SO_REUSEADDR,
(char*) &yes,
sizeof yes);
if (r == SOCKET_ERROR) {
uv__set_sys_error(handle->loop, WSAGetLastError());
return -1;
}
return 0;
}
#define SOCKOPT_SETTER(name, option4, option6, validate) \
int uv_udp_set_##name(uv_udp_t* handle, int value) { \
DWORD optval = (DWORD) value; \

8
deps/uv/test/benchmark-list.h

@ -33,6 +33,10 @@ BENCHMARK_DECLARE (tcp_pump1_client)
BENCHMARK_DECLARE (pipe_pump100_client)
BENCHMARK_DECLARE (pipe_pump1_client)
BENCHMARK_DECLARE (tcp_multi_accept2)
BENCHMARK_DECLARE (tcp_multi_accept4)
BENCHMARK_DECLARE (tcp_multi_accept8)
/* Run until X packets have been sent/received. */
BENCHMARK_DECLARE (udp_pummel_1v1)
BENCHMARK_DECLARE (udp_pummel_1v10)
@ -112,6 +116,10 @@ TASK_LIST_START
BENCHMARK_ENTRY (pipe_pound_1000)
BENCHMARK_HELPER (pipe_pound_1000, pipe_echo_server)
BENCHMARK_ENTRY (tcp_multi_accept2)
BENCHMARK_ENTRY (tcp_multi_accept4)
BENCHMARK_ENTRY (tcp_multi_accept8)
BENCHMARK_ENTRY (udp_pummel_1v1)
BENCHMARK_ENTRY (udp_pummel_1v10)
BENCHMARK_ENTRY (udp_pummel_1v100)

436
deps/uv/test/benchmark-multi-accept.c

@ -0,0 +1,436 @@
/* 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"
#define IPC_PIPE_NAME TEST_PIPENAME
#define NUM_CONNECTS (250 * 1000)
union stream_handle {
uv_pipe_t pipe;
uv_tcp_t tcp;
};
/* Use as (uv_stream_t *) &handle_storage -- it's kind of clunky but it
* avoids aliasing warnings.
*/
typedef unsigned char handle_storage_t[sizeof(union stream_handle)];
/* Used for passing around the listen handle, not part of the benchmark proper.
* We have an overabundance of server types here. It works like this:
*
* 1. The main thread starts an IPC pipe server.
* 2. The worker threads connect to the IPC server and obtain a listen handle.
* 3. The worker threads start accepting requests on the listen handle.
* 4. The main thread starts connecting repeatedly.
*
* Step #4 should perhaps be farmed out over several threads.
*/
struct ipc_server_ctx {
handle_storage_t server_handle;
unsigned int num_connects;
uv_pipe_t ipc_pipe;
};
struct ipc_peer_ctx {
handle_storage_t peer_handle;
uv_write_t write_req;
};
struct ipc_client_ctx {
uv_connect_t connect_req;
uv_stream_t* server_handle;
uv_pipe_t ipc_pipe;
char scratch[16];
};
/* Used in the actual benchmark. */
struct server_ctx {
handle_storage_t server_handle;
unsigned int num_connects;
uv_async_t async_handle;
uv_thread_t thread_id;
uv_sem_t semaphore;
};
struct client_ctx {
handle_storage_t client_handle;
unsigned int num_connects;
uv_connect_t connect_req;
uv_idle_t idle_handle;
};
static void ipc_connection_cb(uv_stream_t* ipc_pipe, int status);
static void ipc_write_cb(uv_write_t* req, int status);
static void ipc_close_cb(uv_handle_t* handle);
static void ipc_connect_cb(uv_connect_t* req, int status);
static void ipc_read2_cb(uv_pipe_t* ipc_pipe,
ssize_t nread,
uv_buf_t buf,
uv_handle_type type);
static uv_buf_t ipc_alloc_cb(uv_handle_t* handle, size_t suggested_size);
static void sv_async_cb(uv_async_t* handle, int status);
static void sv_connection_cb(uv_stream_t* server_handle, int status);
static void sv_read_cb(uv_stream_t* handle, ssize_t nread, uv_buf_t buf);
static uv_buf_t sv_alloc_cb(uv_handle_t* handle, size_t suggested_size);
static void cl_connect_cb(uv_connect_t* req, int status);
static void cl_idle_cb(uv_idle_t* handle, int status);
static void cl_close_cb(uv_handle_t* handle);
static struct sockaddr_in listen_addr;
static void ipc_connection_cb(uv_stream_t* ipc_pipe, int status) {
struct ipc_server_ctx* sc;
struct ipc_peer_ctx* pc;
uv_loop_t* loop;
uv_buf_t buf;
loop = ipc_pipe->loop;
buf = uv_buf_init("PING", 4);
sc = container_of(ipc_pipe, struct ipc_server_ctx, ipc_pipe);
pc = calloc(1, sizeof(*pc));
ASSERT(pc != NULL);
if (ipc_pipe->type == UV_TCP)
ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) &pc->peer_handle));
else if (ipc_pipe->type == UV_NAMED_PIPE)
ASSERT(0 == uv_pipe_init(loop, (uv_pipe_t*) &pc->peer_handle, 1));
else
ASSERT(0);
ASSERT(0 == uv_accept(ipc_pipe, (uv_stream_t*) &pc->peer_handle));
ASSERT(0 == uv_write2(&pc->write_req,
(uv_stream_t*) &pc->peer_handle,
&buf,
1,
(uv_stream_t*) &sc->server_handle,
ipc_write_cb));
if (--sc->num_connects == 0)
uv_close((uv_handle_t*) ipc_pipe, NULL);
}
static void ipc_write_cb(uv_write_t* req, int status) {
struct ipc_peer_ctx* ctx;
ctx = container_of(req, struct ipc_peer_ctx, write_req);
uv_close((uv_handle_t*) &ctx->peer_handle, ipc_close_cb);
}
static void ipc_close_cb(uv_handle_t* handle) {
struct ipc_peer_ctx* ctx;
ctx = container_of(handle, struct ipc_peer_ctx, peer_handle);
free(ctx);
}
static void ipc_connect_cb(uv_connect_t* req, int status) {
struct ipc_client_ctx* ctx;
ctx = container_of(req, struct ipc_client_ctx, connect_req);
ASSERT(0 == status);
ASSERT(0 == uv_read2_start((uv_stream_t*) &ctx->ipc_pipe,
ipc_alloc_cb,
ipc_read2_cb));
}
static uv_buf_t ipc_alloc_cb(uv_handle_t* handle, size_t suggested_size) {
struct ipc_client_ctx* ctx;
ctx = container_of(handle, struct ipc_client_ctx, ipc_pipe);
return uv_buf_init(ctx->scratch, sizeof(ctx->scratch));
}
static void ipc_read2_cb(uv_pipe_t* ipc_pipe,
ssize_t nread,
uv_buf_t buf,
uv_handle_type type) {
struct ipc_client_ctx* ctx;
uv_loop_t* loop;
ctx = container_of(ipc_pipe, struct ipc_client_ctx, ipc_pipe);
loop = ipc_pipe->loop;
if (type == UV_TCP)
ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) ctx->server_handle));
else if (type == UV_NAMED_PIPE)
ASSERT(0 == uv_pipe_init(loop, (uv_pipe_t*) ctx->server_handle, 0));
else
ASSERT(0);
ASSERT(0 == uv_accept((uv_stream_t*) &ctx->ipc_pipe, ctx->server_handle));
uv_close((uv_handle_t*) &ctx->ipc_pipe, NULL);
}
/* Set up an IPC pipe server that hands out listen sockets to the worker
* threads. It's kind of cumbersome for such a simple operation, maybe we
* should revive uv_import() and uv_export().
*/
static void send_listen_handles(uv_handle_type type,
unsigned int num_servers,
struct server_ctx* servers) {
struct ipc_server_ctx ctx;
uv_loop_t* loop;
unsigned int i;
loop = uv_default_loop();
ctx.num_connects = num_servers;
if (type == UV_TCP) {
ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) &ctx.server_handle));
ASSERT(0 == uv_tcp_bind((uv_tcp_t*) &ctx.server_handle, listen_addr));
}
else if (type == UV_NAMED_PIPE) {
ASSERT(0 == uv_pipe_init(loop, (uv_pipe_t*) &ctx.server_handle, 0));
ASSERT(0 == uv_pipe_bind((uv_pipe_t*) &ctx.server_handle, IPC_PIPE_NAME));
}
else
ASSERT(0);
ASSERT(0 == uv_pipe_init(loop, &ctx.ipc_pipe, 1));
ASSERT(0 == uv_pipe_bind(&ctx.ipc_pipe, IPC_PIPE_NAME));
ASSERT(0 == uv_listen((uv_stream_t*) &ctx.ipc_pipe, 128, ipc_connection_cb));
for (i = 0; i < num_servers; i++)
uv_sem_post(&servers[i].semaphore);
ASSERT(0 == uv_run(loop));
uv_close((uv_handle_t*) &ctx.server_handle, NULL);
ASSERT(0 == uv_run(loop));
for (i = 0; i < num_servers; i++)
uv_sem_wait(&servers[i].semaphore);
}
static void get_listen_handle(uv_loop_t* loop, uv_stream_t* server_handle) {
struct ipc_client_ctx ctx;
ctx.server_handle = server_handle;
ctx.server_handle->data = "server handle";
ASSERT(0 == uv_pipe_init(loop, &ctx.ipc_pipe, 1));
uv_pipe_connect(&ctx.connect_req,
&ctx.ipc_pipe,
IPC_PIPE_NAME,
ipc_connect_cb);
ASSERT(0 == uv_run(loop));
}
static void server_cb(void *arg) {
struct server_ctx *ctx;
uv_loop_t* loop;
ctx = arg;
loop = uv_loop_new();
ASSERT(loop != NULL);
ASSERT(0 == uv_async_init(loop, &ctx->async_handle, sv_async_cb));
uv_unref((uv_handle_t*) &ctx->async_handle);
/* Wait until the main thread is ready. */
uv_sem_wait(&ctx->semaphore);
get_listen_handle(loop, (uv_stream_t*) &ctx->server_handle);
uv_sem_post(&ctx->semaphore);
/* Now start the actual benchmark. */
ASSERT(0 == uv_listen((uv_stream_t*) &ctx->server_handle,
128,
sv_connection_cb));
ASSERT(0 == uv_run(loop));
uv_loop_delete(loop);
}
static void sv_async_cb(uv_async_t* handle, int status) {
struct server_ctx* ctx;
ctx = container_of(handle, struct server_ctx, async_handle);
uv_close((uv_handle_t*) &ctx->server_handle, NULL);
uv_close((uv_handle_t*) &ctx->async_handle, NULL);
}
static void sv_connection_cb(uv_stream_t* server_handle, int status) {
handle_storage_t* storage;
struct server_ctx* ctx;
ctx = container_of(server_handle, struct server_ctx, server_handle);
ASSERT(status == 0);
storage = malloc(sizeof(*storage));
ASSERT(storage != NULL);
if (server_handle->type == UV_TCP)
ASSERT(0 == uv_tcp_init(server_handle->loop, (uv_tcp_t*) storage));
else if (server_handle->type == UV_NAMED_PIPE)
ASSERT(0 == uv_pipe_init(server_handle->loop, (uv_pipe_t*) storage, 0));
else
ASSERT(0);
ASSERT(0 == uv_accept(server_handle, (uv_stream_t*) storage));
ASSERT(0 == uv_read_start((uv_stream_t*) storage, sv_alloc_cb, sv_read_cb));
ctx->num_connects++;
}
static uv_buf_t sv_alloc_cb(uv_handle_t* handle, size_t suggested_size) {
static char buf[32];
return uv_buf_init(buf, sizeof(buf));
}
static void sv_read_cb(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) {
ASSERT(nread == -1);
ASSERT(uv_last_error(handle->loop).code == UV_EOF);
uv_close((uv_handle_t*) handle, (uv_close_cb) free);
}
static void cl_connect_cb(uv_connect_t* req, int status) {
struct client_ctx* ctx = container_of(req, struct client_ctx, connect_req);
uv_idle_start(&ctx->idle_handle, cl_idle_cb);
ASSERT(0 == status);
}
static void cl_idle_cb(uv_idle_t* handle, int status) {
struct client_ctx* ctx = container_of(handle, struct client_ctx, idle_handle);
uv_close((uv_handle_t*) &ctx->client_handle, cl_close_cb);
uv_idle_stop(&ctx->idle_handle);
}
static void cl_close_cb(uv_handle_t* handle) {
struct client_ctx* ctx;
ctx = container_of(handle, struct client_ctx, client_handle);
if (--ctx->num_connects == 0) {
uv_close((uv_handle_t*) &ctx->idle_handle, NULL);
return;
}
ASSERT(0 == uv_tcp_init(handle->loop, (uv_tcp_t*) &ctx->client_handle));
ASSERT(0 == uv_tcp_connect(&ctx->connect_req,
(uv_tcp_t*) &ctx->client_handle,
listen_addr,
cl_connect_cb));
}
static int test_tcp(unsigned int num_servers, unsigned int num_clients) {
struct server_ctx* servers;
struct client_ctx* clients;
uv_loop_t* loop;
uv_tcp_t* handle;
unsigned int i;
double time;
listen_addr = uv_ip4_addr("127.0.0.1", TEST_PORT);
loop = uv_default_loop();
servers = calloc(num_servers, sizeof(servers[0]));
clients = calloc(num_clients, sizeof(clients[0]));
ASSERT(servers != NULL);
ASSERT(clients != NULL);
/* We're making the assumption here that from the perspective of the
* OS scheduler, threads are functionally equivalent to and interchangeable
* with full-blown processes.
*/
for (i = 0; i < num_servers; i++) {
struct server_ctx* ctx = servers + i;
ASSERT(0 == uv_sem_init(&ctx->semaphore, 0));
ASSERT(0 == uv_thread_create(&ctx->thread_id, server_cb, ctx));
}
send_listen_handles(UV_TCP, num_servers, servers);
for (i = 0; i < num_clients; i++) {
struct client_ctx* ctx = clients + i;
ctx->num_connects = NUM_CONNECTS / num_clients;
handle = (uv_tcp_t*) &ctx->client_handle;
handle->data = "client handle";
ASSERT(0 == uv_tcp_init(loop, handle));
ASSERT(0 == uv_tcp_connect(&ctx->connect_req,
handle,
listen_addr,
cl_connect_cb));
ASSERT(0 == uv_idle_init(loop, &ctx->idle_handle));
}
{
uint64_t t = uv_hrtime();
ASSERT(0 == uv_run(loop));
t = uv_hrtime() - t;
time = t / 1e9;
}
for (i = 0; i < num_servers; i++) {
struct server_ctx* ctx = servers + i;
uv_async_send(&ctx->async_handle);
ASSERT(0 == uv_thread_join(&ctx->thread_id));
uv_sem_destroy(&ctx->semaphore);
}
printf("accept%u: %.0f accepts/sec (%u total)\n",
num_servers,
NUM_CONNECTS / time,
NUM_CONNECTS);
for (i = 0; i < num_servers; i++) {
struct server_ctx* ctx = servers + i;
printf(" thread #%u: %.0f accepts/sec (%u total, %.1f%%)\n",
i,
ctx->num_connects / time,
ctx->num_connects,
ctx->num_connects * 100.0 / NUM_CONNECTS);
}
free(clients);
free(servers);
uv_loop_delete(uv_default_loop()); /* Silence valgrind. */
return 0;
}
BENCHMARK_IMPL(tcp_multi_accept2) {
return test_tcp(2, 40);
}
BENCHMARK_IMPL(tcp_multi_accept4) {
return test_tcp(4, 40);
}
BENCHMARK_IMPL(tcp_multi_accept8) {
return test_tcp(8, 40);
}

5
deps/uv/test/dns-server.c

@ -153,7 +153,6 @@ static void process_req(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) {
hdrbuf_remaining = DNSREC_LEN - readbuf_remaining;
break;
} else {
short int reclen_n;
/* save header */
memcpy(&hdrbuf[DNSREC_LEN - hdrbuf_remaining], dnsreq, hdrbuf_remaining);
dnsreq += hdrbuf_remaining;
@ -161,8 +160,8 @@ static void process_req(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) {
hdrbuf_remaining = 0;
/* get record length */
reclen_n = *((short int*)hdrbuf);
rec_remaining = ntohs(reclen_n) - (DNSREC_LEN - 2);
rec_remaining = (unsigned) hdrbuf[0] * 256 + (unsigned) hdrbuf[1];
rec_remaining -= (DNSREC_LEN - 2);
}
}

3
deps/uv/test/test-fs-event.c

@ -98,8 +98,7 @@ static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename,
ASSERT(handle == &fs_event);
ASSERT(status == 0);
ASSERT(events == UV_RENAME);
ASSERT(filename == NULL || strcmp(filename, "file1") == 0 ||
strstr(filename, "watch_dir") != NULL);
ASSERT(filename == NULL || strcmp(filename, "file1") == 0);
uv_close((uv_handle_t*)handle, close_cb);
}

8
deps/uv/test/test-list.h

@ -40,6 +40,7 @@ TEST_DECLARE (pipe_ping_pong)
TEST_DECLARE (delayed_accept)
TEST_DECLARE (multiple_listen)
TEST_DECLARE (tcp_writealot)
TEST_DECLARE (tcp_open)
TEST_DECLARE (tcp_connect_error_after_write)
TEST_DECLARE (tcp_shutdown_after_write)
TEST_DECLARE (tcp_bind_error_addrinuse)
@ -69,6 +70,7 @@ TEST_DECLARE (udp_dgram_too_big)
TEST_DECLARE (udp_dual_stack)
TEST_DECLARE (udp_ipv6_only)
TEST_DECLARE (udp_options)
TEST_DECLARE (udp_open)
TEST_DECLARE (pipe_bind_error_addrinuse)
TEST_DECLARE (pipe_bind_error_addrnotavail)
TEST_DECLARE (pipe_bind_error_inval)
@ -237,6 +239,9 @@ TASK_LIST_START
TEST_ENTRY (tcp_writealot)
TEST_HELPER (tcp_writealot, tcp4_echo_server)
TEST_ENTRY (tcp_open)
TEST_HELPER (tcp_open, tcp4_echo_server)
TEST_ENTRY (tcp_shutdown_after_write)
TEST_HELPER (tcp_shutdown_after_write, tcp4_echo_server)
@ -271,6 +276,9 @@ TASK_LIST_START
TEST_ENTRY (udp_multicast_join)
TEST_ENTRY (udp_multicast_ttl)
TEST_ENTRY (udp_open)
TEST_HELPER (udp_open, udp4_echo_server)
TEST_ENTRY (pipe_bind_error_addrinuse)
TEST_ENTRY (pipe_bind_error_addrnotavail)
TEST_ENTRY (pipe_bind_error_inval)

175
deps/uv/test/test-tcp-open.c

@ -0,0 +1,175 @@
/* 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>
#ifndef _WIN32
# include <unistd.h>
#endif
static int shutdown_cb_called = 0;
static int connect_cb_called = 0;
static int write_cb_called = 0;
static int close_cb_called = 0;
static uv_connect_t connect_req;
static uv_shutdown_t shutdown_req;
static uv_write_t write_req;
static void startup(void) {
#ifdef _WIN32
struct WSAData wsa_data;
int r = WSAStartup(MAKEWORD(2, 2), &wsa_data);
ASSERT(r == 0);
#endif
}
static uv_os_sock_t create_tcp_socket(void) {
uv_os_sock_t sock;
int r;
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
#ifdef _WIN32
ASSERT(sock != INVALID_SOCKET);
#else
ASSERT(sock >= 0);
#endif
#ifndef _WIN32
{
/* Allow reuse of the port. */
int yes = 1;
r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
ASSERT(r == 0);
}
#endif
return sock;
}
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 close_cb(uv_handle_t* handle) {
ASSERT(handle != NULL);
close_cb_called++;
}
static void shutdown_cb(uv_shutdown_t* req, int status) {
ASSERT(req == &shutdown_req);
ASSERT(status == 0);
/* Now we wait for the EOF */
shutdown_cb_called++;
}
static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) {
ASSERT(tcp != NULL);
if (nread >= 0) {
ASSERT(nread == 4);
ASSERT(memcmp("PING", buf.base, nread) == 0);
}
else {
ASSERT(uv_last_error(uv_default_loop()).code == UV_EOF);
printf("GOT EOF\n");
uv_close((uv_handle_t*)tcp, close_cb);
}
}
static void write_cb(uv_write_t* req, int status) {
ASSERT(req != NULL);
if (status) {
uv_err_t err = uv_last_error(uv_default_loop());
fprintf(stderr, "uv_write error: %s\n", uv_strerror(err));
ASSERT(0);
}
write_cb_called++;
}
static void connect_cb(uv_connect_t* req, int status) {
uv_buf_t buf = uv_buf_init("PING", 4);
uv_stream_t* stream;
int r;
ASSERT(req == &connect_req);
ASSERT(status == 0);
stream = req->handle;
connect_cb_called++;
r = uv_write(&write_req, stream, &buf, 1, write_cb);
ASSERT(r == 0);
/* Shutdown on drain. */
r = uv_shutdown(&shutdown_req, stream, shutdown_cb);
ASSERT(r == 0);
/* Start reading */
r = uv_read_start(stream, alloc_cb, read_cb);
ASSERT(r == 0);
}
TEST_IMPL(tcp_open) {
struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT);
uv_tcp_t client;
uv_os_sock_t sock;
int r;
startup();
sock = create_tcp_socket();
r = uv_tcp_init(uv_default_loop(), &client);
ASSERT(r == 0);
r = uv_tcp_open(&client, sock);
ASSERT(r == 0);
r = uv_tcp_connect(&connect_req, &client, addr, connect_cb);
ASSERT(r == 0);
uv_run(uv_default_loop());
ASSERT(shutdown_cb_called == 1);
ASSERT(connect_cb_called == 1);
ASSERT(write_cb_called == 1);
ASSERT(close_cb_called == 1);
return 0;
}

154
deps/uv/test/test-udp-open.c

@ -0,0 +1,154 @@
/* 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>
#ifndef _WIN32
# include <unistd.h>
#endif
static int send_cb_called = 0;
static int close_cb_called = 0;
static uv_udp_send_t send_req;
static void startup(void) {
#ifdef _WIN32
struct WSAData wsa_data;
int r = WSAStartup(MAKEWORD(2, 2), &wsa_data);
ASSERT(r == 0);
#endif
}
static uv_os_sock_t create_udp_socket(void) {
uv_os_sock_t sock;
int r;
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
#ifdef _WIN32
ASSERT(sock != INVALID_SOCKET);
#else
ASSERT(sock >= 0);
#endif
#ifndef _WIN32
{
/* Allow reuse of the port. */
int yes = 1;
r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
ASSERT(r == 0);
}
#endif
return sock;
}
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 close_cb(uv_handle_t* handle) {
ASSERT(handle != NULL);
close_cb_called++;
}
static void recv_cb(uv_udp_t* handle,
ssize_t nread,
uv_buf_t buf,
struct sockaddr* addr,
unsigned flags) {
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;
}
ASSERT(flags == 0);
ASSERT(addr != NULL);
ASSERT(nread == 4);
ASSERT(memcmp("PING", buf.base, nread) == 0);
r = uv_udp_recv_stop(handle);
ASSERT(r == 0);
uv_close((uv_handle_t*) handle, close_cb);
}
static void send_cb(uv_udp_send_t* req, int status) {
ASSERT(req != NULL);
ASSERT(status == 0);
send_cb_called++;
}
TEST_IMPL(udp_open) {
struct sockaddr_in addr = uv_ip4_addr("127.0.0.1", TEST_PORT);
uv_buf_t buf = uv_buf_init("PING", 4);
uv_udp_t client;
uv_os_sock_t sock;
int r;
startup();
sock = create_udp_socket();
r = uv_udp_init(uv_default_loop(), &client);
ASSERT(r == 0);
r = uv_udp_open(&client, sock);
ASSERT(r == 0);
r = uv_udp_bind(&client, addr, 0);
ASSERT(r == 0);
r = uv_udp_recv_start(&client, alloc_cb, recv_cb);
ASSERT(r == 0);
r = uv_udp_send(&send_req, &client, &buf, 1, addr, send_cb);
ASSERT(r == 0);
uv_run(uv_default_loop());
ASSERT(send_cb_called == 1);
ASSERT(close_cb_called == 1);
return 0;
}

3
deps/uv/uv.gyp

@ -305,6 +305,7 @@
'test/test-tcp-connect-error.c',
'test/test-tcp-connect-timeout.c',
'test/test-tcp-connect6-error.c',
'test/test-tcp-open.c',
'test/test-tcp-write-error.c',
'test/test-tcp-write-to-half-open-connection.c',
'test/test-tcp-writealot.c',
@ -318,6 +319,7 @@
'test/test-tty.c',
'test/test-udp-dgram-too-big.c',
'test/test-udp-ipv6.c',
'test/test-udp-open.c',
'test/test-udp-options.c',
'test/test-udp-send-and-recv.c',
'test/test-udp-multicast-join.c',
@ -370,6 +372,7 @@
'test/benchmark-list.h',
'test/benchmark-loop-count.c',
'test/benchmark-million-timers.c',
'test/benchmark-multi-accept.c',
'test/benchmark-ping-pongs.c',
'test/benchmark-pound.c',
'test/benchmark-pump.c',

381
lib/url.js

@ -26,6 +26,22 @@ exports.resolve = urlResolve;
exports.resolveObject = urlResolveObject;
exports.format = urlFormat;
exports.Url = Url;
function Url() {
this.protocol = null;
this.slashes = null;
this.auth = null;
this.host = null;
this.port = null;
this.hostname = null;
this.hash = null;
this.search = null;
this.query = null;
this.pathname = null;
this.path = null;
}
// Reference: RFC 3986, RFC 1808, RFC 2396
// define these here so at least they only have to be
@ -90,14 +106,19 @@ var protocolPattern = /^([a-z0-9.+-]+:)/i,
querystring = require('querystring');
function urlParse(url, parseQueryString, slashesDenoteHost) {
if (url && typeof(url) === 'object' && url.href) return url;
if (url && typeof(url) === 'object' && url instanceof Url) return url;
var u = new Url;
u.parse(url, parseQueryString, slashesDenoteHost);
return u;
}
Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
if (typeof url !== 'string') {
throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
}
var out = {},
rest = url;
var rest = url;
// trim before proceeding.
// This is to support parse stuff like " http://foo.com \n"
@ -107,7 +128,7 @@ function urlParse(url, parseQueryString, slashesDenoteHost) {
if (proto) {
proto = proto[0];
var lowerProto = proto.toLowerCase();
out.protocol = lowerProto;
this.protocol = lowerProto;
rest = rest.substr(proto.length);
}
@ -119,7 +140,7 @@ function urlParse(url, parseQueryString, slashesDenoteHost) {
var slashes = rest.substr(0, 2) === '//';
if (slashes && !(proto && hostlessProtocol[proto])) {
rest = rest.substr(2);
out.slashes = true;
this.slashes = true;
}
}
@ -149,7 +170,7 @@ function urlParse(url, parseQueryString, slashesDenoteHost) {
if (hasAuth) {
// pluck off the auth portion.
out.auth = decodeURIComponent(auth);
this.auth = decodeURIComponent(auth);
rest = rest.substr(atSign + 1);
}
}
@ -162,35 +183,28 @@ function urlParse(url, parseQueryString, slashesDenoteHost) {
}
if (firstNonHost !== -1) {
out.host = rest.substr(0, firstNonHost);
this.host = rest.substr(0, firstNonHost);
rest = rest.substr(firstNonHost);
} else {
out.host = rest;
this.host = rest;
rest = '';
}
// pull out port.
var p = parseHost(out.host);
var keys = Object.keys(p);
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
out[key] = p[key];
}
this.parseHost();
// we've indicated that there is a hostname,
// so even if it's empty, it has to be present.
out.hostname = out.hostname || '';
this.hostname = this.hostname || '';
// if hostname begins with [ and ends with ]
// assume that it's an IPv6 address.
var ipv6Hostname = out.hostname[0] === '[' &&
out.hostname[out.hostname.length - 1] === ']';
var ipv6Hostname = this.hostname[0] === '[' &&
this.hostname[this.hostname.length - 1] === ']';
// validate a little.
if (out.hostname.length > hostnameMaxLen) {
out.hostname = '';
} else if (!ipv6Hostname) {
var hostparts = out.hostname.split(/\./);
if (!ipv6Hostname) {
var hostparts = this.hostname.split(/\./);
for (var i = 0, l = hostparts.length; i < l; i++) {
var part = hostparts[i];
if (!part) continue;
@ -218,38 +232,44 @@ function urlParse(url, parseQueryString, slashesDenoteHost) {
if (notHost.length) {
rest = '/' + notHost.join('.') + rest;
}
out.hostname = validParts.join('.');
this.hostname = validParts.join('.');
break;
}
}
}
}
// hostnames are always lower case.
out.hostname = out.hostname.toLowerCase();
if (this.hostname.length > hostnameMaxLen) {
this.hostname = '';
} else {
// hostnames are always lower case.
this.hostname = this.hostname.toLowerCase();
}
if (!ipv6Hostname) {
// IDNA Support: Returns a puny coded representation of "domain".
// It only converts the part of the domain name that
// has non ASCII characters. I.e. it dosent matter if
// you call it with a domain that already is in ASCII.
var domainArray = out.hostname.split('.');
var domainArray = this.hostname.split('.');
var newOut = [];
for (var i = 0; i < domainArray.length; ++i) {
var s = domainArray[i];
newOut.push(s.match(/[^A-Za-z0-9_-]/) ?
'xn--' + punycode.encode(s) : s);
}
out.hostname = newOut.join('.');
this.hostname = newOut.join('.');
}
out.host = (out.hostname || '') +
((out.port) ? ':' + out.port : '');
out.href += out.host;
var p = this.port ? ':' + this.port : '';
var h = this.hostname || '';
this.host = h + p;
this.href += this.host;
// strip [ and ] from the hostname
// the host field still retains them, though
if (ipv6Hostname) {
out.hostname = out.hostname.substr(1, out.hostname.length - 2);
this.hostname = this.hostname.substr(1, this.hostname.length - 2);
if (rest[0] !== '/') {
rest = '/' + rest;
}
@ -278,38 +298,39 @@ function urlParse(url, parseQueryString, slashesDenoteHost) {
var hash = rest.indexOf('#');
if (hash !== -1) {
// got a fragment string.
out.hash = rest.substr(hash);
this.hash = rest.substr(hash);
rest = rest.slice(0, hash);
}
var qm = rest.indexOf('?');
if (qm !== -1) {
out.search = rest.substr(qm);
out.query = rest.substr(qm + 1);
this.search = rest.substr(qm);
this.query = rest.substr(qm + 1);
if (parseQueryString) {
out.query = querystring.parse(out.query);
this.query = querystring.parse(this.query);
}
rest = rest.slice(0, qm);
} else if (parseQueryString) {
// no query string, but parseQueryString still requested
out.search = '';
out.query = {};
this.search = '';
this.query = {};
}
if (rest) out.pathname = rest;
if (rest) this.pathname = rest;
if (slashedProtocol[proto] &&
out.hostname && !out.pathname) {
out.pathname = '/';
this.hostname && !this.pathname) {
this.pathname = '/';
}
//to support http.request
if (out.pathname || out.search) {
out.path = (out.pathname ? out.pathname : '') +
(out.search ? out.search : '');
if (this.pathname || this.search) {
var p = this.pathname || '';
var s = this.search || '';
this.path = p + s;
}
// finally, reconstruct the href based on what has been validated.
out.href = urlFormat(out);
return out;
}
this.href = this.format();
return this;
};
// format a parsed object into a url string
function urlFormat(obj) {
@ -318,43 +339,47 @@ function urlFormat(obj) {
// this way, you can call url_format() on strings
// to clean up potentially wonky urls.
if (typeof(obj) === 'string') obj = urlParse(obj);
if (!(obj instanceof Url)) return Url.prototype.format.call(obj);
return obj.format();
}
var auth = obj.auth || '';
Url.prototype.format = function() {
var auth = this.auth || '';
if (auth) {
auth = encodeURIComponent(auth);
auth = auth.replace(/%3A/i, ':');
auth += '@';
}
var protocol = obj.protocol || '',
pathname = obj.pathname || '',
hash = obj.hash || '',
var protocol = this.protocol || '',
pathname = this.pathname || '',
hash = this.hash || '',
host = false,
query = '';
if (obj.host !== undefined) {
host = auth + obj.host;
} else if (obj.hostname !== undefined) {
host = auth + (obj.hostname.indexOf(':') === -1 ?
obj.hostname :
'[' + obj.hostname + ']');
if (obj.port) {
host += ':' + obj.port;
if (this.host) {
host = auth + this.host;
} else if (this.hostname) {
host = auth + (this.hostname.indexOf(':') === -1 ?
this.hostname :
'[' + this.hostname + ']');
if (this.port) {
host += ':' + this.port;
}
}
if (obj.query && typeof obj.query === 'object' &&
Object.keys(obj.query).length) {
query = querystring.stringify(obj.query);
if (this.query && typeof this.query === 'object' &&
Object.keys(this.query).length) {
query = querystring.stringify(this.query);
}
var search = obj.search || (query && ('?' + query)) || '';
var search = this.search || (query && ('?' + query)) || '';
if (protocol && protocol.substr(-1) !== ':') protocol += ':';
// only the slashedProtocols get the //. Not mailto:, xmpp:, etc.
// unless they had them to begin with.
if (obj.slashes ||
if (this.slashes ||
(!protocol || slashedProtocol[protocol]) && host !== false) {
host = '//' + (host || '');
if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname;
@ -366,39 +391,62 @@ function urlFormat(obj) {
if (search && search.charAt(0) !== '?') search = '?' + search;
return protocol + host + pathname + search + hash;
}
};
function urlResolve(source, relative) {
return urlFormat(urlResolveObject(source, relative));
return urlParse(source, false, true).resolve(relative);
}
Url.prototype.resolve = function(relative) {
return this.resolveObject(urlParse(relative, false, true)).format();
};
function urlResolveObject(source, relative) {
if (!source) return relative;
return urlParse(source, false, true).resolveObject(relative);
}
Url.prototype.resolveObject = function(relative) {
if (typeof relative === 'string') {
var rel = new Url();
rel.parse(relative, false, true);
relative = rel;
}
source = urlParse(urlFormat(source), false, true);
relative = urlParse(urlFormat(relative), false, true);
var result = new Url();
Object.keys(this).forEach(function(k) {
result[k] = this[k];
}, this);
// hash is always overridden, no matter what.
source.hash = relative.hash;
// even href="" will remove it.
result.hash = relative.hash;
// if the relative url is empty, then there's nothing left to do here.
if (relative.href === '') {
source.href = urlFormat(source);
return source;
result.href = result.format();
return result;
}
// hrefs like //foo/bar always cut to the protocol.
if (relative.slashes && !relative.protocol) {
relative.protocol = source.protocol;
// take everything except the protocol from relative
Object.keys(relative).forEach(function(k) {
if (k !== 'protocol')
result[k] = relative[k];
});
//urlParse appends trailing / to urls like http://www.example.com
if (slashedProtocol[relative.protocol] &&
relative.hostname && !relative.pathname) {
relative.path = relative.pathname = '/';
if (slashedProtocol[result.protocol] &&
result.hostname && !result.pathname) {
result.path = result.pathname = '/';
}
relative.href = urlFormat(relative);
return relative;
result.href = result.format();
return result;
}
if (relative.protocol && relative.protocol !== source.protocol) {
if (relative.protocol && relative.protocol !== result.protocol) {
// if it's a known url protocol, then changing
// the protocol does weird things
// first, if it's not file:, then we MUST have a host,
@ -408,10 +456,14 @@ function urlResolveObject(source, relative) {
// because that's known to be hostless.
// anything else is assumed to be absolute.
if (!slashedProtocol[relative.protocol]) {
relative.href = urlFormat(relative);
return relative;
Object.keys(relative).forEach(function(k) {
result[k] = relative[k];
});
result.href = result.format();
return result;
}
source.protocol = relative.protocol;
result.protocol = relative.protocol;
if (!relative.host && !hostlessProtocol[relative.protocol]) {
var relPath = (relative.pathname || '').split('/');
while (relPath.length && !(relative.host = relPath.shift()));
@ -419,72 +471,72 @@ function urlResolveObject(source, relative) {
if (!relative.hostname) relative.hostname = '';
if (relPath[0] !== '') relPath.unshift('');
if (relPath.length < 2) relPath.unshift('');
relative.pathname = relPath.join('/');
result.pathname = relPath.join('/');
} else {
result.pathname = relative.pathname;
}
source.pathname = relative.pathname;
source.search = relative.search;
source.query = relative.query;
source.host = relative.host || '';
source.auth = relative.auth;
source.hostname = relative.hostname || relative.host;
source.port = relative.port;
//to support http.request
if (source.pathname !== undefined || source.search !== undefined) {
source.path = (source.pathname ? source.pathname : '') +
(source.search ? source.search : '');
result.search = relative.search;
result.query = relative.query;
result.host = relative.host || '';
result.auth = relative.auth;
result.hostname = relative.hostname || relative.host;
result.port = relative.port;
// to support http.request
if (result.pathname || result.search) {
var p = result.pathname || '';
var s = result.search || '';
result.path = p + s;
}
source.slashes = source.slashes || relative.slashes;
source.href = urlFormat(source);
return source;
result.slashes = result.slashes || relative.slashes;
result.href = result.format();
return result;
}
var isSourceAbs = (source.pathname && source.pathname.charAt(0) === '/'),
var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),
isRelAbs = (
relative.host !== undefined ||
relative.host ||
relative.pathname && relative.pathname.charAt(0) === '/'
),
mustEndAbs = (isRelAbs || isSourceAbs ||
(source.host && relative.pathname)),
(result.host && relative.pathname)),
removeAllDots = mustEndAbs,
srcPath = source.pathname && source.pathname.split('/') || [],
srcPath = result.pathname && result.pathname.split('/') || [],
relPath = relative.pathname && relative.pathname.split('/') || [],
psychotic = source.protocol &&
!slashedProtocol[source.protocol];
psychotic = result.protocol && !slashedProtocol[result.protocol];
// if the url is a non-slashed url, then relative
// links like ../.. should be able
// to crawl up to the hostname, as well. This is strange.
// source.protocol has already been set by now.
// result.protocol has already been set by now.
// Later on, put the first path part into the host field.
if (psychotic) {
delete source.hostname;
delete source.port;
if (source.host) {
if (srcPath[0] === '') srcPath[0] = source.host;
else srcPath.unshift(source.host);
result.hostname = '';
result.port = null;
if (result.host) {
if (srcPath[0] === '') srcPath[0] = result.host;
else srcPath.unshift(result.host);
}
delete source.host;
result.host = '';
if (relative.protocol) {
delete relative.hostname;
delete relative.port;
relative.hostname = null;
relative.port = null;
if (relative.host) {
if (relPath[0] === '') relPath[0] = relative.host;
else relPath.unshift(relative.host);
}
delete relative.host;
relative.host = null;
}
mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');
}
if (isRelAbs) {
// it's absolute.
source.host = (relative.host || relative.host === '') ?
relative.host : source.host;
source.hostname = (relative.hostname || relative.hostname === '') ?
relative.hostname : source.hostname;
source.search = relative.search;
source.query = relative.query;
result.host = (relative.host || relative.host === '') ?
relative.host : result.host;
result.hostname = (relative.hostname || relative.hostname === '') ?
relative.hostname : result.hostname;
result.search = relative.search;
result.query = relative.query;
srcPath = relPath;
// fall through to the dot-handling below.
} else if (relPath.length) {
@ -493,53 +545,55 @@ function urlResolveObject(source, relative) {
if (!srcPath) srcPath = [];
srcPath.pop();
srcPath = srcPath.concat(relPath);
source.search = relative.search;
source.query = relative.query;
} else if ('search' in relative) {
result.search = relative.search;
result.query = relative.query;
} else if (relative.search !== null && relative.search !== undefined) {
// just pull out the search.
// like href='?foo'.
// Put this after the other two cases because it simplifies the booleans
if (psychotic) {
source.hostname = source.host = srcPath.shift();
result.hostname = result.host = srcPath.shift();
//occationaly the auth can get stuck only in host
//this especialy happens in cases like
//url.resolveObject('mailto:local1@domain1', 'local2@domain2')
var authInHost = source.host && source.host.indexOf('@') > 0 ?
source.host.split('@') : false;
var authInHost = result.host && result.host.indexOf('@') > 0 ?
result.host.split('@') : false;
if (authInHost) {
source.auth = authInHost.shift();
source.host = source.hostname = authInHost.shift();
result.auth = authInHost.shift();
result.host = result.hostname = authInHost.shift();
}
}
source.search = relative.search;
source.query = relative.query;
result.search = relative.search;
result.query = relative.query;
//to support http.request
if (source.pathname !== undefined || source.search !== undefined) {
source.path = (source.pathname ? source.pathname : '') +
(source.search ? source.search : '');
if (result.pathname !== null || result.search !== null) {
result.path = (result.pathname ? result.pathname : '') +
(result.search ? result.search : '');
}
source.href = urlFormat(source);
return source;
result.href = result.format();
return result;
}
if (!srcPath.length) {
// no path at all. easy.
// we've already handled the other stuff above.
delete source.pathname;
result.pathname = null;
//to support http.request
if (!source.search) {
source.path = '/' + source.search;
if (result.search) {
result.path = '/' + result.search;
} else {
delete source.path;
result.path = null;
}
source.href = urlFormat(source);
return source;
result.href = result.format();
return result;
}
// if a url ENDs in . or .., then it must get a trailing slash.
// however, if it ends in anything else non-slashy,
// then it must NOT get a trailing slash.
var last = srcPath.slice(-1)[0];
var hasTrailingSlash = (
(source.host || relative.host) && (last === '.' || last === '..') ||
(result.host || relative.host) && (last === '.' || last === '..') ||
last === '');
// strip single dots, resolve double dots to parent dir
@ -579,47 +633,52 @@ function urlResolveObject(source, relative) {
// put the host back
if (psychotic) {
source.hostname = source.host = isAbsolute ? '' :
result.hostname = result.host = isAbsolute ? '' :
srcPath.length ? srcPath.shift() : '';
//occationaly the auth can get stuck only in host
//this especialy happens in cases like
//url.resolveObject('mailto:local1@domain1', 'local2@domain2')
var authInHost = source.host && source.host.indexOf('@') > 0 ?
source.host.split('@') : false;
var authInHost = result.host && result.host.indexOf('@') > 0 ?
result.host.split('@') : false;
if (authInHost) {
source.auth = authInHost.shift();
source.host = source.hostname = authInHost.shift();
result.auth = authInHost.shift();
result.host = result.hostname = authInHost.shift();
}
}
mustEndAbs = mustEndAbs || (source.host && srcPath.length);
mustEndAbs = mustEndAbs || (result.host && srcPath.length);
if (mustEndAbs && !isAbsolute) {
srcPath.unshift('');
}
source.pathname = srcPath.join('/');
//to support request.http
if (source.pathname !== undefined || source.search !== undefined) {
source.path = (source.pathname ? source.pathname : '') +
(source.search ? source.search : '');
if (!srcPath.length) {
result.pathname = null;
result.path = null;
} else {
result.pathname = srcPath.join('/');
}
source.auth = relative.auth || source.auth;
source.slashes = source.slashes || relative.slashes;
source.href = urlFormat(source);
return source;
}
function parseHost(host) {
var out = {};
//to support request.http
if (result.pathname !== null || result.search !== null) {
result.path = (result.pathname ? result.pathname : '') +
(result.search ? result.search : '');
}
result.auth = relative.auth || result.auth;
result.slashes = result.slashes || relative.slashes;
result.href = result.format();
return result;
};
Url.prototype.parseHost = function() {
var host = this.host;
var port = portPattern.exec(host);
if (port) {
port = port[0];
if (port !== ':') {
out.port = port.substr(1);
this.port = port.substr(1);
}
host = host.substr(0, host.length - port.length);
}
if (host) out.hostname = host;
return out;
}
if (host) this.hostname = host;
};

2
src/node_version.h

@ -25,7 +25,7 @@
#define NODE_MAJOR_VERSION 0
#define NODE_MINOR_VERSION 9
#define NODE_PATCH_VERSION 2
#define NODE_VERSION_IS_RELEASE 0
#define NODE_VERSION_IS_RELEASE 1
#ifndef NODE_STRINGIFY
#define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)

14
test/simple/test-fs-watch.js

@ -24,7 +24,9 @@ var assert = require('assert');
var path = require('path');
var fs = require('fs');
var expectFilePath = process.platform == 'win32' || process.platform == 'linux';
var expectFilePath = process.platform === 'win32' ||
process.platform === 'linux' ||
process.platform === 'darwin';
var watchSeenOne = 0;
var watchSeenTwo = 0;
@ -63,7 +65,10 @@ assert.doesNotThrow(
var watcher = fs.watch(filepathOne)
watcher.on('change', function(event, filename) {
assert.equal('change', event);
if (expectFilePath) {
// darwin only shows the file path for subdir watching,
// not for individual file watching.
if (expectFilePath && process.platform !== 'darwin') {
assert.equal('watch.txt', filename);
} else {
assert.equal(null, filename);
@ -87,7 +92,10 @@ assert.doesNotThrow(
function() {
var watcher = fs.watch(filepathTwo, function(event, filename) {
assert.equal('change', event);
if (expectFilePath) {
// darwin only shows the file path for subdir watching,
// not for individual file watching.
if (expectFilePath && process.platform !== 'darwin') {
assert.equal('hasOwnProperty', filename);
} else {
assert.equal(null, filename);

30
test/simple/test-url.js

@ -659,6 +659,12 @@ for (var u in parseTests) {
spaced = url.parse(' \t ' + u + '\n\t');
expected = parseTests[u];
Object.keys(actual).forEach(function (i) {
if (expected[i] === undefined && actual[i] === null) {
expected[i] = null;
}
});
assert.deepEqual(actual, expected);
assert.deepEqual(spaced, expected);
@ -695,6 +701,11 @@ var parseTestsWithQueryString = {
for (var u in parseTestsWithQueryString) {
var actual = url.parse(u, true);
var expected = parseTestsWithQueryString[u];
for (var i in actual) {
if (actual[i] === null && expected[i] === undefined) {
expected[i] = null;
}
}
assert.deepEqual(actual, expected);
}
@ -1227,15 +1238,6 @@ relativeTests.forEach(function(relativeTest) {
var actual = url.resolveObject(url.parse(relativeTest[0]), relativeTest[1]),
expected = url.parse(relativeTest[2]);
//because of evaluation order
//resolveObject(parse(x), y) == parse(resolve(x, y)) will differ by
//false-ish values. remove all except host and hostname
for (var i in actual) {
if (actual[i] === undefined ||
(!emptyIsImportant.hasOwnProperty(i) && !actual[i])) {
delete actual[i];
}
}
assert.deepEqual(actual, expected);
@ -1264,16 +1266,6 @@ relativeTests2.forEach(function(relativeTest) {
var actual = url.resolveObject(url.parse(relativeTest[1]), relativeTest[0]),
expected = url.parse(relativeTest[2]);
//because of evaluation order
//resolveObject(parse(x), y) == parse(resolve(x, y)) will differ by
//false-ish values. remove all except host and hostname
for (var i in actual) {
if (actual[i] === undefined ||
(!emptyIsImportant.hasOwnProperty(i) && !actual[i])) {
delete actual[i];
}
}
assert.deepEqual(actual, expected);
var expected = relativeTest[2],

Loading…
Cancel
Save