From 8bcd0a4c4a8e3d748bea53d036985609ea634b8d Mon Sep 17 00:00:00 2001 From: Florin-Cristian Gavrila Date: Tue, 13 Jan 2015 18:40:05 +0000 Subject: [PATCH 1/8] http: fix performance regression for GET requests A significant performance regressions has been introduced in 1fddc1f for GET requests which send data through response.end(). The number of requests per second dropped to somewhere around 6% of their previous level. The fix consists of removing a part of the lines added by 1fddc1f, lines which were supposed to affect only HEAD requests, but interfered with GET requests instead. The lines removed would not have affected the behaviour in the case of a HEAD request as this._hasBody would always be false. Therefore, they were not required to fix the issue reported in #8361. Fixes #8940. PR: #9026 PR-URL: https://github.com/joyent/node/pull/9026 Reviewed-By: Julien Gilli --- lib/http.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/http.js b/lib/http.js index 6f640d1af3..f4e8db7de4 100644 --- a/lib/http.js +++ b/lib/http.js @@ -946,10 +946,6 @@ OutgoingMessage.prototype.end = function(data, encoding) { if (encoding === 'hex' || encoding === 'base64') hot = false; - // Transfer-encoding: chunked responses to HEAD requests - if (this._hasBody && this.chunkedEncoding) - hot = false; - if (hot) { // Hot path. They're doing // res.writeHead(); From dcff5d565c000904cabf48c627534a779a40527a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 24 Feb 2015 11:05:39 +0100 Subject: [PATCH 2/8] deps: update libuv to 0.10.36 PR: #9274 PR-URL: https://github.com/joyent/node/pull/9274 Reviewed-By: Julien Gilli --- deps/uv/AUTHORS | 3 + deps/uv/ChangeLog | 70 ++++++++- deps/uv/build.mk | 2 + deps/uv/checksparse.sh | 1 + deps/uv/config-unix.mk | 4 +- deps/uv/include/uv-private/uv-win.h | 5 +- deps/uv/src/unix/async.c | 43 ++---- deps/uv/src/unix/atomic-ops.h | 60 ++++++++ deps/uv/src/unix/internal.h | 15 ++ deps/uv/src/unix/linux-core.c | 40 +++-- deps/uv/src/unix/linux-syscalls.c | 6 +- deps/uv/src/unix/linux-syscalls.h | 2 +- deps/uv/src/unix/process.c | 15 ++ deps/uv/src/unix/stream.c | 13 ++ deps/uv/src/version.c | 2 +- deps/uv/src/win/dl.c | 35 ++++- deps/uv/src/win/fs.c | 4 +- deps/uv/src/win/internal.h | 4 +- deps/uv/src/win/poll.c | 40 +++-- deps/uv/src/win/winsock.c | 12 +- deps/uv/test/test-dlerror.c | 19 +-- deps/uv/test/test-list.h | 10 ++ deps/uv/test/test-loop-configure.c | 40 +++++ .../test-poll-close-doesnt-corrupt-stack.c | 114 +++++++++++++++ deps/uv/test/test-tcp-oob.c | 137 ++++++++++++++++++ deps/uv/uv.gyp | 4 + 26 files changed, 595 insertions(+), 105 deletions(-) create mode 100644 deps/uv/src/unix/atomic-ops.h create mode 100644 deps/uv/test/test-loop-configure.c create mode 100644 deps/uv/test/test-poll-close-doesnt-corrupt-stack.c create mode 100644 deps/uv/test/test-tcp-oob.c diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS index 48a74db32f..1123dd8ec4 100644 --- a/deps/uv/AUTHORS +++ b/deps/uv/AUTHORS @@ -132,3 +132,6 @@ Javier Hernández Tonis Tiigi Michael Hudson-Doyle Helge Deller +Logan Rosen +Kenneth Perry +Michael Penick diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog index 4ba348d571..bd03d53b5c 100644 --- a/deps/uv/ChangeLog +++ b/deps/uv/ChangeLog @@ -1,4 +1,72 @@ -2014.12.10, Version 0.10.30 (Stable) +2015.02.27, Version 0.10.36 (Stable) + +Changes since version 0.10.35: + +* stream: ignore EINVAL for SO_OOBINLINE on OS X (Fedor Indutny) + + +2015.02.25, Version 0.10.35 (Stable), 4dc978825d870643bbaa4660f71d22975efba29e + +Changes since version 0.10.34: + +* stream: use SO_OOBINLINE on OS X (Fedor Indutny) + + +2015.02.21, Version 0.10.34 (Stable), 37aa4aa9b9712c778d7b249563e868cabfdb8332 + +Changes since version 0.10.33: + +* unix: add atomic-ops.h (Ben Noordhuis) + +* unix: fix for uv_async data race (Michael Penick) + +* unix: call setgoups before calling setuid/setgid (Saúl Ibarra Corretgé) + + +2015.01.29, Version 0.10.33 (Stable), 7a2253d33ad8215a26c1b34f1952aee7242dd687 + +Changes since version 0.10.32: + +* linux: fix epoll_pwait() regression with < 2.6.19 (Ben Noordhuis) + +* test: back-port uv_loop_configure() test (Ben Noordhuis) + + +2015.01.06, Version 0.10.32 (Stable), 378de30c59aef5fdb6d130fa5cfcb0a68fce571c + +Changes since version 0.10.31: + +* linux: fix epoll_pwait() sigmask size calculation (Ben Noordhuis) + + +2014.12.25, Version 0.10.31 (Stable), 4dbd27e2219069a6daa769fb37f98673b77b4261 + +Changes since version 0.10.30: + +* test: test that closing a poll handle doesn't corrupt the stack (Bert Belder) + +* win: fix compilation of tests (Marc Schlaich) + +* Revert "win: keep a reference to AFD_POLL_INFO in cancel poll" (Bert Belder) + +* win: avoid stack corruption when closing a poll handle (Bert Belder) + +* gitignore: ignore Visual Studio files (Marc Schlaich) + +* win: set fallback message if FormatMessage fails (Marc Schlaich) + +* win: fall back to default language in uv_dlerror (Marc Schlaich) + +* test: improve compatibility for dlerror test (Marc Schlaich) + +* test: check dlerror is "no error" in no error case (Marc Schlaich) + +* build: link against -pthread (Logan Rosen) + +* win: scandir use 'ls' for formatting long strings (Kenneth Perry) + + +2014.12.10, Version 0.10.30 (Stable), 5a63f5e9546dca482eeebc3054139b21f509f21f Changes since version 0.10.29: diff --git a/deps/uv/build.mk b/deps/uv/build.mk index a2fdac7d45..efa42e1bcc 100644 --- a/deps/uv/build.mk +++ b/deps/uv/build.mk @@ -91,6 +91,7 @@ TESTS= \ test/test-ipc-send-recv.o \ test/test-loop-handles.o \ test/test-loop-stop.o \ + test/test-loop-configure.o \ test/test-multiple-listen.o \ test/test-mutexes.o \ test/test-osx-select.o \ @@ -126,6 +127,7 @@ TESTS= \ test/test-tcp-connect-timeout.o \ test/test-tcp-flags.o \ test/test-tcp-open.o \ + test/test-tcp-oob.o \ test/test-tcp-read-stop.o \ test/test-tcp-shutdown-after-write.o \ test/test-tcp-unexpected-read.o \ diff --git a/deps/uv/checksparse.sh b/deps/uv/checksparse.sh index 01fac07a39..d2b1d5e05d 100755 --- a/deps/uv/checksparse.sh +++ b/deps/uv/checksparse.sh @@ -145,6 +145,7 @@ test/test-tcp-open.c test/test-tcp-read-stop.c test/test-tcp-shutdown-after-write.c test/test-tcp-unexpected-read.c +test/test-tcp-oob.c test/test-tcp-write-error.c test/test-tcp-write-to-half-open-connection.c test/test-tcp-writealot.c diff --git a/deps/uv/config-unix.mk b/deps/uv/config-unix.mk index 37c4a72f6e..db21daa0ba 100644 --- a/deps/uv/config-unix.mk +++ b/deps/uv/config-unix.mk @@ -22,7 +22,7 @@ E= CSTDFLAG=--std=c89 -pedantic -Wall -Wextra -Wno-unused-parameter CFLAGS += -g CPPFLAGS += -I$(SRCDIR)/src -LDFLAGS=-lm +LDFLAGS=-lm -pthread CPPFLAGS += -D_LARGEFILE_SOURCE CPPFLAGS += -D_FILE_OFFSET_BITS=64 @@ -186,7 +186,7 @@ src/.buildstamp src/unix/.buildstamp test/.buildstamp: mkdir -p $(@D) touch $@ -src/unix/%.o src/unix/%.pic.o: src/unix/%.c include/uv.h include/uv-private/uv-unix.h src/unix/internal.h src/unix/.buildstamp $(DTRACE_HEADER) +src/unix/%.o src/unix/%.pic.o: src/unix/%.c include/uv.h include/uv-private/uv-unix.h src/unix/atomic-ops.h src/unix/internal.h src/unix/.buildstamp $(DTRACE_HEADER) $(CC) $(CSTDFLAG) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ src/%.o src/%.pic.o: src/%.c include/uv.h include/uv-private/uv-unix.h src/.buildstamp diff --git a/deps/uv/include/uv-private/uv-win.h b/deps/uv/include/uv-private/uv-win.h index e5f2f3b4c8..c9ec38ef4c 100644 --- a/deps/uv/include/uv-private/uv-win.h +++ b/deps/uv/include/uv-private/uv-win.h @@ -474,10 +474,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); /* Used in fast mode */ \ SOCKET peer_socket; \ AFD_POLL_INFO afd_poll_info_1; \ - union { \ - AFD_POLL_INFO* afd_poll_info_ptr; \ - AFD_POLL_INFO afd_poll_info; \ - } afd_poll_info_2; \ + AFD_POLL_INFO afd_poll_info_2; \ /* Used in fast and slow mode. */ \ uv_req_t poll_req_1; \ uv_req_t poll_req_2; \ diff --git a/deps/uv/src/unix/async.c b/deps/uv/src/unix/async.c index 43f54ed4b2..8adf046a69 100644 --- a/deps/uv/src/unix/async.c +++ b/deps/uv/src/unix/async.c @@ -24,6 +24,7 @@ #include "uv.h" #include "internal.h" +#include "atomic-ops.h" #include #include @@ -34,7 +35,6 @@ static void uv__async_event(uv_loop_t* loop, struct uv__async* w, unsigned int nevents); -static int uv__async_make_pending(int* pending); static int uv__async_eventfd(void); @@ -54,7 +54,11 @@ int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) { int uv_async_send(uv_async_t* handle) { - if (uv__async_make_pending(&handle->pending) == 0) + /* Do a cheap read first. */ + if (ACCESS_ONCE(int, handle->pending) != 0) + return 0; + + if (cmpxchgi(&handle->pending, 0, 1) == 0) uv__async_send(&handle->loop->async_watcher); return 0; @@ -75,41 +79,12 @@ static void uv__async_event(uv_loop_t* loop, ngx_queue_foreach(q, &loop->async_handles) { h = ngx_queue_data(q, uv_async_t, queue); - if (!h->pending) continue; - h->pending = 0; - h->async_cb(h, 0); - } -} + if (cmpxchgi(&h->pending, 1, 0) == 0) + continue; -static int uv__async_make_pending(int* pending) { - /* Do a cheap read first. */ - if (ACCESS_ONCE(int, *pending) != 0) - return 1; - - /* Micro-optimization: use atomic memory operations to detect if we've been - * preempted by another thread and don't have to make an expensive syscall. - * This speeds up the heavily contended case by about 1-2% and has little - * if any impact on the non-contended case. - * - * Use XCHG instead of the CMPXCHG that __sync_val_compare_and_swap() emits - * on x86, it's about 4x faster. It probably makes zero difference in the - * grand scheme of things but I'm OCD enough not to let this one pass. - */ -#if defined(__i386__) || defined(__x86_64__) - { - unsigned int val = 1; - __asm__ __volatile__ ("xchgl %0, %1" - : "+r" (val) - : "m" (*pending)); - return val != 0; + h->async_cb(h, 0); } -#elif defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ > 0) - return __sync_val_compare_and_swap(pending, 0, 1) != 0; -#else - ACCESS_ONCE(int, *pending) = 1; - return 0; -#endif } diff --git a/deps/uv/src/unix/atomic-ops.h b/deps/uv/src/unix/atomic-ops.h new file mode 100644 index 0000000000..7e4e64beda --- /dev/null +++ b/deps/uv/src/unix/atomic-ops.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2013, Ben Noordhuis + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef UV_ATOMIC_OPS_H_ +#define UV_ATOMIC_OPS_H_ + +#include "internal.h" /* UV_UNUSED */ + +UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)); +UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)); +UV_UNUSED(static void cpu_relax(void)); + +/* Prefer hand-rolled assembly over the gcc builtins because the latter also + * issue full memory barriers. + */ +UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) { +#if defined(__i386__) || defined(__x86_64__) + int out; + __asm__ __volatile__ ("lock; cmpxchg %2, %1;" + : "=a" (out), "+m" (*(volatile int*) ptr) + : "r" (newval), "0" (oldval) + : "memory"); + return out; +#else + return __sync_val_compare_and_swap(ptr, oldval, newval); +#endif +} + +UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)) { +#if defined(__i386__) || defined(__x86_64__) + long out; + __asm__ __volatile__ ("lock; cmpxchg %2, %1;" + : "=a" (out), "+m" (*(volatile long*) ptr) + : "r" (newval), "0" (oldval) + : "memory"); + return out; +#else + return __sync_val_compare_and_swap(ptr, oldval, newval); +#endif +} + +UV_UNUSED(static void cpu_relax(void)) { +#if defined(__i386__) || defined(__x86_64__) + __asm__ __volatile__ ("rep; nop"); /* a.k.a. PAUSE */ +#endif +} + +#endif /* UV_ATOMIC_OPS_H_ */ diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index 003e1a5093..af360539ed 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -66,6 +66,21 @@ } \ while (0) +/* The __clang__ and __INTEL_COMPILER checks are superfluous because they + * define __GNUC__. They are here to convey to you, dear reader, that these + * macros are enabled when compiling with clang or icc. + */ +#if defined(__clang__) || \ + defined(__GNUC__) || \ + defined(__INTEL_COMPILER) || \ + defined(__SUNPRO_C) +# define UV_DESTRUCTOR(declaration) __attribute__((destructor)) declaration +# define UV_UNUSED(declaration) __attribute__((unused)) declaration +#else +# define UV_DESTRUCTOR(declaration) declaration +# define UV_UNUSED(declaration) declaration +#endif + #if defined(__linux__) # define UV__POLLIN UV__EPOLLIN # define UV__POLLOUT UV__EPOLLOUT diff --git a/deps/uv/src/unix/linux-core.c b/deps/uv/src/unix/linux-core.c index 47938f2b20..6cd5a7f003 100644 --- a/deps/uv/src/unix/linux-core.c +++ b/deps/uv/src/unix/linux-core.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include @@ -126,13 +125,15 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { void uv__io_poll(uv_loop_t* loop, int timeout) { + static int no_epoll_pwait; + static int no_epoll_wait; struct uv__epoll_event events[1024]; struct uv__epoll_event* pe; struct uv__epoll_event e; ngx_queue_t* q; uv__io_t* w; - sigset_t* pset; - sigset_t set; + sigset_t sigset; + uint64_t sigmask; uint64_t base; uint64_t diff; int nevents; @@ -141,7 +142,6 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int fd; int op; int i; - static int no_epoll_wait; if (loop->nfds == 0) { assert(ngx_queue_empty(&loop->watcher_queue)); @@ -183,11 +183,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { w->events = w->pevents; } - pset = NULL; + sigmask = 0; if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { - pset = &set; - sigemptyset(pset); - sigaddset(pset, SIGPROF); + sigemptyset(&sigset); + sigaddset(&sigset, SIGPROF); + sigmask |= 1 << (SIGPROF - 1); } assert(timeout >= -1); @@ -195,23 +195,31 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { count = 48; /* Benchmarks suggest this gives the best throughput. */ for (;;) { - if (no_epoll_wait || pset != NULL) { + if (sigmask != 0 && no_epoll_pwait != 0) + if (pthread_sigmask(SIG_BLOCK, &sigset, NULL)) + abort(); + + if (sigmask != 0 && no_epoll_pwait == 0) { nfds = uv__epoll_pwait(loop->backend_fd, events, ARRAY_SIZE(events), timeout, - pset); + sigmask); + if (nfds == -1 && errno == ENOSYS) + no_epoll_pwait = 1; } else { nfds = uv__epoll_wait(loop->backend_fd, events, ARRAY_SIZE(events), timeout); - if (nfds == -1 && errno == ENOSYS) { + if (nfds == -1 && errno == ENOSYS) no_epoll_wait = 1; - continue; - } } + if (sigmask != 0 && no_epoll_pwait != 0) + if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)) + abort(); + /* Update loop->time unconditionally. It's tempting to skip the update when * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the * operating system didn't reschedule our process while in the syscall. @@ -224,6 +232,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } if (nfds == -1) { + if (errno == ENOSYS) { + /* epoll_wait() or epoll_pwait() failed, try the other system call. */ + assert(no_epoll_wait == 0 || no_epoll_pwait == 0); + continue; + } + if (errno != EINTR) abort(); diff --git a/deps/uv/src/unix/linux-syscalls.c b/deps/uv/src/unix/linux-syscalls.c index c9945438f4..267915d879 100644 --- a/deps/uv/src/unix/linux-syscalls.c +++ b/deps/uv/src/unix/linux-syscalls.c @@ -291,15 +291,15 @@ int uv__epoll_pwait(int epfd, struct uv__epoll_event* events, int nevents, int timeout, - const sigset_t* sigmask) { + uint64_t sigmask) { #if defined(__NR_epoll_pwait) return syscall(__NR_epoll_pwait, epfd, events, nevents, timeout, - sigmask, - _NSIG / 8); + &sigmask, + sizeof(sigmask)); #else return errno = ENOSYS, -1; #endif diff --git a/deps/uv/src/unix/linux-syscalls.h b/deps/uv/src/unix/linux-syscalls.h index 62eb5c5a36..7be73bb6e4 100644 --- a/deps/uv/src/unix/linux-syscalls.h +++ b/deps/uv/src/unix/linux-syscalls.h @@ -130,7 +130,7 @@ int uv__epoll_pwait(int epfd, struct uv__epoll_event* events, int nevents, int timeout, - const sigset_t* sigmask); + uint64_t sigmask); int uv__eventfd2(unsigned int count, int flags); int uv__inotify_init(void); int uv__inotify_init1(int flags); diff --git a/deps/uv/src/unix/process.c b/deps/uv/src/unix/process.c index 19686a291f..d1f9440c53 100644 --- a/deps/uv/src/unix/process.c +++ b/deps/uv/src/unix/process.c @@ -40,6 +40,10 @@ extern char **environ; #endif +#ifdef __linux__ +# include +#endif + static ngx_queue_t* uv__process_queue(uv_loop_t* loop, int pid) { assert(pid > 0); @@ -331,6 +335,17 @@ static void uv__process_child_init(uv_process_options_t options, _exit(127); } + if (options.flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) { + /* When dropping privileges from root, the `setgroups` call will + * remove any extraneous groups. If we don't call this, then + * even though our uid has dropped, we may still have groups + * that enable us to do super-user things. This will fail if we + * aren't root, so don't bother checking the return value, this + * is just done as an optimistic privilege dropping function. + */ + SAVE_ERRNO(setgroups(0, NULL)); + } + if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) { uv__write_int(error_fd, errno); _exit(127); diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index 81933e6462..a8802c85a5 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -403,6 +403,10 @@ failed_malloc: int uv__stream_open(uv_stream_t* stream, int fd, int flags) { +#if defined(__APPLE__) + int enable; +#endif + assert(fd >= 0); stream->flags |= flags; @@ -415,6 +419,15 @@ int uv__stream_open(uv_stream_t* stream, int fd, int flags) { return uv__set_sys_error(stream->loop, errno); } +#if defined(__APPLE__) + enable = 1; + if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &enable, sizeof(enable)) && + errno != ENOTSOCK && + errno != EINVAL) { + return uv__set_sys_error(stream->loop, errno); + } +#endif + stream->io_watcher.fd = fd; return 0; diff --git a/deps/uv/src/version.c b/deps/uv/src/version.c index 1b5a1e4cdb..5540866064 100644 --- a/deps/uv/src/version.c +++ b/deps/uv/src/version.c @@ -34,7 +34,7 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 -#define UV_VERSION_PATCH 30 +#define UV_VERSION_PATCH 36 #define UV_VERSION_IS_RELEASE 1 diff --git a/deps/uv/src/win/dl.c b/deps/uv/src/win/dl.c index d5b8f7c7d3..f7fca81ca2 100644 --- a/deps/uv/src/win/dl.c +++ b/deps/uv/src/win/dl.c @@ -69,17 +69,44 @@ const char* uv_dlerror(uv_lib_t* lib) { } +static void uv__format_fallback_error(uv_lib_t* lib, int errorno){ + DWORD_PTR args[1] = { (DWORD_PTR) errorno }; + LPSTR fallback_error = "error: %1!d!"; + + FormatMessageA(FORMAT_MESSAGE_FROM_STRING | + FORMAT_MESSAGE_ARGUMENT_ARRAY | + FORMAT_MESSAGE_ALLOCATE_BUFFER, + fallback_error, 0, 0, + (LPSTR) &lib->errmsg, + 0, (va_list*) args); +} + + + static int uv__dlerror(uv_lib_t* lib, int errorno) { + DWORD res; + if (lib->errmsg) { LocalFree((void*)lib->errmsg); lib->errmsg = NULL; } if (errorno) { - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, - MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), - (LPSTR)&lib->errmsg, 0, NULL); + res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPSTR) &lib->errmsg, 0, NULL); + if (!res && GetLastError() == ERROR_MUI_FILE_NOT_FOUND) { + res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, + 0, (LPSTR) &lib->errmsg, 0, NULL); + } + + if (!res) { + uv__format_fallback_error(lib, errorno); + } } return errorno ? -1 : 0; diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c index 7f3704e8aa..c5a4b2b703 100644 --- a/deps/uv/src/win/fs.c +++ b/deps/uv/src/win/fs.c @@ -732,9 +732,9 @@ void fs__readdir(uv_fs_t* req) { if (len == 0) { fmt = L"./*"; } else if (pathw[len - 1] == L'/' || pathw[len - 1] == L'\\') { - fmt = L"%s*"; + fmt = L"%ls*"; } else { - fmt = L"%s\\*"; + fmt = L"%ls\\*"; } /* Figure out whether path is a file or a directory. */ diff --git a/deps/uv/src/win/internal.h b/deps/uv/src/win/internal.h index 9920f704ab..f32d24a778 100644 --- a/deps/uv/src/win/internal.h +++ b/deps/uv/src/win/internal.h @@ -332,8 +332,8 @@ int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers, int* addr_len, WSAOVERLAPPED *overlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine); -int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info, - OVERLAPPED* overlapped); +int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in, + AFD_POLL_INFO* info_out, OVERLAPPED* overlapped); /* Whether there are any non-IFS LSPs stacked on TCP */ extern int uv_tcp_non_ifs_lsp_ipv4; diff --git a/deps/uv/src/win/poll.c b/deps/uv/src/win/poll.c index 5c5c7d80f0..3724279278 100644 --- a/deps/uv/src/win/poll.c +++ b/deps/uv/src/win/poll.c @@ -46,6 +46,8 @@ typedef struct uv_single_fd_set_s { static OVERLAPPED overlapped_dummy_; static uv_once_t overlapped_dummy_init_guard_ = UV_ONCE_INIT; +static AFD_POLL_INFO afd_poll_info_dummy_; + static void uv__init_overlapped_dummy(void) { HANDLE event; @@ -65,6 +67,11 @@ static OVERLAPPED* uv__get_overlapped_dummy() { } +static AFD_POLL_INFO* uv__get_afd_poll_info_dummy() { + return &afd_poll_info_dummy_; +} + + static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { uv_req_t* req; AFD_POLL_INFO* afd_poll_info; @@ -79,7 +86,7 @@ static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { handle->mask_events_2 = handle->events; } else if (handle->submitted_events_2 == 0) { req = &handle->poll_req_2; - afd_poll_info = &handle->afd_poll_info_2.afd_poll_info_ptr[0]; + afd_poll_info = &handle->afd_poll_info_2; handle->submitted_events_2 = handle->events; handle->mask_events_1 = handle->events; handle->mask_events_2 = 0; @@ -107,6 +114,7 @@ static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { memset(&req->overlapped, 0, sizeof req->overlapped); result = uv_msafd_poll((SOCKET) handle->peer_socket, + afd_poll_info, afd_poll_info, &req->overlapped); if (result != 0 && WSAGetLastError() != WSA_IO_PENDING) { @@ -118,19 +126,19 @@ static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { static int uv__fast_poll_cancel_poll_req(uv_loop_t* loop, uv_poll_t* handle) { - AFD_POLL_INFO* afd_poll_info; + AFD_POLL_INFO afd_poll_info; DWORD result; - afd_poll_info = &handle->afd_poll_info_2.afd_poll_info_ptr[1]; - afd_poll_info->Exclusive = TRUE; - afd_poll_info->NumberOfHandles = 1; - afd_poll_info->Timeout.QuadPart = INT64_MAX; - afd_poll_info->Handles[0].Handle = (HANDLE) handle->socket; - afd_poll_info->Handles[0].Status = 0; - afd_poll_info->Handles[0].Events = AFD_POLL_ALL; + afd_poll_info.Exclusive = TRUE; + afd_poll_info.NumberOfHandles = 1; + afd_poll_info.Timeout.QuadPart = INT64_MAX; + afd_poll_info.Handles[0].Handle = (HANDLE) handle->socket; + afd_poll_info.Handles[0].Status = 0; + afd_poll_info.Handles[0].Events = AFD_POLL_ALL; result = uv_msafd_poll(handle->socket, - afd_poll_info, + &afd_poll_info, + uv__get_afd_poll_info_dummy(), uv__get_overlapped_dummy()); if (result == SOCKET_ERROR) { @@ -155,7 +163,7 @@ static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, handle->submitted_events_1 = 0; mask_events = handle->mask_events_1; } else if (req == &handle->poll_req_2) { - afd_poll_info = &handle->afd_poll_info_2.afd_poll_info_ptr[0]; + afd_poll_info = &handle->afd_poll_info_2; handle->submitted_events_2 = 0; mask_events = handle->mask_events_2; } else { @@ -552,12 +560,6 @@ int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, handle->poll_req_2.type = UV_POLL_REQ; handle->poll_req_2.data = handle; - handle->afd_poll_info_2.afd_poll_info_ptr = malloc(sizeof(*handle->afd_poll_info_2.afd_poll_info_ptr) * 2); - if (handle->afd_poll_info_2.afd_poll_info_ptr == NULL) { - uv__set_artificial_error(loop, UV_ENOMEM); - return -1; - } - return 0; } @@ -611,9 +613,5 @@ void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle) { assert(handle->submitted_events_1 == 0); assert(handle->submitted_events_2 == 0); - if (handle->afd_poll_info_2.afd_poll_info_ptr) { - free(handle->afd_poll_info_2.afd_poll_info_ptr); - handle->afd_poll_info_2.afd_poll_info_ptr = NULL; - } uv__handle_close(handle); } diff --git a/deps/uv/src/win/winsock.c b/deps/uv/src/win/winsock.c index cf6d031891..b91a237338 100644 --- a/deps/uv/src/win/winsock.c +++ b/deps/uv/src/win/winsock.c @@ -467,8 +467,8 @@ int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers, } -int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info, - OVERLAPPED* overlapped) { +int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in, + AFD_POLL_INFO* info_out, OVERLAPPED* overlapped) { IO_STATUS_BLOCK iosb; IO_STATUS_BLOCK* iosb_ptr; HANDLE event = NULL; @@ -506,10 +506,10 @@ int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info, apc_context, iosb_ptr, IOCTL_AFD_POLL, - info, - sizeof *info, - info, - sizeof *info); + info_in, + sizeof *info_in, + info_out, + sizeof *info_out); if (overlapped == NULL) { /* If this is a blocking operation, wait for the event to become */ diff --git a/deps/uv/test/test-dlerror.c b/deps/uv/test/test-dlerror.c index 877ebf3712..091200edbe 100644 --- a/deps/uv/test/test-dlerror.c +++ b/deps/uv/test/test-dlerror.c @@ -26,31 +26,28 @@ TEST_IMPL(dlerror) { const char* path = "test/fixtures/load_error.node"; + const char* dlerror_no_error = "no error"; const char* msg; uv_lib_t lib; int r; -#ifdef __linux__ - const char* dlerror_desc = "file too short"; -#elif defined (__sun__) - const char* dlerror_desc = "unknown file type"; -#elif defined (_WIN32) - const char* dlerror_desc = "%1 is not a valid Win32 application"; -#else - const char* dlerror_desc = ""; -#endif + lib.errmsg = NULL; + lib.handle = NULL; + msg = uv_dlerror(&lib); + ASSERT(msg != NULL); + ASSERT(strstr(msg, dlerror_no_error) != NULL); r = uv_dlopen(path, &lib); ASSERT(r == -1); msg = uv_dlerror(&lib); ASSERT(msg != NULL); - ASSERT(strstr(msg, dlerror_desc) != NULL); + ASSERT(strstr(msg, dlerror_no_error) == NULL); /* Should return the same error twice in a row. */ msg = uv_dlerror(&lib); ASSERT(msg != NULL); - ASSERT(strstr(msg, dlerror_desc) != NULL); + ASSERT(strstr(msg, dlerror_no_error) == NULL); uv_dlclose(&lib); diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index 4744425882..0bfb9e41e0 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -24,6 +24,7 @@ TEST_DECLARE (callback_order) TEST_DECLARE (run_once) TEST_DECLARE (run_nowait) TEST_DECLARE (loop_stop) +TEST_DECLARE (loop_configure) TEST_DECLARE (barrier_1) TEST_DECLARE (barrier_2) TEST_DECLARE (barrier_3) @@ -64,7 +65,10 @@ TEST_DECLARE (tcp_connect_error_fault) TEST_DECLARE (tcp_connect_timeout) TEST_DECLARE (tcp_close_while_connecting) TEST_DECLARE (tcp_close) +#ifndef _WIN32 TEST_DECLARE (tcp_close_accept) +TEST_DECLARE (tcp_oob) +#endif TEST_DECLARE (tcp_flags) TEST_DECLARE (tcp_write_to_half_open_connection) TEST_DECLARE (tcp_unexpected_read) @@ -215,6 +219,7 @@ TEST_DECLARE (poll_duplex) TEST_DECLARE (poll_unidirectional) TEST_DECLARE (poll_close) #ifdef _WIN32 +TEST_DECLARE (poll_close_doesnt_corrupt_stack) TEST_DECLARE (poll_closesocket) TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) TEST_DECLARE (argument_escaping) @@ -249,6 +254,7 @@ TASK_LIST_START TEST_ENTRY (run_once) TEST_ENTRY (run_nowait) TEST_ENTRY (loop_stop) + TEST_ENTRY (loop_configure) TEST_ENTRY (barrier_1) TEST_ENTRY (barrier_2) TEST_ENTRY (barrier_3) @@ -308,7 +314,10 @@ TASK_LIST_START TEST_ENTRY (tcp_connect_timeout) TEST_ENTRY (tcp_close_while_connecting) TEST_ENTRY (tcp_close) +#ifndef _WIN32 TEST_ENTRY (tcp_close_accept) + TEST_ENTRY (tcp_oob) +#endif TEST_ENTRY (tcp_flags) TEST_ENTRY (tcp_write_to_half_open_connection) TEST_ENTRY (tcp_unexpected_read) @@ -451,6 +460,7 @@ TASK_LIST_START TEST_ENTRY (kill) #ifdef _WIN32 + TEST_ENTRY (poll_close_doesnt_corrupt_stack) TEST_ENTRY (poll_closesocket) TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows) TEST_ENTRY (argument_escaping) diff --git a/deps/uv/test/test-loop-configure.c b/deps/uv/test/test-loop-configure.c new file mode 100644 index 0000000000..9fefc59201 --- /dev/null +++ b/deps/uv/test/test-loop-configure.c @@ -0,0 +1,40 @@ +/* Copyright (c) 2014, Ben Noordhuis + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +static void timer_cb(uv_timer_t* handle, int status) { + uv_close((uv_handle_t*) handle, NULL); +} + + +TEST_IMPL(loop_configure) { + uv_timer_t timer_handle; + uv_loop_t* loop; + + loop = uv_default_loop(); +#ifdef _WIN32 + ASSERT(UV_ENOSYS == uv_loop_configure(loop, UV_LOOP_BLOCK_SIGNAL, 0)); +#else + ASSERT(0 == uv_loop_configure(loop, UV_LOOP_BLOCK_SIGNAL, SIGPROF)); +#endif + ASSERT(0 == uv_timer_init(loop, &timer_handle)); + ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 10, 0)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/deps/uv/test/test-poll-close-doesnt-corrupt-stack.c b/deps/uv/test/test-poll-close-doesnt-corrupt-stack.c new file mode 100644 index 0000000000..eb10507ac1 --- /dev/null +++ b/deps/uv/test/test-poll-close-doesnt-corrupt-stack.c @@ -0,0 +1,114 @@ +/* Copyright Bert Belder, and other libuv 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. + */ + +#ifdef _WIN32 + +#include +#include + +#include "uv.h" +#include "task.h" + +#ifdef _MSC_VER /* msvc */ +# define NO_INLINE __declspec(noinline) +#else /* gcc */ +# define NO_INLINE __attribute__ ((noinline)) +#endif + + +uv_os_sock_t sock; +uv_poll_t handle; + +static int close_cb_called = 0; + + +static void close_cb(uv_handle_t* h) { + close_cb_called++; +} + + +static void poll_cb(uv_poll_t* h, int status, int events) { + ASSERT(0 && "should never get here"); +} + + +static void NO_INLINE close_socket_and_verify_stack() { + const uint32_t MARKER = 0xDEADBEEF; + const int VERIFY_AFTER = 10; /* ms */ + int r; + + volatile uint32_t data[65536]; + size_t i; + + for (i = 0; i < ARRAY_SIZE(data); i++) + data[i] = MARKER; + + r = closesocket(sock); + ASSERT(r == 0); + + uv_sleep(VERIFY_AFTER); + + for (i = 0; i < ARRAY_SIZE(data); i++) + ASSERT(data[i] == MARKER); +} + + +TEST_IMPL(poll_close_doesnt_corrupt_stack) { + struct WSAData wsa_data; + int r; + unsigned long on; + struct sockaddr_in addr; + + r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + ASSERT(r == 0); + + sock = socket(AF_INET, SOCK_STREAM, 0); + ASSERT(sock != INVALID_SOCKET); + on = 1; + r = ioctlsocket(sock, FIONBIO, &on); + ASSERT(r == 0); + + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + ASSERT(r == 0); + + r = connect(sock, (const struct sockaddr*) &addr, sizeof addr); + ASSERT(r != 0); + ASSERT(WSAGetLastError() == WSAEWOULDBLOCK); + + r = uv_poll_init_socket(uv_default_loop(), &handle, sock); + ASSERT(r == 0); + r = uv_poll_start(&handle, UV_READABLE | UV_WRITABLE, poll_cb); + ASSERT(r == 0); + + uv_close((uv_handle_t*) &handle, close_cb); + + close_socket_and_verify_stack(); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* _WIN32 */ diff --git a/deps/uv/test/test-tcp-oob.c b/deps/uv/test/test-tcp-oob.c new file mode 100644 index 0000000000..35a2420416 --- /dev/null +++ b/deps/uv/test/test-tcp-oob.c @@ -0,0 +1,137 @@ +/* Copyright Fedor Indutny. 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. + */ + +#if !defined(_WIN32) + +#include "uv.h" +#include "task.h" + +#include +#include +#include + +static uv_tcp_t server_handle; +static uv_tcp_t client_handle; +static uv_tcp_t peer_handle; +static uv_idle_t idle; +static uv_connect_t connect_req; +static int ticks; +static const int kMaxTicks = 10; + + +static void set_nonblocking(int fd, int set) { + int r; + int flags = fcntl(fd, F_GETFL, 0); + ASSERT(flags >= 0); + if (set) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + r = fcntl(fd, F_SETFL, flags); + ASSERT(r >= 0); +} + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) { + static char storage[1024]; + return uv_buf_init(storage, sizeof(storage)); +} + + +static void idle_cb(uv_idle_t* idle, int status) { + ASSERT(status == 0); + if (++ticks < kMaxTicks) + return; + + uv_close((uv_handle_t*) &server_handle, NULL); + uv_close((uv_handle_t*) &client_handle, NULL); + uv_close((uv_handle_t*) &peer_handle, NULL); + uv_close((uv_handle_t*) idle, NULL); +} + + +static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { + ASSERT(nread > 0); + ASSERT(0 == uv_idle_start(&idle, idle_cb)); +} + + +static void connect_cb(uv_connect_t* req, int status) { + ASSERT(req->handle == (uv_stream_t*) &client_handle); + ASSERT(0 == status); +} + + +static void connection_cb(uv_stream_t* handle, int status) { + int r; + int fd; + + ASSERT(0 == status); + ASSERT(0 == uv_accept(handle, (uv_stream_t*) &peer_handle)); + ASSERT(0 == uv_read_start((uv_stream_t*) &peer_handle, alloc_cb, read_cb)); + + /* Send some OOB data */ + fd = client_handle.io_watcher.fd; + set_nonblocking(fd, 0); + + /* The problem triggers only on a second message, it seem that xnu is not + * triggering `kevent()` for the first one + */ + do { + r = send(fd, "hello", 5, MSG_OOB); + } while (r < 0 && errno == EINTR); + ASSERT(5 == r); + + do { + r = send(fd, "hello", 5, MSG_OOB); + } while (r < 0 && errno == EINTR); + ASSERT(5 == r); + + set_nonblocking(fd, 1); +} + + +TEST_IMPL(tcp_oob) { + uv_loop_t* loop; + loop = uv_default_loop(); + + ASSERT(0 == uv_tcp_init(loop, &server_handle)); + ASSERT(0 == uv_tcp_init(loop, &client_handle)); + ASSERT(0 == uv_tcp_init(loop, &peer_handle)); + ASSERT(0 == uv_idle_init(loop, &idle)); + ASSERT(0 == uv_tcp_bind(&server_handle, uv_ip4_addr("127.0.0.1", TEST_PORT))); + ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 1, connection_cb)); + + /* Ensure two separate packets */ + ASSERT(0 == uv_tcp_nodelay(&client_handle, 1)); + + ASSERT(0 == uv_tcp_connect(&connect_req, + &client_handle, + uv_ip4_addr("127.0.0.1", TEST_PORT), + connect_cb)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + + ASSERT(ticks == kMaxTicks); + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif diff --git a/deps/uv/uv.gyp b/deps/uv/uv.gyp index 5af99bd59c..d5d0f94078 100644 --- a/deps/uv/uv.gyp +++ b/deps/uv/uv.gyp @@ -133,6 +133,7 @@ 'include/uv-private/uv-darwin.h', 'include/uv-private/uv-bsd.h', 'src/unix/async.c', + 'src/unix/atomic-ops.h', 'src/unix/core.c', 'src/unix/dl.c', 'src/unix/error.c', @@ -311,6 +312,7 @@ 'test/test-list.h', 'test/test-loop-handles.c', 'test/test-loop-stop.c', + 'test/test-loop-configure.c', 'test/test-walk-handles.c', 'test/test-watcher-cross-stop.c', 'test/test-multiple-listen.c', @@ -323,6 +325,7 @@ 'test/test-platform-output.c', 'test/test-poll.c', 'test/test-poll-close.c', + 'test/test-poll-close-doesnt-corrupt-stack.c', 'test/test-poll-closesocket.c', 'test/test-process-title.c', 'test/test-ref.c', @@ -352,6 +355,7 @@ 'test/test-tcp-write-to-half-open-connection.c', 'test/test-tcp-writealot.c', 'test/test-tcp-unexpected-read.c', + 'test/test-tcp-oob.c', 'test/test-tcp-read-stop.c', 'test/test-threadpool.c', 'test/test-threadpool-cancel.c', From a5be84fe9b6862b9d132f87bcc915285e40432e2 Mon Sep 17 00:00:00 2001 From: Julien Gilli Date: Fri, 20 Feb 2015 14:00:32 -0800 Subject: [PATCH 3/8] url: revert reslove urls with . and .. This reverts commit ad0684807c474db5cda7d28592e34e19910eb7ab. Initially, this bug fix targeted master, and I pushed to have it included in v0.10. In retrospect, I'm not sure it should have made into v0.10 as it seems it could break a lot of existing working code. In my opinion, this change is still a bug fix, and it is not backward incompatible per se. However, I'm not sure that taking the risk to break a lot of users with a new 0.10.x release that would include this fix is reasonable, especially now that 0.10.x releases are entering maintenance mode. PR-URL: https://github.com/joyent/node/pull/9257 Reviewed-by: Trevor Norris Reviewed-By: Colin Ihrig --- lib/url.js | 4 ++-- test/simple/test-url.js | 8 -------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/url.js b/lib/url.js index e991f6d63f..d5948e450b 100644 --- a/lib/url.js +++ b/lib/url.js @@ -600,8 +600,8 @@ Url.prototype.resolveObject = function(relative) { // then it must NOT get a trailing slash. var last = srcPath.slice(-1)[0]; var hasTrailingSlash = ( - (result.host || relative.host || srcPath.length > 1) && - (last === '.' || last === '..') || last === ''); + (result.host || relative.host) && (last === '.' || last === '..') || + last === ''); // strip single dots, resolve double dots to parent dir // if the path tries to go above the root, `up` ends up > 0 diff --git a/test/simple/test-url.js b/test/simple/test-url.js index 6c807930b5..95f50a23c3 100644 --- a/test/simple/test-url.js +++ b/test/simple/test-url.js @@ -1087,14 +1087,6 @@ var relativeTests = [ ['/foo/bar/baz/', 'quux/baz', '/foo/bar/baz/quux/baz'], ['/foo/bar/baz', '../../../../../../../../quux/baz', '/quux/baz'], ['/foo/bar/baz', '../../../../../../../quux/baz', '/quux/baz'], - ['/foo', '.', '/'], - ['/foo', '..', '/'], - ['/foo/', '.', '/foo/'], - ['/foo/', '..', '/'], - ['/foo/bar', '.', '/foo/'], - ['/foo/bar', '..', '/'], - ['/foo/bar/', '.', '/foo/bar/'], - ['/foo/bar/', '..', '/foo/'], ['foo/bar', '../../../baz', '../../baz'], ['foo/bar/', '../../../baz', '../baz'], ['http://example.com/b//c//d;p?q#blarg', 'https:#hash2', 'https:///#hash2'], From 51fe319faf4399fd027f8b32d1c425200b911e44 Mon Sep 17 00:00:00 2001 From: Tyler Anton Date: Sat, 7 Mar 2015 21:52:18 -0500 Subject: [PATCH 4/8] docs: add return value for sync fs functions Clarify that synchronous functions in fs with no return value return undefined. Specify that fs.openSync() returns an integer and fs.existsSync() returns true or false. Fixes #9313 PR: #9359 PR-URL: https://github.com/joyent/node/pull/9359 Reviewed-By: Julien Gilli --- doc/api/fs.markdown | 53 ++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/doc/api/fs.markdown b/doc/api/fs.markdown index f886b73dc7..56d66ba4e9 100644 --- a/doc/api/fs.markdown +++ b/doc/api/fs.markdown @@ -77,7 +77,7 @@ to the completion callback. ## fs.renameSync(oldPath, newPath) -Synchronous rename(2). +Synchronous rename(2). Returns `undefined`. ## fs.ftruncate(fd, len, callback) @@ -86,7 +86,7 @@ given to the completion callback. ## fs.ftruncateSync(fd, len) -Synchronous ftruncate(2). +Synchronous ftruncate(2). Returns `undefined`. ## fs.truncate(path, len, callback) @@ -95,7 +95,7 @@ given to the completion callback. ## fs.truncateSync(path, len) -Synchronous truncate(2). +Synchronous truncate(2). Returns `undefined`. ## fs.chown(path, uid, gid, callback) @@ -104,7 +104,7 @@ to the completion callback. ## fs.chownSync(path, uid, gid) -Synchronous chown(2). +Synchronous chown(2). Returns `undefined`. ## fs.fchown(fd, uid, gid, callback) @@ -113,7 +113,7 @@ to the completion callback. ## fs.fchownSync(fd, uid, gid) -Synchronous fchown(2). +Synchronous fchown(2). Returns `undefined`. ## fs.lchown(path, uid, gid, callback) @@ -122,7 +122,7 @@ to the completion callback. ## fs.lchownSync(path, uid, gid) -Synchronous lchown(2). +Synchronous lchown(2). Returns `undefined`. ## fs.chmod(path, mode, callback) @@ -131,7 +131,7 @@ to the completion callback. ## fs.chmodSync(path, mode) -Synchronous chmod(2). +Synchronous chmod(2). Returns `undefined`. ## fs.fchmod(fd, mode, callback) @@ -140,7 +140,7 @@ are given to the completion callback. ## fs.fchmodSync(fd, mode) -Synchronous fchmod(2). +Synchronous fchmod(2). Returns `undefined`. ## fs.lchmod(path, mode, callback) @@ -151,7 +151,7 @@ Only available on Mac OS X. ## fs.lchmodSync(path, mode) -Synchronous lchmod(2). +Synchronous lchmod(2). Returns `undefined`. ## fs.stat(path, callback) @@ -191,7 +191,7 @@ the completion callback. ## fs.linkSync(srcpath, dstpath) -Synchronous link(2). +Synchronous link(2). Returns `undefined`. ## fs.symlink(srcpath, dstpath, [type], callback) @@ -204,7 +204,7 @@ Note that Windows junction points require the destination path to be absolute. ## fs.symlinkSync(srcpath, dstpath, [type]) -Synchronous symlink(2). +Synchronous symlink(2). Returns `undefined`. ## fs.readlink(path, callback) @@ -241,7 +241,7 @@ to the completion callback. ## fs.unlinkSync(path) -Synchronous unlink(2). +Synchronous unlink(2). Returns `undefined`. ## fs.rmdir(path, callback) @@ -250,7 +250,7 @@ to the completion callback. ## fs.rmdirSync(path) -Synchronous rmdir(2). +Synchronous rmdir(2). Returns `undefined`. ## fs.mkdir(path, [mode], callback) @@ -259,7 +259,7 @@ to the completion callback. `mode` defaults to `0777`. ## fs.mkdirSync(path, [mode]) -Synchronous mkdir(2). +Synchronous mkdir(2). Returns `undefined`. ## fs.readdir(path, callback) @@ -279,7 +279,7 @@ to the completion callback. ## fs.closeSync(fd) -Synchronous close(2). +Synchronous close(2). Returns `undefined`. ## fs.open(path, flags, [mode], callback) @@ -340,19 +340,27 @@ the end of the file. ## fs.openSync(path, flags, [mode]) -Synchronous version of `fs.open()`. +Synchronous version of `fs.open()`. Returns an integer representing the file +descriptor. ## fs.utimes(path, atime, mtime, callback) -## fs.utimesSync(path, atime, mtime) Change file timestamps of the file referenced by the supplied path. +## fs.utimesSync(path, atime, mtime) + +Synchronous version of `fs.utimes()`. Returns `undefined`. + + ## fs.futimes(fd, atime, mtime, callback) -## fs.futimesSync(fd, atime, mtime) Change the file timestamps of a file referenced by the supplied file descriptor. +## fs.futimesSync(fd, atime, mtime) + +Synchronous version of `fs.futimes()`. Returns `undefined`. + ## fs.fsync(fd, callback) Asynchronous fsync(2). No arguments other than a possible exception are given @@ -360,7 +368,7 @@ to the completion callback. ## fs.fsyncSync(fd) -Synchronous fsync(2). +Synchronous fsync(2). Returns `undefined`. ## fs.write(fd, buffer, offset, length, position, callback) @@ -461,7 +469,7 @@ Example: ## fs.writeFileSync(filename, data, [options]) -The synchronous version of `fs.writeFile`. +The synchronous version of `fs.writeFile`. Returns `undefined`. ## fs.appendFile(filename, data, [options], callback) @@ -485,7 +493,7 @@ Example: ## fs.appendFileSync(filename, data, [options]) -The synchronous version of `fs.appendFile`. +The synchronous version of `fs.appendFile`. Returns `undefined`. ## fs.watchFile(filename, [options], listener) @@ -605,7 +613,8 @@ and handle the error when it's not there. ## fs.existsSync(path) -Synchronous version of `fs.exists`. +Synchronous version of `fs.exists()`. Returns `true` if the file exists, +`false` otherwise. ## Class: fs.Stats From f2a45caf2e1b73fea01fa9a941bc61096a999664 Mon Sep 17 00:00:00 2001 From: Jonas Dohse Date: Mon, 9 Mar 2015 15:27:04 -0700 Subject: [PATCH 5/8] domains: fix stack clearing after error handled caeb67735baa8e069902e23f21d01033907c4f33 introduced a regression where the domains stack would not be cleared after an error had been handled by the top-level domain. This change clears the domains stack regardless of the position of the active domain in the stack. PR: #9364 PR-URL: https://github.com/joyent/node/pull/9364 Reviewed-By: Trevor Norris Reviewed-By: Julien Gilli --- src/node.js | 20 +++++---- ...in-top-level-error-handler-clears-stack.js | 41 +++++++++++++++++++ 2 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 test/simple/test-domain-top-level-error-handler-clears-stack.js diff --git a/src/node.js b/src/node.js index e95a01fb49..ab560405fa 100644 --- a/src/node.js +++ b/src/node.js @@ -216,6 +216,14 @@ return startup._lazyConstants; }; + function _clearDomainsStack() { + var domainModule = NativeModule.require('domain'); + var domainStack = domainModule._stack; + domainStack.length = 0; + domainModule.active = null; + process.domain = null; + } + startup.processFatal = function() { // call into the active domain, or emit uncaughtException, // and exit if there are no listeners. @@ -268,13 +276,6 @@ // If caught is false after this, then there's no need to exit() // the domain, because we're going to crash the process anyway. caught = domain.emit('error', er); - - // Exit all domains on the stack. Uncaught exceptions end the - // current tick and no domains should be left on the stack - // between ticks. - var domainModule = NativeModule.require('domain'); - domainStack.length = 0; - domainModule.active = process.domain = null; } catch (er2) { // The domain error handler threw! oh no! // See if another domain can catch THIS error, @@ -291,6 +292,11 @@ } } } + + // Exit all domains on the stack. Uncaught exceptions end the + // current tick and no domains should be left on the stack + // between ticks. + _clearDomainsStack(); } else { caught = process.emit('uncaughtException', er); } diff --git a/test/simple/test-domain-top-level-error-handler-clears-stack.js b/test/simple/test-domain-top-level-error-handler-clears-stack.js new file mode 100644 index 0000000000..f95864da64 --- /dev/null +++ b/test/simple/test-domain-top-level-error-handler-clears-stack.js @@ -0,0 +1,41 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +var assert = require('assert'); +var domain = require('domain'); + +/* + * Make sure that the domains stack is cleared after a top-level domain + * error handler exited gracefully. + */ +var d = domain.create(); + +d.on('error', function() { + process.nextTick(function() { + if (domain._stack.length !== 1) { + process.exit(1); + } + }); +}); + +d.run(function() { + throw new Error('Error from domain'); +}); From 219c80d0996304ebb98383eb7eeead2135e37f15 Mon Sep 17 00:00:00 2001 From: Julien Gilli Date: Wed, 11 Mar 2015 11:45:57 -0700 Subject: [PATCH 6/8] build: allow custom PackageMaker path Make PACKAGEMAKER customizable because PackageMaker is not necessarily installed in /Developer on OSX anymore. PR: #9377 PR-URL: https://github.com/joyent/node/pull/9377 Reviewed-By: Timothy J Fontaine --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index e1af3c81d0..ceb4ef7667 100644 --- a/Makefile +++ b/Makefile @@ -223,7 +223,7 @@ TARBALL=$(TARNAME).tar.gz BINARYNAME=$(TARNAME)-$(PLATFORM)-$(ARCH) BINARYTAR=$(BINARYNAME).tar.gz PKG=out/$(TARNAME).pkg -packagemaker=/Developer/Applications/Utilities/PackageMaker.app/Contents/MacOS/PackageMaker +PACKAGEMAKER ?= /Developer/Applications/Utilities/PackageMaker.app/Contents/MacOS/PackageMaker PKGSRC=nodejs-$(DESTCPU)-$(RAWVER).tgz ifdef NIGHTLY @@ -273,7 +273,7 @@ $(PKG): release-only -create mv $(PKGDIR)/usr/local/bin/node-universal $(PKGDIR)/usr/local/bin/node rm -rf $(PKGDIR)/32 - $(packagemaker) \ + $(PACKAGEMAKER) \ --id "org.nodejs.Node" \ --doc tools/osx-pkg.pmdoc \ --out $(PKG) From 7d6b5b1d5ba726331f9ccaaae59af7cd53eee82e Mon Sep 17 00:00:00 2001 From: Julien Gilli Date: Wed, 11 Mar 2015 11:31:30 -0700 Subject: [PATCH 7/8] 2015.03.11, Version 0.10.37 (Maintenance) * uv: update to 0.10.36 (CVE-2015-0278) * domains: fix stack clearing after error handled (Jonas Dohse) * buffer: reword Buffer.concat error message (Chris Dickinson) * console: allow Object.prototype fields as labels (Julien Gilli) * V8: log version in profiler log file (Ben Noordhuis) * http: fix performance regression for GET requests (Florin-Cristian Gavrila) --- AUTHORS | 4 ++++ ChangeLog | 17 ++++++++++++++++- src/node_version.h | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index a3071d1f80..aab71df334 100644 --- a/AUTHORS +++ b/AUTHORS @@ -526,3 +526,7 @@ Saúl Ibarra Corretgé silverwind Steven R. Loomis James M Snell +Amir Saboury +Florin-Cristian Gavrila +Tyler Anton +Jonas Dohse diff --git a/ChangeLog b/ChangeLog index 9d5cae1b55..0bdd5e45e2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,19 @@ -2015.01.26, Version 0.10.36 (Stable) +2015.03.11, Version 0.10.37 (Maintenance) + +* uv: update to 0.10.36 (CVE-2015-0278) + +* domains: fix stack clearing after error handled (Jonas Dohse) + +* buffer: reword Buffer.concat error message (Chris Dickinson) + +* console: allow Object.prototype fields as labels (Julien Gilli) + +* V8: log version in profiler log file (Ben Noordhuis) + +* http: fix performance regression for GET requests (Florin-Cristian Gavrila) + + +2015.01.26, Version 0.10.36 (Stable), 09b482886bdd3d863c3d4e7d71264eac0daaf9e1 * openssl: update to 1.0.1l diff --git a/src/node_version.h b/src/node_version.h index 6baad291a9..684d7ec5d5 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -26,7 +26,7 @@ #define NODE_MINOR_VERSION 10 #define NODE_PATCH_VERSION 37 -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_TAG # define NODE_TAG "" From 4332c77bf7fd5f780fcdac5cac24a876883d8840 Mon Sep 17 00:00:00 2001 From: Julien Gilli Date: Wed, 11 Mar 2015 21:13:15 -0700 Subject: [PATCH 8/8] Now working on 0.10.38 --- src/node_version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node_version.h b/src/node_version.h index 684d7ec5d5..7ff6b5f28d 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -24,9 +24,9 @@ #define NODE_MAJOR_VERSION 0 #define NODE_MINOR_VERSION 10 -#define NODE_PATCH_VERSION 37 +#define NODE_PATCH_VERSION 38 -#define NODE_VERSION_IS_RELEASE 1 +#define NODE_VERSION_IS_RELEASE 0 #ifndef NODE_TAG # define NODE_TAG ""