Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'v0.9.2-release'

  • Loading branch information...
commit 7fcb7b4d9cc9ac8ba4645bc6337a5af3ccc75496 2 parents 90a6253 + 6e20558
@isaacs isaacs authored
Showing with 1,278 additions and 286 deletions.
  1. +7 −0 AUTHORS
  2. +44 −1 ChangeLog
  3. +2 −2 deps/openssl/openssl.gyp
  4. +3 −0  deps/uv/include/uv-private/uv-darwin.h
  5. +1 −2  deps/uv/include/uv-private/uv-unix.h
  6. +19 −2 deps/uv/include/uv.h
  7. +52 −4 deps/uv/src/unix/fsevents.c
  8. +3 −3 deps/uv/src/unix/kqueue.c
  9. +0 −3  deps/uv/src/unix/linux/inotify.c
  10. +4 −4 deps/uv/src/unix/pipe.c
  11. +7 −40 deps/uv/src/unix/stream.c
  12. +0 −2  deps/uv/src/unix/sunos.c
  13. +9 −17 deps/uv/src/unix/tcp.c
  14. +56 −12 deps/uv/src/unix/udp.c
  15. +0 −3  deps/uv/src/win/fs-event.c
  16. +4 −2 deps/uv/src/win/pipe.c
  17. +15 −0 deps/uv/src/win/tcp.c
  18. +22 −0 deps/uv/src/win/udp.c
  19. +8 −0 deps/uv/test/benchmark-list.h
  20. +436 −0 deps/uv/test/benchmark-multi-accept.c
  21. +2 −3 deps/uv/test/dns-server.c
  22. +1 −2  deps/uv/test/test-fs-event.c
  23. +8 −0 deps/uv/test/test-list.h
  24. +175 −0 deps/uv/test/test-tcp-open.c
  25. +154 −0 deps/uv/test/test-udp-open.c
  26. +3 −0  deps/uv/uv.gyp
  27. +220 −161 lib/url.js
  28. +1 −1  src/node_version.h
  29. +11 −3 test/simple/test-fs-watch.js
  30. +11 −19 test/simple/test-url.js
View
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>
View
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)
View
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',
View
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; \
View
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; \
View
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);
@@ -704,6 +709,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.
*
* Arguments:
@@ -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
};
View
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;
}
View
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;
View
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
View
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);
}
View
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);
+ }
}
}
View
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);
View
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);
}
View
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) {
View
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. */
View
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;
}
View
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;
+}
View
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; \
View
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)
View
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);
+}
View
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);
}
}
View
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);
}
View
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)
View
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;
+}
View
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;
+}
View
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',
View
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;
+};
View
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)
View
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);
View
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],

2 comments on commit 7fcb7b4

@bnoordhuis

@jbergstroem Is it possible that the machine is running a debug build now?

Please sign in to comment.