From 4dc660e164417e0a1bc86eadd825b41d7abb053f Mon Sep 17 00:00:00 2001 From: Julien Gilli Date: Tue, 2 Dec 2014 22:55:53 -0800 Subject: [PATCH 01/16] build: do not generate support for libuv's probes Dtrace probes were removed from libuv recently, but their usage by node was not completely removed, causing build breaks on SmartOS. Even though the build is working on other platforms, these probes are not fired by libuv anymore, so there's no point in using them on these platforms too. Reviewed-by: Trevor Norris --- node.gyp | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/node.gyp b/node.gyp index 1ed8d673eec..b59474ee0ac 100644 --- a/node.gyp +++ b/node.gyp @@ -245,8 +245,7 @@ 'conditions': [ [ 'OS=="linux"', { 'sources': [ - '<(SHARED_INTERMEDIATE_DIR)/node_dtrace_provider.o', - '<(SHARED_INTERMEDIATE_DIR)/libuv_dtrace_provider.o', + '<(SHARED_INTERMEDIATE_DIR)/node_dtrace_provider.o' ], }], [ 'OS!="mac" and OS!="linux"', { @@ -510,15 +509,13 @@ { 'action_name': 'node_dtrace_provider_o', 'inputs': [ - '<(OBJ_DIR)/libuv/deps/uv/src/unix/core.o', '<(OBJ_DIR)/node/src/node_dtrace.o', ], 'outputs': [ '<(OBJ_DIR)/node/src/node_dtrace_provider.o' ], 'action': [ 'dtrace', '-G', '-xnolibs', '-s', 'src/node_provider.d', - '-s', 'deps/uv/src/unix/uv-dtrace.d', '<@(_inputs)', - '-o', '<@(_outputs)' ] + '<@(_inputs)', '-o', '<@(_outputs)' ] } ] }], @@ -533,17 +530,7 @@ 'action': [ 'dtrace', '-C', '-G', '-s', '<@(_inputs)', '-o', '<@(_outputs)' ], - }, - { - 'action_name': 'libuv_dtrace_provider_o', - 'inputs': [ 'deps/uv/src/unix/uv-dtrace.d' ], - 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/libuv_dtrace_provider.o' - ], - 'action': [ - 'dtrace', '-C', '-G', '-s', '<@(_inputs)', '-o', '<@(_outputs)' - ], - }, + } ], }], ] From 20a7088d9c62c43fedf9ab077fbbeae92c7e6617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 9 Dec 2014 21:01:35 +0100 Subject: [PATCH 02/16] deps: update libuv to 1.0.2 PR-URL: https://github.com/joyent/node/pull/8847 Reviewed-by: Trevor Norris --- deps/uv/AUTHORS | 6 +- deps/uv/ChangeLog | 89 +++++++++++++++++++ deps/uv/README.md | 3 +- deps/uv/configure.ac | 2 +- deps/uv/docs/src/conf.py | 2 +- deps/uv/docs/src/design.rst | 6 +- deps/uv/docs/src/dll.rst | 2 +- deps/uv/docs/src/fs.rst | 5 +- deps/uv/docs/src/fs_event.rst | 2 +- deps/uv/docs/src/fs_poll.rst | 2 +- deps/uv/docs/src/loop.rst | 19 +++- deps/uv/docs/src/migration_010_100.rst | 8 +- deps/uv/docs/src/misc.rst | 8 +- deps/uv/docs/src/pipe.rst | 2 +- deps/uv/docs/src/stream.rst | 4 +- deps/uv/docs/src/threadpool.rst | 2 +- deps/uv/include/uv-version.h | 2 +- deps/uv/include/uv-win.h | 5 +- deps/uv/include/uv.h | 4 + deps/uv/src/unix/aix.c | 4 +- deps/uv/src/unix/core.c | 2 +- deps/uv/src/unix/getaddrinfo.c | 4 +- deps/uv/src/unix/internal.h | 5 ++ deps/uv/src/unix/kqueue.c | 17 +++- deps/uv/src/unix/linux-core.c | 28 ++++-- deps/uv/src/unix/linux-syscalls.c | 3 +- deps/uv/src/unix/linux-syscalls.h | 8 +- deps/uv/src/unix/loop.c | 12 +++ deps/uv/src/unix/pipe.c | 13 +-- deps/uv/src/unix/stream.c | 24 +++-- deps/uv/src/unix/sunos.c | 29 ++++-- deps/uv/src/unix/udp.c | 3 - deps/uv/src/uv-common.c | 14 +++ deps/uv/src/uv-common.h | 3 + deps/uv/src/win/core.c | 15 ++-- deps/uv/src/win/fs.c | 4 +- deps/uv/src/win/getaddrinfo.c | 2 +- deps/uv/src/win/poll.c | 34 ++++--- deps/uv/src/win/process.c | 6 +- deps/uv/src/win/tcp.c | 6 +- deps/uv/src/win/thread.c | 4 +- deps/uv/src/win/udp.c | 2 +- deps/uv/src/win/util.c | 4 +- deps/uv/test/run-benchmarks.c | 7 +- deps/uv/test/run-tests.c | 7 +- deps/uv/test/runner-unix.c | 13 ++- deps/uv/test/runner-win.c | 4 +- deps/uv/test/runner.c | 2 +- deps/uv/test/runner.h | 10 ++- deps/uv/test/test-osx-select.c | 2 + .../test/test-pipe-close-stdout-read-stdin.c | 6 +- deps/uv/test/test-tty.c | 7 ++ 52 files changed, 356 insertions(+), 121 deletions(-) diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS index 72fa470dab5..d4c18cf532f 100644 --- a/deps/uv/AUTHORS +++ b/deps/uv/AUTHORS @@ -86,9 +86,7 @@ Nicholas Vavilov Miroslav Bajtoš Sean Silva Wynn Wilkes -Linus Mårtensson Andrei Sedoi -Navaneeth Kedaram Nambiathan Alex Crichton Brent Cook Brian Kaisner @@ -110,7 +108,6 @@ Yazhong Liu Sam Roberts River Tarnell Nathan Sweet -Luca Bruno Trevor Norris Oguz Bastemur Dylan Cali @@ -173,3 +170,6 @@ Yuri D'Elia Manos Nikolaidis Elijah Andrews Michael Ira Krufky +Helge Deller +Joey Geralnik +Tim Caswell diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog index 78600b78edd..e2169998429 100644 --- a/deps/uv/ChangeLog +++ b/deps/uv/ChangeLog @@ -1,3 +1,67 @@ +2014.12.10, Version 1.0.2 (Stable), eec671f0059953505f9a3c9aeb7f9f31466dd7cd + +Changes since version 1.0.1: + +* linux: fix sigmask size arg in epoll_pwait() call (Ben Noordhuis) + +* linux: handle O_NONBLOCK != SOCK_NONBLOCK case (Helge Deller) + +* doc: fix spelling (Joey Geralnik) + +* unix, windows: fix typos in comments (Joey Geralnik) + +* test: canonicalize test runner path (Ben Noordhuis) + +* test: fix compilation warnings (Saúl Ibarra Corretgé) + +* test: skip tty test if detected width and height are 0 (Saúl Ibarra Corretgé) + +* doc: update README with IRC channel (Saúl Ibarra Corretgé) + +* Revert "unix: use cfmakeraw() for setting raw TTY mode" (Ben Noordhuis) + +* doc: document how to get result of uv_fs_mkdtemp (Tim Caswell) + +* unix: add flag for blocking SIGPROF during poll (Ben Noordhuis) + +* unix, windows: add uv_loop_configure() function (Ben Noordhuis) + +* win: keep a reference to AFD_POLL_INFO in cancel poll (Marc Schlaich) + +* test: raise fd limit for OSX select test (Saúl Ibarra Corretgé) + +* unix: remove overzealous assert in uv_read_stop (Saúl Ibarra Corretgé) + +* unix: reset the reading flag when a stream gets EOF (Saúl Ibarra Corretgé) + +* unix: stop reading if an error is produced (Saúl Ibarra Corretgé) + +* cleanup: remove all dead assignments (Maciej Małecki) + +* linux: return early if we have no interfaces (Maciej Małecki) + +* cleanup: remove a dead increment (Maciej Małecki) + + +2014.12.10, Version 0.10.30 (Stable), 5a63f5e9546dca482eeebc3054139b21f509f21f + +Changes since version 0.10.29: + +* linux: fix sigmask size arg in epoll_pwait() call (Ben Noordhuis) + +* linux: handle O_NONBLOCK != SOCK_NONBLOCK case (Helge Deller) + +* doc: update project links (Ben Noordhuis) + +* windows: fix compilation of tests (Marc Schlaich) + +* unix: add flag for blocking SIGPROF during poll (Ben Noordhuis) + +* unix, windows: add uv_loop_configure() function (Ben Noordhuis) + +* win: keep a reference to AFD_POLL_INFO in cancel poll (Marc Schlaich) + + 2014.11.27, Version 1.0.1 (Stable), 0a8e81374e861d425b56c45c8599595d848911d2 Changes since version 1.0.0: @@ -85,6 +149,17 @@ Changes since version 1.0.0-rc1: * windows: fix fs_write with nbufs > 1 and offset (Unknown W. Brackets) +2014.10.21, Version 0.10.29 (Stable), 2d728542d3790183417f8f122a110693cd85db14 + +Changes since version 0.10.28: + +* darwin: allocate enough space for select() hack (Fedor Indutny) + +* linux: try epoll_pwait if epoll_wait is missing (Michael Hudson-Doyle) + +* windows: map ERROR_INVALID_DRIVE to UV_ENOENT (Saúl Ibarra Corretgé) + + 2014.09.18, Version 1.0.0-rc1 (Unstable), 0c28bbf7b42882853d1799ab96ff68b07f7f8d49 Changes since version 0.11.29: @@ -273,6 +348,20 @@ Changes since version 0.11.26: * windows: relay TCP bind errors via ipc (Alexis Campailla) +2014.07.32, Version 0.10.28 (Stable), 9c14b616f5fb84bfd7d45707bab4bbb85894443e + +Changes since version 0.10.27: + +* windows: fix handling closed socket while poll handle is closing (Saúl Ibarra + Corretgé) + +* unix: return system error on EAI_SYSTEM (Saúl Ibarra Corretgé) + +* unix: fix bogus structure field name (Saúl Ibarra Corretgé) + +* darwin: invoke `mach_timebase_info` only once (Fedor Indutny) + + 2014.06.28, Version 0.11.26 (Unstable), 115281a1058c4034d5c5ccedacb667fe3f6327ea Changes since version 0.11.25: diff --git a/deps/uv/README.md b/deps/uv/README.md index 7fc7cfbd063..a267f0d5b52 100644 --- a/deps/uv/README.md +++ b/deps/uv/README.md @@ -36,12 +36,13 @@ used by [Luvit](http://luvit.io/), [Julia](http://julialang.org/), ## Versioning Starting with version 1.0.0 libuv follows the [semantic versioning](http://semver.org/) -scheme. The API change and backwards compatiblity rules are those indicated by +scheme. The API change and backwards compatibility rules are those indicated by SemVer. libuv will keep a stable ABI across major releases. ## Community * [Mailing list](http://groups.google.com/group/libuv) + * [IRC chatroom (#libuv@irc.freenode.org)](http://webchat.freenode.net?channels=libuv&uio=d4) ## Documentation diff --git a/deps/uv/configure.ac b/deps/uv/configure.ac index 247e42bd272..6ae53cc9164 100644 --- a/deps/uv/configure.ac +++ b/deps/uv/configure.ac @@ -13,7 +13,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_PREREQ(2.57) -AC_INIT([libuv], [1.0.1], [https://github.com/libuv/libuv/issues]) +AC_INIT([libuv], [1.0.2], [https://github.com/libuv/libuv/issues]) AC_CONFIG_MACRO_DIR([m4]) m4_include([m4/libuv-extra-automake-flags.m4]) m4_include([m4/as_case.m4]) diff --git a/deps/uv/docs/src/conf.py b/deps/uv/docs/src/conf.py index 9ec9ec2c98d..f614fc5b434 100644 --- a/deps/uv/docs/src/conf.py +++ b/deps/uv/docs/src/conf.py @@ -261,7 +261,7 @@ man_pages = [ # dir menu entry, description, category) texinfo_documents = [ ('index', 'libuv', u'libuv API documentation', - u'libuv contributors', 'libuv', 'Cross-platform asychronous I/O', + u'libuv contributors', 'libuv', 'Cross-platform asynchronous I/O', 'Miscellaneous'), ] diff --git a/deps/uv/docs/src/design.rst b/deps/uv/docs/src/design.rst index 803a4219835..63141bedf58 100644 --- a/deps/uv/docs/src/design.rst +++ b/deps/uv/docs/src/design.rst @@ -8,7 +8,7 @@ libuv is cross-platform support library which was originally written for NodeJS. around the event-driven asynchronous I/O model. The library provides much more than simply abstraction over different I/O polling mechanisms: -'handles' and 'streams' provde a high level abstraction for sockets and other entities; +'handles' and 'streams' provide a high level abstraction for sockets and other entities; cross-platform file I/O and threading functionality is also provided, amongst other things. Here is a diagram illustrating the different parts that compose libuv and what subsystem they @@ -42,7 +42,7 @@ operations, and it's meant to be tied to a single thread. One can run multiple e as long as each runs in a different thread. The libuv event loop (or any other API involving the loop or handles, for that matter) **is not thread-safe** except stated otherwise. -The event loop follows the rather usual single threaded asynchronous I/O approah: all (network) +The event loop follows the rather usual single threaded asynchronous I/O approach: all (network) I/O is performed on non-blocking sockets which are polled using the best mechanism available on the given platform: epoll on Linux, kqueue on OSX and other BSDs, event ports on SunOS and IOCP on Windows. As part of a loop iteration the loop will block waiting for I/O activity on sockets @@ -104,7 +104,7 @@ stages of a loop iteration: #. Iteration ends. If the loop was run with ``UV_RUN_NOWAIT`` or ``UV_RUN_ONCE`` modes the iteration is ended and :c:func:`uv_run` will return. If the loop was run with ``UV_RUN_DEFAULT`` - it will contionue from the start if it's asill *alive*, otherwise it will also end. + it will continue from the start if it's still *alive*, otherwise it will also end. .. important:: diff --git a/deps/uv/docs/src/dll.rst b/deps/uv/docs/src/dll.rst index 3afa31f39d0..3fb11e192db 100644 --- a/deps/uv/docs/src/dll.rst +++ b/deps/uv/docs/src/dll.rst @@ -4,7 +4,7 @@ Shared library handling ======================= -libuv prodives cross platform utilities for loading shared libraries and +libuv provides cross platform utilities for loading shared libraries and retrieving symbols from them, using the following API. diff --git a/deps/uv/docs/src/fs.rst b/deps/uv/docs/src/fs.rst index 27d92d0b453..cd535f756fc 100644 --- a/deps/uv/docs/src/fs.rst +++ b/deps/uv/docs/src/fs.rst @@ -191,6 +191,9 @@ API Equivalent to ``mkdtemp(3)``. + .. note:: + The result can be found as a null terminated string at `req->path`. + .. c:function:: int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) Equivalent to ``rmdir(2)``. @@ -258,7 +261,7 @@ API * ``UV_FS_SYMLINK_DIR``: indicates that `path` points to a directory. * ``UV_FS_SYMLINK_JUNCTION``: request that the symlink is created - using junktion points. + using junction points. .. c:function:: int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) diff --git a/deps/uv/docs/src/fs_event.rst b/deps/uv/docs/src/fs_event.rst index eeb6bfbcb99..9bc9939fc2c 100644 --- a/deps/uv/docs/src/fs_event.rst +++ b/deps/uv/docs/src/fs_event.rst @@ -95,7 +95,7 @@ API Get the path being monitored by the handle. The buffer must be preallocated by the user. Returns 0 on success or an error code < 0 in case of failure. - On sucess, `buf` will contain the path and `len` its length. If the buffer + On success, `buf` will contain the path and `len` its length. If the buffer is not big enough UV_ENOBUFS will be returned and len will be set to the required size. diff --git a/deps/uv/docs/src/fs_poll.rst b/deps/uv/docs/src/fs_poll.rst index 7459aac07b8..df310535214 100644 --- a/deps/uv/docs/src/fs_poll.rst +++ b/deps/uv/docs/src/fs_poll.rst @@ -62,7 +62,7 @@ API Get the path being monitored by the handle. The buffer must be preallocated by the user. Returns 0 on success or an error code < 0 in case of failure. - On sucess, `buf` will contain the path and `len` its length. If the buffer + On success, `buf` will contain the path and `len` its length. If the buffer is not big enough UV_ENOBUFS will be returned and len will be set to the required size. diff --git a/deps/uv/docs/src/loop.rst b/deps/uv/docs/src/loop.rst index c63ef7eb67c..0a9e8a60869 100644 --- a/deps/uv/docs/src/loop.rst +++ b/deps/uv/docs/src/loop.rst @@ -50,6 +50,23 @@ API Initializes the given `uv_loop_t` structure. +.. c:function:: int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...) + + Set additional loop options. You should normally call this before the + first call to :c:func:`uv_run` unless mentioned otherwise. + + Returns 0 on success or a UV_E* error code on failure. Be prepared to + handle UV_ENOSYS; it means the loop option is not supported by the platform. + + Supported options: + + - UV_LOOP_BLOCK_SIGNAL: Block a signal when polling for new events. The + second argument to :c:func:`uv_loop_configure` is the signal number. + + This operation is currently only implemented for SIGPROF signals, + to suppress unnecessary wakeups when using a sampling profiler. + Requesting other signals will fail with UV_EINVAL. + .. c:function:: int uv_loop_close(uv_loop_t* loop) Closes all internal loop resources. This function must only be called once @@ -59,7 +76,7 @@ API .. c:function:: uv_loop_t* uv_default_loop(void) Returns the initialized default loop. It may return NULL in case of - allocation failture. + allocation failure. .. c:function:: int uv_run(uv_loop_t* loop, uv_run_mode mode) diff --git a/deps/uv/docs/src/migration_010_100.rst b/deps/uv/docs/src/migration_010_100.rst index 83b38655675..bb6ac1a8092 100644 --- a/deps/uv/docs/src/migration_010_100.rst +++ b/deps/uv/docs/src/migration_010_100.rst @@ -80,7 +80,7 @@ In libuv 0.10 Unix used a threadpool which defaulted to 4 threads, while Windows threads per process. In 1.0, we unified both implementations, so Windows now uses the same implementation Unix -does. The threadppol size can be set by exporting the ``UV_THREADPOOL_SIZE`` environment +does. The threadpool size can be set by exporting the ``UV_THREADPOOL_SIZE`` environment variable. See :c:ref:`threadpool`. @@ -95,7 +95,7 @@ In libuv 0.10 the callback had to return a filled :c:type:`uv_buf_t` by value: return uv_buf_init(malloc(size), size); } -In libuv 1.0 a pointer to a buffer is passed to the callbck, which the user +In libuv 1.0 a pointer to a buffer is passed to the callback, which the user needs to fill: :: @@ -200,7 +200,7 @@ for such function looked like this: ... } -In libuv 1.0, `uv_read2_start` was removed, and the user needs to check if there are penging +In libuv 1.0, `uv_read2_start` was removed, and the user needs to check if there are pending handles using :c:func:`uv_pipe_pending_count` and :c:func:`uv_pipe_pending_type` while in the read callback: @@ -222,7 +222,7 @@ Extracting the file descriptor out of a handle ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ While it wasn't supported by the API, users often accessed the libuv internals in -order to get access to the file descript of a TCP handle, for example. +order to get access to the file descriptor of a TCP handle, for example. :: diff --git a/deps/uv/docs/src/misc.rst b/deps/uv/docs/src/misc.rst index 1e1125ad6b0..4b810fe0847 100644 --- a/deps/uv/docs/src/misc.rst +++ b/deps/uv/docs/src/misc.rst @@ -1,10 +1,10 @@ .. _misc: -Miscelaneous utilities +Miscellaneous utilities ====================== -This section contains miscelaneous functions that don't really belong in any +This section contains miscellaneous functions that don't really belong in any other section. @@ -186,11 +186,11 @@ API .. c:function:: int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size) - Convert a binary structure containing an IPv4 addres to a string. + Convert a binary structure containing an IPv4 address to a string. .. c:function:: int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size) - Convert a binary structure containing an IPv6 addres to a string. + Convert a binary structure containing an IPv6 address to a string. .. c:function:: int uv_inet_ntop(int af, const void* src, char* dst, size_t size) .. c:function:: int uv_inet_pton(int af, const char* src, void* dst) diff --git a/deps/uv/docs/src/pipe.rst b/deps/uv/docs/src/pipe.rst index 9a4a19340b8..614bb2e3b1f 100644 --- a/deps/uv/docs/src/pipe.rst +++ b/deps/uv/docs/src/pipe.rst @@ -39,7 +39,7 @@ API Open an existing file descriptor or HANDLE as a pipe. .. note:: - The user is responsible for setting the dile descriptor in non-blocking mode. + The user is responsible for setting the file descriptor in non-blocking mode. .. c:function:: int uv_pipe_bind(uv_pipe_t* handle, const char* name) diff --git a/deps/uv/docs/src/stream.rst b/deps/uv/docs/src/stream.rst index 44dccbe9426..2c669cf0418 100644 --- a/deps/uv/docs/src/stream.rst +++ b/deps/uv/docs/src/stream.rst @@ -62,7 +62,7 @@ Data types Callback called when a stream server has received an incoming connection. The user can accept the connection by calling :c:func:`uv_accept`. - `status` will de 0 in case of success, < 0 otherwise. + `status` will be 0 in case of success, < 0 otherwise. Public members @@ -200,7 +200,7 @@ API When blocking mode is enabled all writes complete synchronously. The interface remains unchanged otherwise, e.g. completion or failure of the operation will still be reported through a callback which is made - asychronously. + asynchronously. .. warning:: Relying too much on this API is not recommended. It is likely to change diff --git a/deps/uv/docs/src/threadpool.rst b/deps/uv/docs/src/threadpool.rst index 875bb36aeab..66ff53e2305 100644 --- a/deps/uv/docs/src/threadpool.rst +++ b/deps/uv/docs/src/threadpool.rst @@ -5,7 +5,7 @@ Thread pool work scheduling =========================== libuv provides a threadpool which can be used to run user code and get notified -in the loop thread. This thread pool is internally used to run al filesystem +in the loop thread. This thread pool is internally used to run all filesystem operations, as well as getaddrinfo and getnameinfo requests. Its default size is 4, but it can be changed at startup time by setting the diff --git a/deps/uv/include/uv-version.h b/deps/uv/include/uv-version.h index 889abffca30..25c31ab5e10 100644 --- a/deps/uv/include/uv-version.h +++ b/deps/uv/include/uv-version.h @@ -32,7 +32,7 @@ #define UV_VERSION_MAJOR 1 #define UV_VERSION_MINOR 0 -#define UV_VERSION_PATCH 1 +#define UV_VERSION_PATCH 2 #define UV_VERSION_IS_RELEASE 1 #define UV_VERSION_SUFFIX "" diff --git a/deps/uv/include/uv-win.h b/deps/uv/include/uv-win.h index 4abb294c051..0c188e7e22a 100644 --- a/deps/uv/include/uv-win.h +++ b/deps/uv/include/uv-win.h @@ -517,7 +517,10 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); /* Used in fast mode */ \ SOCKET peer_socket; \ AFD_POLL_INFO afd_poll_info_1; \ - AFD_POLL_INFO afd_poll_info_2; \ + union { \ + AFD_POLL_INFO* afd_poll_info_ptr; \ + AFD_POLL_INFO 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/include/uv.h b/deps/uv/include/uv.h index 7f4fa6dd8ad..7b3c25223b2 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -229,6 +229,9 @@ typedef struct uv_cpu_info_s uv_cpu_info_t; typedef struct uv_interface_address_s uv_interface_address_t; typedef struct uv_dirent_s uv_dirent_t; +typedef enum { + UV_LOOP_BLOCK_SIGNAL +} uv_loop_option; typedef enum { UV_RUN_DEFAULT = 0, @@ -257,6 +260,7 @@ UV_EXTERN uv_loop_t* uv_loop_new(void); UV_EXTERN void uv_loop_delete(uv_loop_t*); UV_EXTERN size_t uv_loop_size(void); UV_EXTERN int uv_loop_alive(const uv_loop_t* loop); +UV_EXTERN int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...); UV_EXTERN int uv_run(uv_loop_t*, uv_run_mode mode); UV_EXTERN void uv_stop(uv_loop_t*); diff --git a/deps/uv/src/unix/aix.c b/deps/uv/src/unix/aix.c index eb901113451..349c2b558e4 100644 --- a/deps/uv/src/unix/aix.c +++ b/deps/uv/src/unix/aix.c @@ -151,7 +151,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { * Could maybe mod if we knew for sure no events are removed, but * content of w->events is handled above as not reliable (falls back) * so may require a pollset_query() which would have to be pretty cheap - * compared to a PS_DELETE to be worth optimising. Alternatively, could + * compared to a PS_DELETE to be worth optimizing. Alternatively, could * lazily remove events, squelching them in the mean time. */ pc.cmd = PS_DELETE; if (pollset_ctl(loop->backend_fd, &pc, 1)) { @@ -332,7 +332,7 @@ int uv_exepath(char* buffer, size_t* size) { res = readlink(symlink, temp_buffer, PATH_MAX-1); /* if readlink fails, it is a normal file just copy symlink to the - * outbut buffer. + * output buffer. */ if (res < 0) { assert(*size > strlen(symlink)); diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index e6a076831cc..c08040e5378 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -325,7 +325,7 @@ int uv_run(uv_loop_t* loop, uv_run_mode mode) { uv__run_closing_handles(loop); if (mode == UV_RUN_ONCE) { - /* UV_RUN_ONCE implies forward progess: at least one callback must have + /* UV_RUN_ONCE implies forward progress: at least one callback must have * been invoked when it returns. uv__io_poll() can return without doing * I/O (meaning: no callbacks) when its timeout expires - which means we * have pending timers that satisfy the forward progress constraint. diff --git a/deps/uv/src/unix/getaddrinfo.c b/deps/uv/src/unix/getaddrinfo.c index f6c2de9b438..faf9add9285 100644 --- a/deps/uv/src/unix/getaddrinfo.c +++ b/deps/uv/src/unix/getaddrinfo.c @@ -182,10 +182,8 @@ int uv_getaddrinfo(uv_loop_t* loop, len += service_len; } - if (hostname) { + if (hostname) req->hostname = memcpy(buf + len, hostname, hostname_len); - len += hostname_len; - } uv__work_submit(loop, &req->work_req, diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index b94508cba59..daad61b782f 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -146,6 +146,11 @@ enum { UV_HANDLE_IPV6 = 0x10000 /* Handle is bound to a IPv6 socket. */ }; +/* loop flags */ +enum { + UV_LOOP_BLOCK_SIGPROF = 1 +}; + typedef enum { UV_CLOCK_PRECISE = 0, /* Use the highest resolution clock available. */ UV_CLOCK_FAST = 1 /* Use the fastest clock with <= 1ms granularity. */ diff --git a/deps/uv/src/unix/kqueue.c b/deps/uv/src/unix/kqueue.c index b4f9f5d8405..aaadcd8419a 100644 --- a/deps/uv/src/unix/kqueue.c +++ b/deps/uv/src/unix/kqueue.c @@ -55,9 +55,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { unsigned int nevents; unsigned int revents; QUEUE* q; + uv__io_t* w; + sigset_t* pset; + sigset_t set; uint64_t base; uint64_t diff; - uv__io_t* w; int filter; int fflags; int count; @@ -117,6 +119,13 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { w->events = w->pevents; } + pset = NULL; + if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { + pset = &set; + sigemptyset(pset); + sigaddset(pset, SIGPROF); + } + assert(timeout >= -1); base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ @@ -127,6 +136,9 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { spec.tv_nsec = (timeout % 1000) * 1000000; } + if (pset != NULL) + pthread_sigmask(SIG_BLOCK, pset, NULL); + nfds = kevent(loop->backend_fd, events, nevents, @@ -134,6 +146,9 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { ARRAY_SIZE(events), timeout == -1 ? NULL : &spec); + if (pset != NULL) + pthread_sigmask(SIG_UNBLOCK, pset, NULL); + /* 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. diff --git a/deps/uv/src/unix/linux-core.c b/deps/uv/src/unix/linux-core.c index 7a43630494d..a2145b0f369 100644 --- a/deps/uv/src/unix/linux-core.c +++ b/deps/uv/src/unix/linux-core.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -141,6 +142,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { struct uv__epoll_event e; QUEUE* q; uv__io_t* w; + sigset_t* pset; + sigset_t set; uint64_t base; uint64_t diff; int nevents; @@ -191,12 +194,25 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { w->events = w->pevents; } + pset = NULL; + if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { + pset = &set; + sigemptyset(pset); + sigaddset(pset, SIGPROF); + } + assert(timeout >= -1); base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ for (;;) { - if (!no_epoll_wait) { + if (no_epoll_wait || pset != NULL) { + nfds = uv__epoll_pwait(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout, + pset); + } else { nfds = uv__epoll_wait(loop->backend_fd, events, ARRAY_SIZE(events), @@ -205,12 +221,6 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { no_epoll_wait = 1; continue; } - } else { - nfds = uv__epoll_pwait(loop->backend_fd, - events, - ARRAY_SIZE(events), - timeout, - NULL); } /* Update loop->time unconditionally. It's tempting to skip the update when @@ -744,6 +754,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, return -errno; *count = 0; + *addresses = NULL; /* Count the number of interfaces */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { @@ -756,6 +767,9 @@ int uv_interface_addresses(uv_interface_address_t** addresses, (*count)++; } + if (*count == 0) + return 0; + *addresses = malloc(*count * sizeof(**addresses)); if (!(*addresses)) return -ENOMEM; diff --git a/deps/uv/src/unix/linux-syscalls.c b/deps/uv/src/unix/linux-syscalls.c index 1ff8abd197f..e036fad5ef6 100644 --- a/deps/uv/src/unix/linux-syscalls.c +++ b/deps/uv/src/unix/linux-syscalls.c @@ -21,6 +21,7 @@ #include "linux-syscalls.h" #include +#include #include #include #include @@ -328,7 +329,7 @@ int uv__epoll_pwait(int epfd, nevents, timeout, sigmask, - sizeof(*sigmask)); + _NSIG / 8); #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 0f0b34b1ed3..fd6bb48665f 100644 --- a/deps/uv/src/unix/linux-syscalls.h +++ b/deps/uv/src/unix/linux-syscalls.h @@ -44,7 +44,7 @@ #if defined(__alpha__) # define UV__O_NONBLOCK 0x4 #elif defined(__hppa__) -# define UV__O_NONBLOCK 0x10004 +# define UV__O_NONBLOCK O_NONBLOCK #elif defined(__mips__) # define UV__O_NONBLOCK 0x80 #elif defined(__sparc__) @@ -60,7 +60,11 @@ #define UV__IN_NONBLOCK UV__O_NONBLOCK #define UV__SOCK_CLOEXEC UV__O_CLOEXEC -#define UV__SOCK_NONBLOCK UV__O_NONBLOCK +#if defined(SOCK_NONBLOCK) +# define UV__SOCK_NONBLOCK SOCK_NONBLOCK +#else +# define UV__SOCK_NONBLOCK UV__O_NONBLOCK +#endif /* epoll flags */ #define UV__EPOLL_CLOEXEC UV__O_CLOEXEC diff --git a/deps/uv/src/unix/loop.c b/deps/uv/src/unix/loop.c index 002224855c2..616cf5bc43b 100644 --- a/deps/uv/src/unix/loop.c +++ b/deps/uv/src/unix/loop.c @@ -192,3 +192,15 @@ static void uv__loop_close(uv_loop_t* loop) { loop->watchers = NULL; loop->nwatchers = 0; } + + +int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { + if (option != UV_LOOP_BLOCK_SIGNAL) + return UV_ENOSYS; + + if (va_arg(ap, int) != SIGPROF) + return UV_EINVAL; + + loop->flags |= UV_LOOP_BLOCK_SIGPROF; + return 0; +} diff --git a/deps/uv/src/unix/pipe.c b/deps/uv/src/unix/pipe.c index a26c3dbc135..b20fb9210c0 100644 --- a/deps/uv/src/unix/pipe.c +++ b/deps/uv/src/unix/pipe.c @@ -44,13 +44,10 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { struct sockaddr_un saddr; const char* pipe_fname; int sockfd; - int bound; int err; pipe_fname = NULL; sockfd = -1; - bound = 0; - err = -EINVAL; /* Already bound? */ if (uv__stream_fd(handle) >= 0) @@ -83,7 +80,6 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { err = -EACCES; goto out; } - bound = 1; /* Success. */ handle->pipe_fname = pipe_fname; /* Is a strdup'ed copy. */ @@ -91,11 +87,9 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { return 0; out: - if (bound) { - /* unlink() before uv__close() to avoid races. */ - assert(pipe_fname != NULL); - unlink(pipe_fname); - } + /* unlink() before uv__close() to avoid races. */ + assert(pipe_fname != NULL); + unlink(pipe_fname); uv__close(sockfd); free((void*)pipe_fname); return err; @@ -158,7 +152,6 @@ void uv_pipe_connect(uv_connect_t* req, int r; new_sock = (uv__stream_fd(handle) == -1); - err = -EINVAL; if (new_sock) { err = uv__socket(AF_UNIX, SOCK_STREAM, 0); diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index 9c7d28cbf4d..d41a3429a78 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -549,7 +549,6 @@ int uv_accept(uv_stream_t* server, uv_stream_t* client) { if (server->accepted_fd == -1) return -EAGAIN; - err = 0; switch (client->type) { case UV_NAMED_PIPE: case UV_TCP: @@ -951,6 +950,7 @@ static void uv__stream_eof(uv_stream_t* stream, const uv_buf_t* buf) { uv__handle_stop(stream); uv__stream_osx_interrupt_select(stream); stream->read_cb(stream, UV_EOF, buf); + stream->flags &= ~UV_STREAM_READING; } @@ -1117,8 +1117,13 @@ static void uv__read(uv_stream_t* stream) { } else { /* Error. User should call uv_close(). */ stream->read_cb(stream, -errno, &buf); - assert(!uv__io_active(&stream->io_watcher, UV__POLLIN) && - "stream->read_cb(status=-1) did not call uv_close()"); + if (stream->flags & UV_STREAM_READING) { + stream->flags &= ~UV_STREAM_READING; + uv__io_stop(stream->loop, &stream->io_watcher, UV__POLLIN); + if (!uv__io_active(&stream->io_watcher, UV__POLLOUT)) + uv__handle_stop(stream); + uv__stream_osx_interrupt_select(stream); + } } return; } else if (nread == 0) { @@ -1319,7 +1324,7 @@ int uv_write2(uv_write_t* req, /* It's legal for write_queue_size > 0 even when the write_queue is empty; * it means there are error-state requests in the write_completed_queue that * will touch up write_queue_size later, see also uv__write_req_finish(). - * We chould check that write_queue is empty instead but that implies making + * We could check that write_queue is empty instead but that implies making * a write() syscall when we know that the handle is in error mode. */ empty_queue = (stream->write_queue_size == 0); @@ -1471,15 +1476,8 @@ int uv_read_start(uv_stream_t* stream, int uv_read_stop(uv_stream_t* stream) { - /* Sanity check. We're going to stop the handle unless it's primed for - * writing but that means there should be some kind of write action in - * progress. - */ - assert(!uv__io_active(&stream->io_watcher, UV__POLLOUT) || - !QUEUE_EMPTY(&stream->write_completed_queue) || - !QUEUE_EMPTY(&stream->write_queue) || - stream->shutdown_req != NULL || - stream->connect_req != NULL); + if (!(stream->flags & UV_STREAM_READING)) + return 0; stream->flags &= ~UV_STREAM_READING; uv__io_stop(stream->loop, &stream->io_watcher, UV__POLLIN); diff --git a/deps/uv/src/unix/sunos.c b/deps/uv/src/unix/sunos.c index a630dba759a..d6fb7f49509 100644 --- a/deps/uv/src/unix/sunos.c +++ b/deps/uv/src/unix/sunos.c @@ -122,6 +122,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { struct timespec spec; QUEUE* q; uv__io_t* w; + sigset_t* pset; + sigset_t set; uint64_t base; uint64_t diff; unsigned int nfds; @@ -129,6 +131,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int saved_errno; int nevents; int count; + int err; int fd; if (loop->nfds == 0) { @@ -150,6 +153,13 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { w->events = w->pevents; } + pset = NULL; + if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { + pset = &set; + sigemptyset(pset); + sigaddset(pset, SIGPROF); + } + assert(timeout >= -1); base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ @@ -165,11 +175,20 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { nfds = 1; saved_errno = 0; - if (port_getn(loop->backend_fd, - events, - ARRAY_SIZE(events), - &nfds, - timeout == -1 ? NULL : &spec)) { + + if (pset != NULL) + pthread_sigmask(SIG_BLOCK, pset, NULL); + + err = port_getn(loop->backend_fd, + events, + ARRAY_SIZE(events), + &nfds, + timeout == -1 ? NULL : &spec); + + if (pset != NULL) + pthread_sigmask(SIG_UNBLOCK, pset, NULL); + + if (err) { /* Work around another kernel bug: port_getn() may return events even * on error. */ diff --git a/deps/uv/src/unix/udp.c b/deps/uv/src/unix/udp.c index 7cafea1d089..71a0e41f1f7 100644 --- a/deps/uv/src/unix/udp.c +++ b/deps/uv/src/unix/udp.c @@ -278,9 +278,6 @@ int uv__udp_bind(uv_udp_t* handle, int yes; int fd; - err = -EINVAL; - fd = -1; - /* Check for bad flags. */ if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR)) return -EINVAL; diff --git a/deps/uv/src/uv-common.c b/deps/uv/src/uv-common.c index 5ba1ea4df4d..f84f8c4ae10 100644 --- a/deps/uv/src/uv-common.c +++ b/deps/uv/src/uv-common.c @@ -24,6 +24,7 @@ #include #include +#include #include /* NULL */ #include /* malloc */ #include /* memset */ @@ -442,3 +443,16 @@ int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) { return 0; } + + +int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...) { + va_list ap; + int err; + + va_start(ap, option); + /* Any platform-agnostic options should be handled here. */ + err = uv__loop_configure(loop, option, ap); + va_end(ap); + + return err; +} diff --git a/deps/uv/src/uv-common.h b/deps/uv/src/uv-common.h index e06606c19ba..7d3c58f1218 100644 --- a/deps/uv/src/uv-common.h +++ b/deps/uv/src/uv-common.h @@ -28,6 +28,7 @@ #define UV_COMMON_H_ #include +#include #include #if defined(_MSC_VER) && _MSC_VER < 1600 @@ -59,6 +60,8 @@ enum { # define UV__HANDLE_CLOSING 0x01 #endif +int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap); + int uv__tcp_bind(uv_tcp_t* tcp, const struct sockaddr* addr, unsigned int addrlen, diff --git a/deps/uv/src/win/core.c b/deps/uv/src/win/core.c index c9e4c88fa73..48897cf29bc 100644 --- a/deps/uv/src/win/core.c +++ b/deps/uv/src/win/core.c @@ -39,7 +39,7 @@ static uv_loop_t default_loop_struct; static uv_loop_t* default_loop_ptr; -/* uv_once intialization guards */ +/* uv_once initialization guards */ static uv_once_t uv_init_guard_ = UV_ONCE_INIT; @@ -103,7 +103,7 @@ static void uv_init(void) { #endif /* Fetch winapi function pointers. This must be done first because other - * intialization code might need these function pointers to be loaded. + * initialization code might need these function pointers to be loaded. */ uv_winapi_init(); @@ -133,7 +133,7 @@ int uv_loop_init(uv_loop_t* loop) { if (loop->iocp == NULL) return uv_translate_sys_error(GetLastError()); - /* To prevent uninitialized memory access, loop->time must be intialized + /* To prevent uninitialized memory access, loop->time must be initialized * to zero before calling uv_update_time for the first time. */ loop->time = 0; @@ -199,7 +199,7 @@ uv_loop_t* uv_default_loop(void) { static void uv__loop_close(uv_loop_t* loop) { size_t i; - /* close the async handle without needeing an extra loop iteration */ + /* close the async handle without needing an extra loop iteration */ assert(!loop->wq_async.async_sent); loop->wq_async.close_cb = NULL; uv__handle_closing(&loop->wq_async); @@ -272,6 +272,11 @@ void uv_loop_delete(uv_loop_t* loop) { } +int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { + return UV_ENOSYS; +} + + int uv_backend_fd(const uv_loop_t* loop) { return -1; } @@ -411,7 +416,7 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { uv_process_endgames(loop); if (mode == UV_RUN_ONCE) { - /* UV_RUN_ONCE implies forward progess: at least one callback must have + /* UV_RUN_ONCE implies forward progress: at least one callback must have * been invoked when it returns. uv__io_poll() can return without doing * I/O (meaning: no callbacks) when its timeout expires - which means we * have pending timers that satisfy the forward progress constraint. diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c index 7208a65c424..30a457a023b 100644 --- a/deps/uv/src/win/fs.c +++ b/deps/uv/src/win/fs.c @@ -947,7 +947,7 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf) { * * Currently it's based on whether the 'readonly' attribute is set, which * makes little sense because the semantics are so different: the 'read-only' - * flag is just a way for a user to protect against accidental deleteion, and + * flag is just a way for a user to protect against accidental deletion, and * serves no security purpose. Windows uses ACLs for that. * * Also people now use uv_fs_chmod() to take away the writable bit for good @@ -956,7 +956,7 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf) { * deleted. * * IOW it's all just a clusterfuck and we should think of something that - * makes slighty more sense. + * makes slightly more sense. * * And uv_fs_chmod should probably just fail on windows or be a total no-op. * There's nothing sensible it can do anyway. diff --git a/deps/uv/src/win/getaddrinfo.c b/deps/uv/src/win/getaddrinfo.c index 787cfd53664..53a6084efe5 100644 --- a/deps/uv/src/win/getaddrinfo.c +++ b/deps/uv/src/win/getaddrinfo.c @@ -296,7 +296,7 @@ int uv_getaddrinfo(uv_loop_t* loop, req->alloc = (void*)alloc_ptr; /* convert node string to UTF16 into allocated memory and save pointer in */ - /* the reques. */ + /* the request. */ if (node != NULL) { req->node = (WCHAR*)alloc_ptr; if (uv_utf8_to_utf16(node, diff --git a/deps/uv/src/win/poll.c b/deps/uv/src/win/poll.c index 85c314828bc..622cbabe399 100644 --- a/deps/uv/src/win/poll.c +++ b/deps/uv/src/win/poll.c @@ -79,7 +79,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 = &handle->afd_poll_info_2.afd_poll_info_ptr[0]; handle->submitted_events_2 = handle->events; handle->mask_events_1 = handle->events; handle->mask_events_2 = 0; @@ -119,18 +119,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; - int result; + AFD_POLL_INFO* afd_poll_info; + DWORD result; - 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 = &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; result = uv_msafd_poll(handle->socket, - &afd_poll_info, + afd_poll_info, uv__get_overlapped_dummy()); if (result == SOCKET_ERROR) { @@ -154,7 +155,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 = &handle->afd_poll_info_2.afd_poll_info_ptr[0]; handle->submitted_events_2 = 0; mask_events = handle->mask_events_2; } else { @@ -546,7 +547,7 @@ int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, handle->flags |= UV_HANDLE_POLL_SLOW; } - /* Intialize 2 poll reqs. */ + /* Initialize 2 poll reqs. */ handle->submitted_events_1 = 0; uv_req_init(loop, (uv_req_t*) &(handle->poll_req_1)); handle->poll_req_1.type = UV_POLL_REQ; @@ -557,6 +558,11 @@ 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) { + return UV_ENOMEM; + } + return 0; } @@ -618,5 +624,9 @@ 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/process.c b/deps/uv/src/win/process.c index 4d04a0e9061..3a0106f82d6 100644 --- a/deps/uv/src/win/process.c +++ b/deps/uv/src/win/process.c @@ -1063,7 +1063,7 @@ int uv_spawn(uv_loop_t* loop, if (options->flags & UV_PROCESS_DETACHED) { /* Note that we're not setting the CREATE_BREAKAWAY_FROM_JOB flag. That - * means that libuv might not let you create a fully deamonized process + * means that libuv might not let you create a fully daemonized process * when run under job control. However the type of job control that libuv * itself creates doesn't trickle down to subprocesses so they can still * daemonize. @@ -1141,7 +1141,7 @@ int uv_spawn(uv_loop_t* loop, assert(!err); /* Make the handle active. It will remain active until the exit callback */ - /* iis made or the handle is closed, whichever happens first. */ + /* is made or the handle is closed, whichever happens first. */ uv__handle_start(process); /* Cleanup, whether we succeeded or failed. */ @@ -1177,7 +1177,7 @@ static int uv__kill(HANDLE process_handle, int signum) { return 0; /* If the process already exited before TerminateProcess was called, */ - /* TerminateProcess will fail with ERROR_ACESS_DENIED. */ + /* TerminateProcess will fail with ERROR_ACCESS_DENIED. */ err = GetLastError(); if (err == ERROR_ACCESS_DENIED && GetExitCodeProcess(process_handle, &status) && diff --git a/deps/uv/src/win/tcp.c b/deps/uv/src/win/tcp.c index 23fadc220da..cff2929e4cc 100644 --- a/deps/uv/src/win/tcp.c +++ b/deps/uv/src/win/tcp.c @@ -241,7 +241,7 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { * allow binding to addresses that are in use by sockets in TIME_WAIT, it * effectively allows 'stealing' a port which is in use by another application. * - * SO_EXCLUSIVEADDRUSE is also not good here because it does cehck all sockets, + * SO_EXCLUSIVEADDRUSE is also not good here because it does check all sockets, * regardless of state, so we'd get an error even if the port is in use by a * socket in TIME_WAIT state. * @@ -590,7 +590,7 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { } /* Initialize other unused requests too, because uv_tcp_endgame */ - /* doesn't know how how many requests were intialized, so it will */ + /* doesn't know how how many requests were initialized, so it will */ /* try to clean up {uv_simultaneous_server_accepts} requests. */ for (i = simultaneous_accepts; i < uv_simultaneous_server_accepts; i++) { req = &handle->accept_reqs[i]; @@ -1342,7 +1342,7 @@ void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) { if (uv_tcp_try_cancel_io(tcp) != 0) { /* When cancellation is not possible, there is another option: we can */ /* close the incoming sockets, which will also cancel the accept */ - /* operations. However this is not cool because we might inadvertedly */ + /* operations. However this is not cool because we might inadvertently */ /* close a socket that just accepted a new connection, which will */ /* cause the connection to be aborted. */ unsigned int i; diff --git a/deps/uv/src/win/thread.c b/deps/uv/src/win/thread.c index 7143743926e..a697d7ae744 100644 --- a/deps/uv/src/win/thread.c +++ b/deps/uv/src/win/thread.c @@ -100,7 +100,7 @@ static NOINLINE void uv__once_inner(uv_once_t* guard, } else { /* We lost the race. Destroy the event we created and wait for the */ - /* existing one todv become signaled. */ + /* existing one to become signaled. */ CloseHandle(created_event); result = WaitForSingleObject(existing_event, INFINITE); assert(result == WAIT_OBJECT_0); @@ -155,7 +155,7 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { ctx->arg = arg; /* Create the thread in suspended state so we have a chance to pass - * its own creation handle to it */ + * its own creation handle to it */ thread = (HANDLE) _beginthreadex(NULL, 0, uv__thread_start, diff --git a/deps/uv/src/win/udp.c b/deps/uv/src/win/udp.c index 99fd80fce9d..73b5bd5e467 100644 --- a/deps/uv/src/win/udp.c +++ b/deps/uv/src/win/udp.c @@ -83,7 +83,7 @@ static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket, } if (pSetFileCompletionNotificationModes) { - /* All know windowses that support SetFileCompletionNotificationModes */ + /* All known Windows that support SetFileCompletionNotificationModes */ /* have a bug that makes it impossible to use this function in */ /* conjunction with datagram sockets. We can work around that but only */ /* if the user is using the default UDP driver (AFD) and has no other */ diff --git a/deps/uv/src/win/util.c b/deps/uv/src/win/util.c index 0bcb721a524..43d843ff5c4 100644 --- a/deps/uv/src/win/util.c +++ b/deps/uv/src/win/util.c @@ -44,7 +44,7 @@ * of the console title is that it is smaller than 64K. However in practice * it is much smaller, and there is no way to figure out what the exact length * of the title is or can be, at least not on XP. To make it even more - * annoying, GetConsoleTitle failes when the buffer to be read into is bigger + * annoying, GetConsoleTitle fails when the buffer to be read into is bigger * than the actual maximum length. So we make a conservative guess here; * just don't put the novel you're writing in the title, unless the plot * survives truncation. @@ -64,7 +64,7 @@ static double hrtime_interval_ = 0; /* - * One-time intialization code for functionality defined in util.c. + * One-time initialization code for functionality defined in util.c. */ void uv__util_init() { LARGE_INTEGER perf_frequency; diff --git a/deps/uv/test/run-benchmarks.c b/deps/uv/test/run-benchmarks.c index 61f062f99aa..8d4f549799e 100644 --- a/deps/uv/test/run-benchmarks.c +++ b/deps/uv/test/run-benchmarks.c @@ -33,7 +33,8 @@ static int maybe_run_test(int argc, char **argv); int main(int argc, char **argv) { - platform_init(argc, argv); + if (platform_init(argc, argv)) + return EXIT_FAILURE; switch (argc) { case 1: return run_tests(1); @@ -41,8 +42,10 @@ int main(int argc, char **argv) { case 3: return run_test_part(argv[1], argv[2]); default: LOGF("Too many arguments.\n"); - return 1; + return EXIT_FAILURE; } + + return EXIT_SUCCESS; } diff --git a/deps/uv/test/run-tests.c b/deps/uv/test/run-tests.c index d8f3cda540a..e92c93008e7 100644 --- a/deps/uv/test/run-tests.c +++ b/deps/uv/test/run-tests.c @@ -46,7 +46,8 @@ static int maybe_run_test(int argc, char **argv); int main(int argc, char **argv) { - platform_init(argc, argv); + if (platform_init(argc, argv)) + return EXIT_FAILURE; argv = uv_setup_args(argc, argv); @@ -56,8 +57,10 @@ int main(int argc, char **argv) { case 3: return run_test_part(argv[1], argv[2]); default: LOGF("Too many arguments.\n"); - return 1; + return EXIT_FAILURE; } + + return EXIT_SUCCESS; } diff --git a/deps/uv/test/runner-unix.c b/deps/uv/test/runner-unix.c index 9afcd1e4881..1f12c6f12d9 100644 --- a/deps/uv/test/runner-unix.c +++ b/deps/uv/test/runner-unix.c @@ -22,10 +22,11 @@ #include "runner-unix.h" #include "runner.h" +#include #include /* uintptr_t */ #include -#include /* usleep */ +#include /* readlink, usleep */ #include /* strdup */ #include #include @@ -40,7 +41,7 @@ /* Do platform-specific initialization. */ -void platform_init(int argc, char **argv) { +int platform_init(int argc, char **argv) { const char* tap; tap = getenv("UV_TAP_OUTPUT"); @@ -49,8 +50,14 @@ void platform_init(int argc, char **argv) { /* Disable stdio output buffering. */ setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); - strncpy(executable_path, argv[0], sizeof(executable_path) - 1); signal(SIGPIPE, SIG_IGN); + + if (realpath(argv[0], executable_path) == NULL) { + perror("realpath"); + return -1; + } + + return 0; } diff --git a/deps/uv/test/runner-win.c b/deps/uv/test/runner-win.c index 83d76783f6b..97ef7599eb8 100644 --- a/deps/uv/test/runner-win.c +++ b/deps/uv/test/runner-win.c @@ -43,7 +43,7 @@ /* Do platform-specific initialization. */ -void platform_init(int argc, char **argv) { +int platform_init(int argc, char **argv) { const char* tap; tap = getenv("UV_TAP_OUTPUT"); @@ -66,6 +66,8 @@ void platform_init(int argc, char **argv) { setvbuf(stderr, NULL, _IONBF, 0); strcpy(executable_path, argv[0]); + + return 0; } diff --git a/deps/uv/test/runner.c b/deps/uv/test/runner.c index a934b24c6e5..e896d43b762 100644 --- a/deps/uv/test/runner.c +++ b/deps/uv/test/runner.c @@ -26,7 +26,7 @@ #include "task.h" #include "uv.h" -char executable_path[PATHMAX] = { '\0' }; +char executable_path[sizeof(executable_path)]; int tap_output = 0; diff --git a/deps/uv/test/runner.h b/deps/uv/test/runner.h index 97c7312da7b..78f3c880a98 100644 --- a/deps/uv/test/runner.h +++ b/deps/uv/test/runner.h @@ -22,6 +22,7 @@ #ifndef RUNNER_H_ #define RUNNER_H_ +#include /* PATH_MAX */ #include /* FILE */ @@ -83,8 +84,11 @@ typedef struct { #define TEST_HELPER HELPER_ENTRY #define BENCHMARK_HELPER HELPER_ENTRY -#define PATHMAX 1024 -extern char executable_path[PATHMAX]; +#ifdef PATH_MAX +extern char executable_path[PATH_MAX]; +#else +extern char executable_path[4096]; +#endif /* * Include platform-dependent definitions @@ -130,7 +134,7 @@ void print_tests(FILE* stream); */ /* Do platform-specific initialization. */ -void platform_init(int argc, char** argv); +int platform_init(int argc, char** argv); /* Invoke "argv[0] test-name [test-part]". Store process info in *p. */ /* Make sure that all stdio output of the processes is buffered up. */ diff --git a/deps/uv/test/test-osx-select.c b/deps/uv/test/test-osx-select.c index 68e5a841678..49b1bb8229a 100644 --- a/deps/uv/test/test-osx-select.c +++ b/deps/uv/test/test-osx-select.c @@ -90,6 +90,8 @@ TEST_IMPL(osx_select_many_fds) { uv_tty_t tty; uv_tcp_t tcps[1500]; + TEST_FILE_LIMIT(ARRAY_SIZE(tcps) + 2); + r = uv_ip4_addr("127.0.0.1", 0, &addr); ASSERT(r == 0); diff --git a/deps/uv/test/test-pipe-close-stdout-read-stdin.c b/deps/uv/test/test-pipe-close-stdout-read-stdin.c index 26a1ee76c93..3064babf98c 100644 --- a/deps/uv/test/test-pipe-close-stdout-read-stdin.c +++ b/deps/uv/test/test-pipe-close-stdout-read-stdin.c @@ -54,7 +54,8 @@ TEST_IMPL(pipe_close_stdout_read_stdin) { int fd[2]; int status; - pipe(fd); + r = pipe(fd); + ASSERT(r == 0); if ((pid = fork()) == 0) { /* @@ -63,7 +64,8 @@ TEST_IMPL(pipe_close_stdout_read_stdin) { */ close(fd[1]); close(0); - dup(fd[0]); + r = dup(fd[0]); + ASSERT(r != -1); /* Create a stream that reads from the pipe. */ uv_pipe_t stdin_pipe; diff --git a/deps/uv/test/test-tty.c b/deps/uv/test/test-tty.c index fb69910732c..7e1ce266889 100644 --- a/deps/uv/test/test-tty.c +++ b/deps/uv/test/test-tty.c @@ -96,6 +96,13 @@ TEST_IMPL(tty) { printf("width=%d height=%d\n", width, height); + if (width == 0 && height == 0) { + /* Some environments such as containers or Jenkins behave like this + * sometimes */ + MAKE_VALGRIND_HAPPY(); + return TEST_SKIP; + } + /* * Is it a safe assumption that most people have terminals larger than * 10x10? From 8708c7abe59cbcaacc1d59cbc268f2a1d3ba3061 Mon Sep 17 00:00:00 2001 From: Alexis Campailla Date: Wed, 10 Dec 2014 12:58:32 +0100 Subject: [PATCH 03/16] test: mark more tests as flaky Marking these two tests as flaky, since they have been failing intermittenly in recent builds: test-debug-signal-cluster test-cluster-basic --- test/simple/simple.status | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/simple/simple.status b/test/simple/simple.status index f0aa4e99b9b..8a0cbfb3cb8 100644 --- a/test/simple/simple.status +++ b/test/simple/simple.status @@ -1,6 +1,8 @@ prefix simple test-crypto-domains : PASS,FLAKY +test-debug-signal-cluster : PASS,FLAKY +test-cluster-basic : PASS,FLAKY [$system==win32] test-timers-first-fire : PASS,FLAKY @@ -14,4 +16,3 @@ test-util-debug : PASS,FLAKY [$system==macos] [$system==solaris] -test-debug-signal-cluster : PASS,FLAKY From 946cec7b65716f4781808f5ae3b3345f6d8b6c6d Mon Sep 17 00:00:00 2001 From: Juanjo Date: Tue, 14 Oct 2014 12:07:19 +0200 Subject: [PATCH 04/16] lib,src: fix spawnSync ignoring its 'env' option PR-URL: https://github.com/joyent/node/pull/8546 Reviewed-By: Colin Ihrig --- lib/child_process.js | 1 + src/spawn_sync.cc | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/child_process.js b/lib/child_process.js index c4345f90c2a..e18b65435db 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -1262,6 +1262,7 @@ function spawnSync(/*file, args, options*/) { options.file = opts.file; options.args = opts.args; + options.envPairs = opts.envPairs; if (options.killSignal) options.killSignal = lookupSignal(options.killSignal); diff --git a/src/spawn_sync.cc b/src/spawn_sync.cc index 59de8d46304..f13a9e9ed33 100644 --- a/src/spawn_sync.cc +++ b/src/spawn_sync.cc @@ -737,9 +737,9 @@ int SyncProcessRunner::ParseOptions(Local js_value) { r = CopyJsStringArray(js_env_pairs, &env_buffer_); if (r < 0) return r; - uv_process_options_.args = reinterpret_cast(env_buffer_); - } + uv_process_options_.env = reinterpret_cast(env_buffer_); + } Local js_uid = js_options->Get(env()->uid_string()); if (IsSet(js_uid)) { if (!CheckRange(js_uid)) From 4bba87050c2b8aa801d982e93ea767b3abdc2f17 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Tue, 9 Dec 2014 13:47:49 -0500 Subject: [PATCH 05/16] test: add test for spawnSync() env option PR-URL: https://github.com/joyent/node/pull/8845 Reviewed-by: Trevor Norris --- .../test-child-process-spawnsync-env.js | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 test/simple/test-child-process-spawnsync-env.js diff --git a/test/simple/test-child-process-spawnsync-env.js b/test/simple/test-child-process-spawnsync-env.js new file mode 100644 index 00000000000..0cde9ffeefa --- /dev/null +++ b/test/simple/test-child-process-spawnsync-env.js @@ -0,0 +1,35 @@ +// 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 common = require('../common'); +var assert = require('assert'); +var cp = require('child_process'); + +if (process.argv[2] === 'child') { + console.log(process.env.foo); +} else { + var expected = 'bar'; + var child = cp.spawnSync(process.execPath, [__filename, 'child'], { + env: {foo: expected} + }); + + assert.equal(child.stdout.toString().trim(), expected); +} From 5b9e5bdd03ef5c53f370e414c5a964fccb4ac030 Mon Sep 17 00:00:00 2001 From: Ben Burns Date: Sun, 27 Jul 2014 02:04:46 +1200 Subject: [PATCH 06/16] doc: clarify create{Read,Write}Stream fd option Clarify the fd option: it is preferred to the path parameter, omits the "open" event if given, and is available on WriteStreams as well. PR-URL: https://github.com/joyent/node/issues/7707 Fixes: https://github.com/joyent/node/issues/7707 Fixes: https://github.com/joyent/node/issues/7708 Fixes: https://github.com/joyent/node/issues/4367 Reviewed-By: Chris Dickinson --- doc/api/fs.markdown | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/api/fs.markdown b/doc/api/fs.markdown index c6bfa2456bc..124962c2a11 100644 --- a/doc/api/fs.markdown +++ b/doc/api/fs.markdown @@ -745,6 +745,9 @@ Returns a new ReadStream object (See `Readable Stream`). the file instead of the entire file. Both `start` and `end` are inclusive and start at 0. The `encoding` can be `'utf8'`, `'ascii'`, or `'base64'`. +If `fd` is specified, `ReadStream` will ignore the `path` argument and will use +the specified file descriptor. This means that no `open` event will be emitted. + If `autoClose` is false, then the file descriptor won't be closed, even if there's an error. It is your responsibility to close it and make sure there's no file descriptor leak. If `autoClose` is set to true (default @@ -775,6 +778,7 @@ Returns a new WriteStream object (See `Writable Stream`). { flags: 'w', encoding: null, + fd: null, mode: 0666 } `options` may also include a `start` option to allow writing data at @@ -782,6 +786,11 @@ some position past the beginning of the file. Modifying a file rather than replacing it may require a `flags` mode of `r+` rather than the default mode `w`. +Like `ReadStream` above, if `fd` is specified, `WriteStream` will ignore the +`path` argument and will use the specified file descriptor. This means that no +`open` event will be emitted. + + ## Class: fs.WriteStream `WriteStream` is a [Writable Stream](stream.html#stream_class_stream_writable). From 6f6a97958e8723b4b4a3ccf352252b36cf7a0d85 Mon Sep 17 00:00:00 2001 From: Luis Reis Date: Tue, 9 Sep 2014 17:30:15 +0100 Subject: [PATCH 07/16] zlib: support concatenated gzip files Reviewed-By: Fedor Indutny PR-URL: https://github.com/joyent/node/pull/6442 --- lib/zlib.js | 9 +- src/node_zlib.cc | 31 +++++-- ...st-zlib-from-multiple-gzip-with-garbage.js | 83 +++++++++++++++++ test/simple/test-zlib-from-multiple-gzip.js | 74 +++++++++++++++ .../test-zlib-from-multiple-huge-gzip.js | 93 +++++++++++++++++++ 5 files changed, 282 insertions(+), 8 deletions(-) create mode 100644 test/simple/test-zlib-from-multiple-gzip-with-garbage.js create mode 100644 test/simple/test-zlib-from-multiple-gzip.js create mode 100644 test/simple/test-zlib-from-multiple-huge-gzip.js diff --git a/lib/zlib.js b/lib/zlib.js index a44e69fe7af..2acde58fd40 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -580,7 +580,7 @@ Zlib.prototype._processChunk = function(chunk, flushFlag, cb) { self._buffer = new Buffer(self._chunkSize); } - if (availOutAfter === 0) { + if (availOutAfter === 0 || availInAfter > 0) { // Not actually done. Need to reprocess. // Also, update the availInBefore to the availInAfter value, // so that if we have to hit it a third (fourth, etc.) time, @@ -588,6 +588,13 @@ Zlib.prototype._processChunk = function(chunk, flushFlag, cb) { inOff += (availInBefore - availInAfter); availInBefore = availInAfter; + if (availOutAfter !== 0) { + // There is still some data available for reading. + // This is usually a concatenated stream, so, reset and restart. + self.reset(); + self._offset = 0; + } + if (!async) return true; diff --git a/src/node_zlib.cc b/src/node_zlib.cc index 4f0c938998a..95907dd03fd 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -63,6 +63,11 @@ enum node_zlib_mode { UNZIP }; +enum node_zlib_error { + NO_ERROR, + FAILED, + WRITE_PENDING +}; void InitZlib(v8::Handle target); @@ -207,7 +212,7 @@ class ZCtx : public AsyncWrap { if (!async) { // sync version Process(work_req); - if (CheckError(ctx)) + if (CheckError(ctx) == NO_ERROR) AfterSync(ctx, args); return; } @@ -292,7 +297,7 @@ class ZCtx : public AsyncWrap { } - static bool CheckError(ZCtx* ctx) { + static node_zlib_error CheckError(ZCtx* ctx) { // Acceptable error states depend on the type of zlib stream. switch (ctx->err_) { case Z_OK: @@ -305,14 +310,18 @@ class ZCtx : public AsyncWrap { ZCtx::Error(ctx, "Missing dictionary"); else ZCtx::Error(ctx, "Bad dictionary"); - return false; + return FAILED; default: // something else. - ZCtx::Error(ctx, "Zlib error"); - return false; + if (ctx->strm_.total_out == 0) { + ZCtx::Error(ctx, "Zlib error"); + return FAILED; + } else { + return WRITE_PENDING; + } } - return true; + return NO_ERROR; } @@ -326,7 +335,8 @@ class ZCtx : public AsyncWrap { HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); - if (!CheckError(ctx)) + node_zlib_error error = CheckError(ctx); + if (error == FAILED) return; Local avail_out = Integer::New(env->isolate(), @@ -340,6 +350,11 @@ class ZCtx : public AsyncWrap { Local args[2] = { avail_in, avail_out }; ctx->MakeCallback(env->callback_string(), ARRAY_SIZE(args), args); + if (error == WRITE_PENDING) { + ZCtx::Error(ctx, "Zlib error"); + return; + } + ctx->Unref(); if (ctx->pending_close_) ctx->Close(); @@ -557,10 +572,12 @@ class ZCtx : public AsyncWrap { switch (ctx->mode_) { case DEFLATE: case DEFLATERAW: + case GZIP: ctx->err_ = deflateReset(&ctx->strm_); break; case INFLATE: case INFLATERAW: + case GUNZIP: ctx->err_ = inflateReset(&ctx->strm_); break; default: diff --git a/test/simple/test-zlib-from-multiple-gzip-with-garbage.js b/test/simple/test-zlib-from-multiple-gzip-with-garbage.js new file mode 100644 index 00000000000..f6a0185e011 --- /dev/null +++ b/test/simple/test-zlib-from-multiple-gzip-with-garbage.js @@ -0,0 +1,83 @@ +// 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. + +// test unzipping a file that was created by concatenating multiple gzip +// streams. + +var common = require('../common'); +var assert = require('assert'); +var zlib = require('zlib'); + +var util = require('util'); + +var gzipBuffer = new Buffer(128); +var gzipOffset = 0; + +var stream1 = '123\n'; +var stream2 = '456\n'; +var stream3 = '789\n'; + +function gzipAppend(data) { + data.copy(gzipBuffer, gzipOffset); + gzipOffset += data.length; +} + +function writeGzipStream(text, cb) { + var gzip = zlib.createGzip(); + gzip.on('data', gzipAppend); + gzip.write(text, function() { + gzip.flush(function() { + gzip.end(function() { + cb(); + }); + }); + }); +} + +function writeGarbageStream(text, cb) { + gzipAppend(new Buffer(text)); + cb(); +} + +writeGzipStream(stream1, function() { + writeGzipStream(stream2, function() { + writeGarbageStream(stream3, function() { + var gunzip = zlib.createGunzip(); + var gunzippedData = new Buffer(2 * 1024); + var gunzippedOffset = 0; + gunzip.on('data', function (data) { + data.copy(gunzippedData, gunzippedOffset); + gunzippedOffset += data.length; + }); + gunzip.on('error', function() { + assert.equal(gunzippedData.toString('utf8', 0, gunzippedOffset), + stream1 + stream2); + }); + gunzip.on('end', function() { + assert.fail('end event not expected'); + }); + + gunzip.write(gzipBuffer.slice(0, gzipOffset), 'binary', function() { + gunzip.end(); + }); + }); + }); +}); diff --git a/test/simple/test-zlib-from-multiple-gzip.js b/test/simple/test-zlib-from-multiple-gzip.js new file mode 100644 index 00000000000..6f4127a4d30 --- /dev/null +++ b/test/simple/test-zlib-from-multiple-gzip.js @@ -0,0 +1,74 @@ +// 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. + +// test unzipping a file that was created by concatenating multiple gzip +// streams. + +var common = require('../common'); +var assert = require('assert'); +var zlib = require('zlib'); + +var util = require('util'); + +var gzipBuffer = new Buffer(128); +var gzipOffset = 0; + +var stream1 = '123\n'; +var stream2 = '456\n'; +var stream3 = '789\n'; + +function gzipAppend(data) { + data.copy(gzipBuffer, gzipOffset); + gzipOffset += data.length; +} + +function writeGzipStream(text, cb) { + var gzip = zlib.createGzip(); + gzip.on('data', gzipAppend); + gzip.write(text, function() { + gzip.flush(function() { + gzip.end(function() { + cb(); + }); + }); + }); +} + +writeGzipStream(stream1, function() { + writeGzipStream(stream2, function() { + writeGzipStream(stream3, function() { + var gunzip = zlib.createGunzip(); + var gunzippedData = new Buffer(2 * 1024); + var gunzippedOffset = 0; + gunzip.on('data', function (data) { + data.copy(gunzippedData, gunzippedOffset); + gunzippedOffset += data.length; + }); + gunzip.on('end', function() { + assert.equal(gunzippedData.toString('utf8', 0, gunzippedOffset), stream1 + stream2 + stream3); + }); + + gunzip.write(gzipBuffer.slice(0, gzipOffset), 'binary', function() { + gunzip.end(); + }); + }); + }); +}); diff --git a/test/simple/test-zlib-from-multiple-huge-gzip.js b/test/simple/test-zlib-from-multiple-huge-gzip.js new file mode 100644 index 00000000000..5533aafeb72 --- /dev/null +++ b/test/simple/test-zlib-from-multiple-huge-gzip.js @@ -0,0 +1,93 @@ +// 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. + +// test unzipping a file that was created by concatenating multiple gzip +// streams. + +var common = require('../common'); +var assert = require('assert'); +var zlib = require('zlib'); + +var util = require('util'); + +var HUGE = 64 * 1024; + +var originalBuffer = new Buffer(3 * HUGE); +var originalOffset = 0; + +var gzipBuffer = new Buffer(3 * HUGE); +var gzipOffset = 0; + +function getRandomLetter() { + return (Math.random() * (122 - 97)) + 97; +} + +function generateHugeStream() { + var buffer = new Buffer(HUGE); + for (var i = 0; i < HUGE; i++) + buffer.writeUInt8(getRandomLetter(), i); + + buffer.copy(originalBuffer, originalOffset); + originalOffset += HUGE; + + return buffer; +} + +function gzipAppend(data) { + data.copy(gzipBuffer, gzipOffset); + gzipOffset += data.length; +} + +function writeGzipStream(text, cb) { + var gzip = zlib.createGzip(); + gzip.on('data', gzipAppend); + gzip.write(text, function() { + gzip.flush(function() { + gzip.end(function() { + cb(); + }); + }); + }); +} + +writeGzipStream(generateHugeStream(), function() { + writeGzipStream(generateHugeStream(), function() { + writeGzipStream(generateHugeStream(), function() { + var gunzip = zlib.createGunzip(); + var gunzippedData = new Buffer(3 * HUGE); + var gunzippedOffset = 0; + gunzip.on('data', function (data) { + data.copy(gunzippedData, gunzippedOffset); + gunzippedOffset += data.length; + }); + gunzip.on('end', function() { + var gunzippedStr = gunzippedData.toString('utf8', 0, gunzippedOffset); + var originalStr = originalBuffer.toString('utf8', 0, 3 * HUGE); + + assert.equal(gunzippedStr, originalStr); + }); + + gunzip.write(gzipBuffer.slice(0, gzipOffset), 'binary', function() { + gunzip.end(); + }); + }); + }); +}); From e93ff4f0ce1b6e9077dfa64598006478276325b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Mon, 4 Nov 2013 15:59:25 +0100 Subject: [PATCH 08/16] debugger: fix unhandled error in setBreakpoint Fix Interface.setBreakpoint() to correctly handle an attempt to set a breakpoint in the current script when there is no current script. This usually happens when the debugged process is not paused. Fixes: https://github.com/joyent/node/issues/6453 PR-URL: https://github.com/joyent/node/pull/6460 Reviewed-By: Chris Dickinson --- lib/_debugger.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/_debugger.js b/lib/_debugger.js index 41d3fc42101..b8a3177d687 100644 --- a/lib/_debugger.js +++ b/lib/_debugger.js @@ -1360,6 +1360,12 @@ Interface.prototype.setBreakpoint = function(script, line, script = this.client.currentScript; } + if (script === undefined) { + this.print('Cannot determine the current script, ' + + 'make sure the debugged process is paused.'); + return; + } + if (/\(\)$/.test(script)) { // setBreakpoint('functionname()'); var req = { From 93533e98f76c2c9e577af3352b4eb709791f4d1e Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Tue, 11 Nov 2014 12:06:55 +0100 Subject: [PATCH 09/16] src: fix windows build error Fix a Windows-only build error that was introduced in commit 1183ba4 ("zlib: support concatenated gzip files"). Rename the NO_ERROR and FAILED enumerations, they conflict with macros of the same name in . PR-URL: https://github.com/joyent/node/pull/8893 Reviewed-By: Fedor Indutny Reviewed-By: Rod Vagg Reviewed-by: Timothy J Fontaine --- src/node_zlib.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/node_zlib.cc b/src/node_zlib.cc index 95907dd03fd..b930d1d8c3c 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -64,9 +64,9 @@ enum node_zlib_mode { }; enum node_zlib_error { - NO_ERROR, - FAILED, - WRITE_PENDING + kNoError, + kFailed, + kWritePending }; void InitZlib(v8::Handle target); @@ -212,7 +212,7 @@ class ZCtx : public AsyncWrap { if (!async) { // sync version Process(work_req); - if (CheckError(ctx) == NO_ERROR) + if (CheckError(ctx) == kNoError) AfterSync(ctx, args); return; } @@ -310,18 +310,18 @@ class ZCtx : public AsyncWrap { ZCtx::Error(ctx, "Missing dictionary"); else ZCtx::Error(ctx, "Bad dictionary"); - return FAILED; + return kFailed; default: // something else. if (ctx->strm_.total_out == 0) { ZCtx::Error(ctx, "Zlib error"); - return FAILED; + return kFailed; } else { - return WRITE_PENDING; + return kWritePending; } } - return NO_ERROR; + return kNoError; } @@ -336,7 +336,7 @@ class ZCtx : public AsyncWrap { Context::Scope context_scope(env->context()); node_zlib_error error = CheckError(ctx); - if (error == FAILED) + if (error == kFailed) return; Local avail_out = Integer::New(env->isolate(), @@ -350,7 +350,7 @@ class ZCtx : public AsyncWrap { Local args[2] = { avail_in, avail_out }; ctx->MakeCallback(env->callback_string(), ARRAY_SIZE(args), args); - if (error == WRITE_PENDING) { + if (error == kWritePending) { ZCtx::Error(ctx, "Zlib error"); return; } From 91586661c983f45d650644451df73c8649a8d459 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Thu, 4 Dec 2014 12:00:23 -0800 Subject: [PATCH 10/16] stream: switch _writableState.buffer to queue In cases where many small writes are made to a stream lacking _writev, the array data structure backing the WriteReq buffer would greatly increase GC pressure. Specifically, in the fs.WriteStream case, the clearBuffer routine would only clear a single WriteReq from the buffer before exiting, but would cause the entire backing array to be GC'd. Switching to [].shift lessened pressure, but still the bulk of the time was spent in memcpy. This replaces that structure with a linked list-backed queue so that adding and removing from the queue is O(1). In the _writev case, collecting the buffer requires an O(N) loop over the buffer, but that was already being performed to collect callbacks, so slowdown should be neglible. PR-URL: https://github.com/joyent/node/pull/8826 Reviewed-by: Timothy J Fontaine Reviewed-by: Trevor Norris --- lib/_stream_writable.js | 68 +++++++++++++++++++-------- lib/net.js | 2 +- test/simple/test-stream2-transform.js | 2 +- 3 files changed, 50 insertions(+), 22 deletions(-) diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js index 92984eb08eb..39eee61460f 100644 --- a/lib/_stream_writable.js +++ b/lib/_stream_writable.js @@ -28,6 +28,7 @@ Writable.WritableState = WritableState; var util = require('util'); var Stream = require('stream'); +var debug = util.debuglog('stream'); util.inherits(Writable, Stream); @@ -35,6 +36,7 @@ function WriteReq(chunk, encoding, cb) { this.chunk = chunk; this.encoding = encoding; this.callback = cb; + this.next = null; } function WritableState(options, stream) { @@ -109,7 +111,8 @@ function WritableState(options, stream) { // the amount that is being written when _write is called. this.writelen = 0; - this.buffer = []; + this.bufferedRequest = null; + this.lastBufferedRequest = null; // number of pending user-supplied write callbacks // this must be 0 before 'finish' can be emitted @@ -123,6 +126,23 @@ function WritableState(options, stream) { this.errorEmitted = false; } +WritableState.prototype.getBuffer = function writableStateGetBuffer() { + var current = this.bufferedRequest; + var out = []; + while (current) { + out.push(current); + current = current.next; + } + return out; +}; + +Object.defineProperty(WritableState.prototype, 'buffer', { + get: util.deprecate(function() { + return this.getBuffer(); + }, '_writableState.buffer is deprecated. Use ' + + '_writableState.getBuffer() instead.') +}); + function Writable(options) { // Writable ctor is applied to Duplexes, though they're not // instanceof Writable, they're instanceof Readable. @@ -216,7 +236,7 @@ Writable.prototype.uncork = function() { !state.corked && !state.finished && !state.bufferProcessing && - state.buffer.length) + state.bufferedRequest) clearBuffer(this, state); } }; @@ -255,8 +275,15 @@ function writeOrBuffer(stream, state, chunk, encoding, cb) { if (!ret) state.needDrain = true; - if (state.writing || state.corked) - state.buffer.push(new WriteReq(chunk, encoding, cb)); + if (state.writing || state.corked) { + var last = state.lastBufferedRequest; + state.lastBufferedRequest = new WriteReq(chunk, encoding, cb); + if (last) { + last.next = state.lastBufferedRequest; + } else { + state.bufferedRequest = state.lastBufferedRequest; + } + } else doWrite(stream, state, false, len, chunk, encoding, cb); @@ -313,7 +340,7 @@ function onwrite(stream, er) { if (!finished && !state.corked && !state.bufferProcessing && - state.buffer.length) { + state.bufferedRequest) { clearBuffer(stream, state); } @@ -349,17 +376,23 @@ function onwriteDrain(stream, state) { // if there's something in the buffer waiting, then process it function clearBuffer(stream, state) { state.bufferProcessing = true; + var entry = state.bufferedRequest; - if (stream._writev && state.buffer.length > 1) { + if (stream._writev && entry && entry.next) { // Fast case, write everything using _writev() + var buffer = []; var cbs = []; - for (var c = 0; c < state.buffer.length; c++) - cbs.push(state.buffer[c].callback); + while (entry) { + cbs.push(entry.callback); + buffer.push(entry); + entry = entry.next; + } // count the one we are adding, as well. // TODO(isaacs) clean this up state.pendingcb++; - doWrite(stream, state, true, state.length, state.buffer, '', function(err) { + state.lastBufferedRequest = null; + doWrite(stream, state, true, state.length, buffer, '', function(err) { for (var i = 0; i < cbs.length; i++) { state.pendingcb--; cbs[i](err); @@ -367,34 +400,29 @@ function clearBuffer(stream, state) { }); // Clear buffer - state.buffer = []; } else { // Slow case, write chunks one-by-one - for (var c = 0; c < state.buffer.length; c++) { - var entry = state.buffer[c]; + while (entry) { var chunk = entry.chunk; var encoding = entry.encoding; var cb = entry.callback; var len = state.objectMode ? 1 : chunk.length; doWrite(stream, state, false, len, chunk, encoding, cb); - + entry = entry.next; // if we didn't call the onwrite immediately, then // it means that we need to wait until it does. // also, that means that the chunk and cb are currently // being processed, so move the buffer counter past them. if (state.writing) { - c++; break; } } - if (c < state.buffer.length) - state.buffer = state.buffer.slice(c); - else - state.buffer.length = 0; + if (entry === null) + state.lastBufferedRequest = null; } - + state.bufferedRequest = entry; state.bufferProcessing = false; } @@ -435,7 +463,7 @@ Writable.prototype.end = function(chunk, encoding, cb) { function needFinish(stream, state) { return (state.ending && state.length === 0 && - state.buffer.length === 0 && + state.bufferedRequest === null && !state.finished && !state.writing); } diff --git a/lib/net.js b/lib/net.js index fac78f8c04d..f0075b5a33e 100644 --- a/lib/net.js +++ b/lib/net.js @@ -732,7 +732,7 @@ Socket.prototype.__defineGetter__('bytesWritten', function() { data = this._pendingData, encoding = this._pendingEncoding; - state.buffer.forEach(function(el) { + state.getBuffer().forEach(function(el) { if (util.isBuffer(el.chunk)) bytes += el.chunk.length; else diff --git a/test/simple/test-stream2-transform.js b/test/simple/test-stream2-transform.js index 9c9ddd8efc3..6064565be0a 100644 --- a/test/simple/test-stream2-transform.js +++ b/test/simple/test-stream2-transform.js @@ -81,7 +81,7 @@ test('writable side consumption', function(t) { t.equal(tx._readableState.length, 10); t.equal(transformed, 10); t.equal(tx._transformState.writechunk.length, 5); - t.same(tx._writableState.buffer.map(function(c) { + t.same(tx._writableState.getBuffer().map(function(c) { return c.chunk.length; }), [6, 7, 8, 9, 10]); From 890baa03a86de0bf8eb415126b4400195ad61750 Mon Sep 17 00:00:00 2001 From: Jackson Tian Date: Wed, 10 Dec 2014 11:20:11 +0800 Subject: [PATCH 11/16] doc: add details for http res/req end callback Add documentation for the callback parameter of http.ClientRequest's and http.ServerResponse's end methods. Signed-off-by: Julien Gilli --- doc/api/http.markdown | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/api/http.markdown b/doc/api/http.markdown index c639bdfd5d9..3d482b441c8 100644 --- a/doc/api/http.markdown +++ b/doc/api/http.markdown @@ -444,6 +444,8 @@ response. If `data` is specified, it is equivalent to calling `response.write(data, encoding)` followed by `response.end(callback)`. +If `callback` is specified, it will be called when the response stream +is finished. ## http.request(options[, callback]) @@ -890,6 +892,9 @@ chunked, this will send the terminating `'0\r\n\r\n'`. If `data` is specified, it is equivalent to calling `request.write(data, encoding)` followed by `request.end(callback)`. +If `callback` is specified, it will be called when the request stream +is finished. + ### request.abort() Aborts a request. (New since v0.3.8.) From 6a03fce16eaa4ec1085463d94734d40b370f3ea4 Mon Sep 17 00:00:00 2001 From: CGavrila Date: Tue, 28 Oct 2014 12:08:37 +0000 Subject: [PATCH 12/16] url: improve parsing speed The url.parse() function now checks whether an escapable character is in the URL before trying to escape it. PR-URL: https://github.com/joyent/node/pull/8638 [trev.norris@gmail.com: Switch to use continue instead of if] Signed-off-by: Trevor Norris --- lib/url.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/url.js b/lib/url.js index 0302fda1427..ac82d251179 100644 --- a/lib/url.js +++ b/lib/url.js @@ -318,6 +318,8 @@ Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) { // need to be. for (var i = 0, l = autoEscape.length; i < l; i++) { var ae = autoEscape[i]; + if (rest.indexOf(ae) === -1) + continue; var esc = encodeURIComponent(ae); if (esc === ae) { esc = escape(ae); From a30839576c65b88e93bc915ae97b59874afde8ac Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Wed, 12 Nov 2014 17:13:14 -0800 Subject: [PATCH 13/16] build: i18n: add icu config options Make "--with-intl=none" the default and add "intl-none" option to vcbuild.bat. If icu data is missing print a warning unless either --download=all or --download=icu is set. If set then automatically download, verify (MD5) and unpack the ICU data if not already available. There's a "list" of URLs being used, but right now only the first is picked up. The logic works something like this: * If there is no directory deps/icu, * If no zip file (currently icu4c-54_1-src.zip), * Download zip file (icu-project.org -> sf.net) * Verify the MD5 sum of the zipfile * If bad, print error and exit * Unpack the zipfile into deps/icu * If deps/icu now exists, use it, else fail with help text Add the configuration option "--with-icu-source=..." Usage: * --with-icu-source=/path/to/my/other/icu * --with-icu-source=/path/to/icu54.zip * --with-icu-source=/path/to/icu54.tgz * --with-icu-source=http://example.com/icu54.tar.bz2 Add the configuration option "--with-icu-locals=...". Allows choosing which locales are used in the "small-icu" case. Example: configure --with-intl=small-icu --with-icu-locales=tlh,grc,nl (Also note that as of this writing, neither Klingon nor Ancient Greek are in upstream CLDR data. Serving suggestion only.) Don't use hard coded ../../out paths on windows. This was suggested by @misterdjules as it causes test failures. With this fix, "out" is no longer created on windows and the following can run properly: python tools/test.py simple Reduce space by about 1MB with ICU 54 (over without this patch). Also trims a few other source files, but only conditional on the exact ICU version used. This is to future-proof - a file that is unneeded now may be needed in future ICUs. Also: * Update distclean to remove icu related files * Refactor some code into tools/configure.d/nodedownload.py * Update docs * Add test PR-URL: https://github.com/joyent/node/pull/8719 Fixes: https://github.com/joyent/node/issues/7676#issuecomment-64704230 [trev.norris@gmail.com small change to test's whitespace and logic] Signed-off-by: Trevor Norris --- .gitignore | 3 + Makefile | 4 +- README.md | 91 ++++++++++++++-- configure | 137 +++++++++++++++++++++-- test/simple/test-intl.js | 103 ++++++++++++++++++ tools/configure.d/nodedownload.py | 127 ++++++++++++++++++++++ tools/icu/icu-generic.gyp | 174 ++++++++++++++++++++++++------ tools/icu/icu_small.json | 15 +-- tools/icu/icutrim.py | 13 +++ vcbuild.bat | 8 +- 10 files changed, 616 insertions(+), 59 deletions(-) create mode 100644 test/simple/test-intl.js create mode 100644 tools/configure.d/nodedownload.py diff --git a/.gitignore b/.gitignore index 6581dee9d1f..9dda73b2e4d 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,9 @@ ipch/ email.md deps/v8-* deps/icu +deps/icu*.zip +deps/icu*.tgz +deps/icu-tmp ./node_modules .svn/ diff --git a/Makefile b/Makefile index f84c2134499..e463280ac4e 100644 --- a/Makefile +++ b/Makefile @@ -78,10 +78,12 @@ clean: distclean: -rm -rf out - -rm -f config.gypi + -rm -f config.gypi icu_config.gypi -rm -f config.mk -rm -rf node node_g blog.html email.md -rm -rf node_modules + -rm -rf deps/icu + -rm -rf deps/icu4c*.tgz deps/icu4c*.zip deps/icu-tmp test: all $(PYTHON) tools/test.py --mode=release simple message diff --git a/README.md b/README.md index 0032c63c073..acaf24b372c 100644 --- a/README.md +++ b/README.md @@ -83,30 +83,103 @@ make doc man doc/node.1 ``` -### To build `Intl` (ECMA-402) support: +### `Intl` (ECMA-402) support: -*Note:* more docs, including how to reduce disk footprint, are on +[Intl](https://github.com/joyent/node/wiki/Intl) support is not +enabled by default. + +#### "small" (English only) support + +This option will build with "small" (English only) support, but +the full `Intl` (ECMA-402) APIs. With `--download=all` it will +download the ICU library as needed. + +Unix/Macintosh: + +```sh +./configure --with-intl=small-icu --download=all +``` + +Windows: + +```sh +vcbuild small-icu download-all +``` + +The `small-icu` mode builds +with English-only data. You can add full data at runtime. + +*Note:* more docs are on [the wiki](https://github.com/joyent/node/wiki/Intl). +#### Build with full ICU support (all locales supported by ICU): + +With the `--download=all`, this may download ICU if you don't +have an ICU in `deps/icu`. + +Unix/Macintosh: + +```sh +./configure --with-intl=full-icu --download=all +``` + +Windows: + +```sh +vcbuild full-icu download-all +``` + +#### Build with no Intl support `:-(` + +The `Intl` object will not be available. +This is the default at present, so this option is not normally needed. + +Unix/Macintosh: + +```sh +./configure --with-intl=none +``` + +Windows: + +```sh +vcbuild intl-none +``` + #### Use existing installed ICU (Unix/Macintosh only): ```sh pkg-config --modversion icu-i18n && ./configure --with-intl=system-icu ``` -#### Build ICU from source: +#### Build with a specific ICU: -First: Unpack latest ICU - [icu4c-**##.#**-src.tgz](http://icu-project.org/download) (or `.zip`) - as `deps/icu` (You'll have: `deps/icu/source/...`) +You can find other ICU releases at +[the ICU homepage](http://icu-project.org/download). +Download the file named something like `icu4c-**##.#**-src.tgz` (or +`.zip`). -Unix/Macintosh: +Unix/Macintosh: from an already-unpacked ICU ```sh -./configure --with-intl=full-icu +./configure --with-intl=[small-icu,full-icu] --with-icu-source=/path/to/icu ``` -Windows: +Unix/Macintosh: from a local ICU tarball + +```sh +./configure --with-intl=[small-icu,full-icu] --with-icu-source=/path/to/icu.tgz +``` + +Unix/Macintosh: from a tarball URL + +```sh +./configure --with-intl=full-icu --with-icu-source=http://url/to/icu.tgz +``` + +Windows: first unpack latest ICU to `deps/icu` + [icu4c-**##.#**-src.tgz](http://icu-project.org/download) (or `.zip`) + as `deps/icu` (You'll have: `deps/icu/source/...`) ```sh vcbuild full-icu diff --git a/configure b/configure index c558f7f8dd7..51475f03575 100755 --- a/configure +++ b/configure @@ -6,6 +6,8 @@ import re import shlex import subprocess import sys +import shutil +import string CC = os.environ.get('CC', 'cc') @@ -13,6 +15,10 @@ root_dir = os.path.dirname(__file__) sys.path.insert(0, os.path.join(root_dir, 'tools', 'gyp', 'pylib')) from gyp.common import GetFlavor +# imports in tools/configure.d +sys.path.insert(0, os.path.join(root_dir, 'tools', 'configure.d')) +import nodedownload + # parse our options parser = optparse.OptionParser() @@ -236,16 +242,31 @@ parser.add_option('--with-etw', dest='with_etw', help='build with ETW (default is true on Windows)') +parser.add_option('--download', + action='store', + dest='download_list', + help=nodedownload.help()) + parser.add_option('--with-icu-path', action='store', dest='with_icu_path', help='Path to icu.gyp (ICU i18n, Chromium version only.)') +parser.add_option('--with-icu-locales', + action='store', + dest='with_icu_locales', + help='Comma-separated list of locales for "small-icu". Default: "root,en". "root" is assumed.') + parser.add_option('--with-intl', action='store', dest='with_intl', help='Intl mode: none, full-icu, small-icu (default is none)') +parser.add_option('--with-icu-source', + action='store', + dest='with_icu_source', + help='Intl mode: optional local path to icu/ dir, or path/URL of icu source archive.') + parser.add_option('--with-perfctr', action='store_true', dest='with_perfctr', @@ -294,6 +315,8 @@ parser.add_option('--xcode', (options, args) = parser.parse_args() +# set up auto-download list +auto_downloads = nodedownload.parse(options.download_list) def b(value): """Returns the string 'true' if value is truthy, 'false' otherwise.""" @@ -712,6 +735,35 @@ def glob_to_var(dir_base, dir_sub): return list def configure_intl(o): + icus = [ + { + 'url': 'http://download.icu-project.org/files/icu4c/54.1/icu4c-54_1-src.zip', + # from https://ssl.icu-project.org/files/icu4c/54.1/icu4c-src-54_1.md5: + 'md5': '6b89d60e2f0e140898ae4d7f72323bca', + }, + ] + def icu_download(path): + # download ICU, if needed + for icu in icus: + url = icu['url'] + md5 = icu['md5'] + local = url.split('/')[-1] + targetfile = os.path.join(root_dir, 'deps', local) + if not os.path.isfile(targetfile): + if nodedownload.candownload(auto_downloads, "icu"): + nodedownload.retrievefile(url, targetfile) + else: + print ' Re-using existing %s' % targetfile + if os.path.isfile(targetfile): + sys.stdout.write(' Checking file integrity with MD5:\r') + gotmd5 = nodedownload.md5sum(targetfile) + print ' MD5: %s %s' % (gotmd5, targetfile) + if (md5 == gotmd5): + return targetfile + else: + print ' Expected: %s *MISMATCH*' % md5 + print '\n ** Corrupted ZIP? Delete %s to retry download.\n' % targetfile + return None icu_config = { 'variables': {} } @@ -723,11 +775,11 @@ def configure_intl(o): write(icu_config_name, do_not_edit + pprint.pformat(icu_config, indent=2) + '\n') - # small ICU is off by default. # always set icu_small, node.gyp depends on it being defined. o['variables']['icu_small'] = b(False) with_intl = options.with_intl + with_icu_source = options.with_icu_source have_icu_path = bool(options.with_icu_path) if have_icu_path and with_intl: print 'Error: Cannot specify both --with-icu-path and --with-intl' @@ -739,6 +791,13 @@ def configure_intl(o): o['variables']['icu_gyp_path'] = options.with_icu_path return # --with-intl= + # set the default + if with_intl is None: + with_intl = 'none' # The default mode of Intl + # sanity check localelist + if options.with_icu_locales and (with_intl != 'small-icu'): + print 'Error: --with-icu-locales only makes sense with --with-intl=small-icu' + sys.exit(1) if with_intl == 'none' or with_intl is None: o['variables']['v8_enable_i18n_support'] = 0 return # no Intl @@ -746,6 +805,12 @@ def configure_intl(o): # small ICU (English only) o['variables']['v8_enable_i18n_support'] = 1 o['variables']['icu_small'] = b(True) + with_icu_locales = options.with_icu_locales + if not with_icu_locales: + with_icu_locales = 'root,en' + locs = set(with_icu_locales.split(',')) + locs.add('root') # must have root + o['variables']['icu_locales'] = string.join(locs,',') elif with_intl == 'full-icu': # full ICU o['variables']['v8_enable_i18n_support'] = 1 @@ -769,20 +834,78 @@ def configure_intl(o): # Note: non-ICU implementations could use other 'with_intl' # values. + # this is just the 'deps' dir. Used for unpacking. + icu_parent_path = os.path.join(root_dir, 'deps') + + # The full path to the ICU source directory. + icu_full_path = os.path.join(icu_parent_path, 'icu') + + # icu-tmp is used to download and unpack the ICU tarball. + icu_tmp_path = os.path.join(icu_parent_path, 'icu-tmp') + + # --with-icu-source processing + # first, check that they didn't pass --with-icu-source=deps/icu + if with_icu_source and os.path.abspath(icu_full_path) == os.path.abspath(with_icu_source): + print 'Ignoring redundant --with-icu-source=%s' % (with_icu_source) + with_icu_source = None + # if with_icu_source is still set, try to use it. + if with_icu_source: + if os.path.isdir(icu_full_path): + print 'Deleting old ICU source: %s' % (icu_full_path) + shutil.rmtree(icu_full_path) + # now, what path was given? + if os.path.isdir(with_icu_source): + # it's a path. Copy it. + print '%s -> %s' % (with_icu_source, icu_full_path) + shutil.copytree(with_icu_source, icu_full_path) + else: + # could be file or URL. + # Set up temporary area + if os.path.isdir(icu_tmp_path): + shutil.rmtree(icu_tmp_path) + os.mkdir(icu_tmp_path) + icu_tarball = None + if os.path.isfile(with_icu_source): + # it's a file. Try to unpack it. + icu_tarball = with_icu_source + else: + # Can we download it? + local = os.path.join(icu_tmp_path, with_icu_source.split('/')[-1]) # local part + icu_tarball = nodedownload.retrievefile(with_icu_source, local) + # continue with "icu_tarball" + nodedownload.unpack(icu_tarball, icu_tmp_path) + # Did it unpack correctly? Should contain 'icu' + tmp_icu = os.path.join(icu_tmp_path, 'icu') + if os.path.isdir(tmp_icu): + os.rename(tmp_icu, icu_full_path) + shutil.rmtree(icu_tmp_path) + else: + print ' Error: --with-icu-source=%s did not result in an "icu" dir.' % with_icu_source + shutil.rmtree(icu_tmp_path) + sys.exit(1) + # ICU mode. (icu-generic.gyp) byteorder = sys.byteorder o['variables']['icu_gyp_path'] = 'tools/icu/icu-generic.gyp' # ICU source dir relative to root - icu_full_path = os.path.join(root_dir, 'deps/icu') o['variables']['icu_path'] = icu_full_path if not os.path.isdir(icu_full_path): - print 'Error: ICU path is not a directory: %s' % (icu_full_path) + print '* ECMA-402 (Intl) support didn\'t find ICU in %s..' % (icu_full_path) + # can we download (or find) a zipfile? + localzip = icu_download(icu_full_path) + if localzip: + nodedownload.unpack(localzip, icu_parent_path) + if not os.path.isdir(icu_full_path): + print ' Cannot build Intl without ICU in %s.' % (icu_full_path) + print ' (Fix, or disable with "--with-intl=none" )' sys.exit(1) + else: + print '* Using ICU in %s' % (icu_full_path) # Now, what version of ICU is it? We just need the "major", such as 54. # uvernum.h contains it as a #define. uvernum_h = os.path.join(icu_full_path, 'source/common/unicode/uvernum.h') if not os.path.isfile(uvernum_h): - print 'Error: could not load %s - is ICU installed?' % uvernum_h + print ' Error: could not load %s - is ICU installed?' % uvernum_h sys.exit(1) icu_ver_major = None matchVerExp = r'^\s*#define\s+U_ICU_VERSION_SHORT\s+"([^"]*)".*' @@ -792,7 +915,7 @@ def configure_intl(o): if m: icu_ver_major = m.group(1) if not icu_ver_major: - print 'Could not read U_ICU_VERSION_SHORT version from %s' % uvernum_h + print ' Could not read U_ICU_VERSION_SHORT version from %s' % uvernum_h sys.exit(1) icu_endianness = sys.byteorder[0]; # TODO(srl295): EBCDIC should be 'e' o['variables']['icu_ver_major'] = icu_ver_major @@ -819,8 +942,8 @@ def configure_intl(o): # this is the icudt*.dat file which node will be using (platform endianness) o['variables']['icu_data_file'] = icu_data_file if not os.path.isfile(icu_data_path): - print 'Error: ICU prebuilt data file %s does not exist.' % icu_data_path - print 'See the README.md.' + print ' Error: ICU prebuilt data file %s does not exist.' % icu_data_path + print ' See the README.md.' # .. and we're not about to build it from .gyp! sys.exit(1) # map from variable name to subdirs diff --git a/test/simple/test-intl.js b/test/simple/test-intl.js new file mode 100644 index 00000000000..841239a8d94 --- /dev/null +++ b/test/simple/test-intl.js @@ -0,0 +1,103 @@ +// 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 common = require('../common'); +var assert = require('assert'); + +// does node think that i18n was enabled? +var enablei18n = process.config.variables.v8_enable_i18n_support; +if (enablei18n === undefined) { + enablei18n = false; +} + +// is the Intl object present? +var haveIntl = (global.Intl != undefined); + +// Returns true if no specific locale ids were configured (i.e. "all") +// Else, returns true if loc is in the configured list +// Else, returns false +function haveLocale(loc) { + var locs = process.config.variables.icu_locales.split(','); + return locs.indexOf(loc) !== -1; +} + +if (!haveIntl) { + var erMsg = + '"Intl" object is NOT present but v8_enable_i18n_support is ' + + enablei18n; + assert.equal(enablei18n, false, erMsg); + console.log('Skipping Intl tests because Intl object not present.'); + +} else { + var erMsg = + '"Intl" object is present but v8_enable_i18n_support is ' + + enablei18n + + '. Is this test out of date?'; + assert.equal(enablei18n, true, erMsg); + + // Construct a new date at the beginning of Unix time + var date0 = new Date(0); + + // Use the GMT time zone + var GMT = 'Etc/GMT'; + + // Construct an English formatter. Should format to "Jan 70" + var dtf = + new Intl.DateTimeFormat(['en'], + {timeZone: GMT, month: 'short', year: '2-digit'}); + + // If list is specified and doesn't contain 'en' then return. + if (process.config.variables.icu_locales && !haveLocale('en')) { + console.log('Skipping detailed Intl tests because English is not listed ' + + 'as supported.'); + // Smoke test. Does it format anything, or fail? + console.log('Date(0) formatted to: ' + dtf.format(date0)); + return; + } + + // Check with toLocaleString + var localeString = dtf.format(date0); + assert.equal(localeString, 'Jan 70'); + + // Options to request GMT + var optsGMT = {timeZone: GMT}; + + // Test format + localeString = date0.toLocaleString(['en'], optsGMT); + assert.equal(localeString, '1/1/1970, 12:00:00 AM'); + + // number format + assert.equal(new Intl.NumberFormat(['en']).format(12345.67890), '12,345.679'); + + var collOpts = { sensitivity: 'base', ignorePunctuation: true }; + var coll = new Intl.Collator(['en'], collOpts); + + assert.equal(coll.compare('blackbird', 'black-bird'), 0, + 'ignore punctuation failed'); + assert.equal(coll.compare('blackbird', 'red-bird'), -1, + 'compare less failed'); + assert.equal(coll.compare('bluebird', 'blackbird'), 1, + 'compare greater failed'); + assert.equal(coll.compare('Bluebird', 'bluebird'), 0, + 'ignore case failed'); + assert.equal(coll.compare('\ufb03', 'ffi'), 0, + 'ffi ligature (contraction) failed'); +} diff --git a/tools/configure.d/nodedownload.py b/tools/configure.d/nodedownload.py new file mode 100644 index 00000000000..e24efd865f3 --- /dev/null +++ b/tools/configure.d/nodedownload.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python +# Moved some utilities here from ../../configure + +import urllib +import hashlib +import sys +import zipfile +import tarfile +import fpformat +import contextlib + +def formatSize(amt): + """Format a size as a string in MB""" + return fpformat.fix(amt / 1024000., 1) + +def spin(c): + """print out an ASCII 'spinner' based on the value of counter 'c'""" + spin = ".:|'" + return (spin[c % len(spin)]) + +class ConfigOpener(urllib.FancyURLopener): + """fancy opener used by retrievefile. Set a UA""" + # append to existing version (UA) + version = '%s node.js/configure' % urllib.URLopener.version + +def reporthook(count, size, total): + """internal hook used by retrievefile""" + sys.stdout.write(' Fetch: %c %sMB total, %sMB downloaded \r' % + (spin(count), + formatSize(total), + formatSize(count*size))) + +def retrievefile(url, targetfile): + """fetch file 'url' as 'targetfile'. Return targetfile or throw.""" + try: + sys.stdout.write(' <%s>\nConnecting...\r' % url) + sys.stdout.flush() + msg = ConfigOpener().retrieve(url, targetfile, reporthook=reporthook) + print '' # clear the line + return targetfile + except: + print ' ** Error occurred while downloading\n <%s>' % url + raise + +def md5sum(targetfile): + """md5sum a file. Return the hex digest.""" + digest = hashlib.md5() + with open(targetfile, 'rb') as f: + chunk = f.read(1024) + while chunk != "": + digest.update(chunk) + chunk = f.read(1024) + return digest.hexdigest() + +def unpack(packedfile, parent_path): + """Unpacks packedfile into parent_path. Assumes .zip. Returns parent_path""" + if zipfile.is_zipfile(packedfile): + with contextlib.closing(zipfile.ZipFile(packedfile, 'r')) as icuzip: + print ' Extracting zipfile: %s' % packedfile + icuzip.extractall(parent_path) + return parent_path + elif tarfile.is_tarfile(packedfile): + with tarfile.TarFile.open(packedfile, 'r') as icuzip: + print ' Extracting tarfile: %s' % packedfile + icuzip.extractall(parent_path) + return parent_path + else: + packedsuffix = packedfile.lower().split('.')[-1] # .zip, .tgz etc + raise Exception('Error: Don\'t know how to unpack %s with extension %s' % (packedfile, packedsuffix)) + +# List of possible "--download=" types. +download_types = set(['icu']) + +# Default options for --download. +download_default = "none" + +def help(): + """This function calculates the '--help' text for '--download'.""" + return """Select which packages may be auto-downloaded. +valid values are: none, all, %s. (default is "%s").""" % (", ".join(download_types), download_default) + +def set2dict(keys, value=None): + """Convert some keys (iterable) to a dict.""" + return dict((key, value) for (key) in keys) + +def parse(opt): + """This function parses the options to --download and returns a set such as { icu: true }, etc. """ + if not opt: + opt = download_default + + theOpts = set(opt.split(',')) + + if 'all' in theOpts: + # all on + return set2dict(download_types, True) + elif 'none' in theOpts: + # all off + return set2dict(download_types, False) + + # OK. Now, process each of the opts. + theRet = set2dict(download_types, False) + for anOpt in opt.split(','): + if not anOpt or anOpt == "": + # ignore stray commas, etc. + continue + elif anOpt is 'all': + # all on + theRet = dict((key, True) for (key) in download_types) + else: + # turn this one on + if anOpt in download_types: + theRet[anOpt] = True + else: + # future proof: ignore unknown types + print 'Warning: ignoring unknown --download= type "%s"' % anOpt + # all done + return theRet + +def candownload(auto_downloads, package): + if not (package in auto_downloads.keys()): + raise Exception('Internal error: "%s" is not in the --downloads list. Check nodedownload.py' % package) + if auto_downloads[package]: + return True + else: + print """Warning: Not downloading package "%s". You could pass "--download=all" + (Windows: "download-all") to try auto-downloading it.""" % package + return False diff --git a/tools/icu/icu-generic.gyp b/tools/icu/icu-generic.gyp index 220d2c16a66..bb2b5e5e4d5 100644 --- a/tools/icu/icu-generic.gyp +++ b/tools/icu/icu-generic.gyp @@ -11,6 +11,17 @@ }, 'includes': [ '../../icu_config.gypi' ], 'targets': [ + { + # a target for additional uconfig defines, target only + 'target_name': 'icu_uconfig_target', + 'type': 'none', + 'toolsets': [ 'target' ], + 'direct_dependent_settings': { + 'defines': [ + 'UCONFIG_NO_CONVERSION=1', + ] + }, + }, { # a target to hold uconfig defines. # for now these are hard coded, but could be defined. @@ -92,24 +103,74 @@ }, { 'target_name': 'icui18n', - 'type': '<(library)', - 'toolsets': [ 'target' ], - 'sources': [ - '<@(icu_src_i18n)' + 'toolsets': [ 'target', 'host' ], + 'conditions' : [ + ['_toolset=="target"', { + 'type': '<(library)', + 'sources': [ + '<@(icu_src_i18n)' + ], + 'conditions': [ + [ 'icu_ver_major == 54', { 'sources!': [ + ## Strip out the following for ICU 54 only. + ## add more conditions in the future? + ## if your compiler can dead-strip, this will + ## make ZERO difference to binary size. + ## Made ICU-specific for future-proofing. + + # alphabetic index + '../../deps/icu/source/i18n/alphaindex.cpp', + # BOCSU + # misc + '../../deps/icu/source/i18n/regexcmp.cpp', + '../../deps/icu/source/i18n/regexcmp.h', + '../../deps/icu/source/i18n/regexcst.h', + '../../deps/icu/source/i18n/regeximp.cpp', + '../../deps/icu/source/i18n/regeximp.h', + '../../deps/icu/source/i18n/regexst.cpp', + '../../deps/icu/source/i18n/regexst.h', + '../../deps/icu/source/i18n/regextxt.cpp', + '../../deps/icu/source/i18n/regextxt.h', + '../../deps/icu/source/i18n/region.cpp', + '../../deps/icu/source/i18n/region_impl.h', + '../../deps/icu/source/i18n/reldatefmt.cpp', + '../../deps/icu/source/i18n/reldatefmt.h' + '../../deps/icu/source/i18n/scientificformathelper.cpp', + '../../deps/icu/source/i18n/tmunit.cpp', + '../../deps/icu/source/i18n/tmutamt.cpp', + '../../deps/icu/source/i18n/tmutfmt.cpp', + '../../deps/icu/source/i18n/uregex.cpp', + '../../deps/icu/source/i18n/uregexc.cpp', + '../../deps/icu/source/i18n/uregion.cpp', + '../../deps/icu/source/i18n/uspoof.cpp', + '../../deps/icu/source/i18n/uspoof_build.cpp', + '../../deps/icu/source/i18n/uspoof_conf.cpp', + '../../deps/icu/source/i18n/uspoof_conf.h', + '../../deps/icu/source/i18n/uspoof_impl.cpp', + '../../deps/icu/source/i18n/uspoof_impl.h', + '../../deps/icu/source/i18n/uspoof_wsconf.cpp', + '../../deps/icu/source/i18n/uspoof_wsconf.h', + ]}]], + 'include_dirs': [ + '../../deps/icu/source/i18n', + ], + 'defines': [ + 'U_I18N_IMPLEMENTATION=1', + ], + 'dependencies': [ 'icuucx', 'icu_implementation', 'icu_uconfig', 'icu_uconfig_target' ], + 'direct_dependent_settings': { + 'include_dirs': [ + '../../deps/icu/source/i18n', + ], + }, + 'export_dependent_settings': [ 'icuucx', 'icu_uconfig_target' ], + }], + ['_toolset=="host"', { + 'type': 'none', + 'dependencies': [ 'icutools' ], + 'export_dependent_settings': [ 'icutools' ], + }], ], - 'include_dirs': [ - '../../deps/icu/source/i18n', - ], - 'defines': [ - 'U_I18N_IMPLEMENTATION=1', - ], - 'dependencies': [ 'icuucx', 'icu_implementation', 'icu_uconfig' ], - 'direct_dependent_settings': { - 'include_dirs': [ - '../../deps/icu/source/i18n', - ], - }, - 'export_dependent_settings': [ 'icuucx' ], }, # This exports actual ICU data { @@ -146,32 +207,33 @@ # trim down ICU 'action_name': 'icutrim', 'inputs': [ '<(icu_data_in)', 'icu_small.json' ], - 'outputs': [ '../../out/icutmp/icudt<(icu_ver_major)<(icu_endianness).dat' ], + 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/icutmp/icudt<(icu_ver_major)<(icu_endianness).dat' ], 'action': [ 'python', 'icutrim.py', '-P', '../../<(CONFIGURATION_NAME)', '-D', '<(icu_data_in)', '--delete-tmp', - '-T', '../../out/icutmp', + '-T', '<(SHARED_INTERMEDIATE_DIR)/icutmp', '-F', 'icu_small.json', '-O', 'icudt<(icu_ver_major)<(icu_endianness).dat', - '-v' ], + '-v', + '-L', '<(icu_locales)'], }, { # build final .dat -> .obj 'action_name': 'genccode', - 'inputs': [ '../../out/icutmp/icudt<(icu_ver_major)<(icu_endianness).dat' ], - 'outputs': [ '../../out/icudt<(icu_ver_major)<(icu_endianness)_dat.obj' ], + 'inputs': [ '<(SHARED_INTERMEDIATE_DIR)/icutmp/icudt<(icu_ver_major)<(icu_endianness).dat' ], + 'outputs': [ '<(SHARED_INTERMEDIATE_DIR)/icudt<(icu_ver_major)<(icu_endianness)_dat.obj' ], 'action': [ '../../<(CONFIGURATION_NAME)/genccode', '-o', - '-d', '../../out/', + '-d', '<(SHARED_INTERMEDIATE_DIR)/', '-n', 'icudata', '-e', 'icusmdt<(icu_ver_major)', '<@(_inputs)' ], }, ], # This file contains the small ICU data. - 'sources': [ '../../out/icudt<(icu_ver_major)<(icu_endianness)_dat.obj' ], + 'sources': [ '<(SHARED_INTERMEDIATE_DIR)/icudt<(icu_ver_major)<(icu_endianness)_dat.obj' ], } ] ], #end of OS==win and icu_small == true }, { # OS != win 'conditions': [ @@ -235,7 +297,8 @@ '-T', '<(SHARED_INTERMEDIATE_DIR)/icutmp', '-F', 'icu_small.json', '-O', 'icudt<(icu_ver_major)<(icu_endianness).dat', - '-v' ], + '-v', + '-L', '<(icu_locales)'], }, { # rename to get the final entrypoint name right 'action_name': 'rename', @@ -284,19 +347,51 @@ { 'target_name': 'icuuc', 'type': 'none', - 'toolsets': [ 'target' ], - 'dependencies': [ 'icuucx', 'icudata' ], - 'export_dependent_settings': [ 'icuucx', 'icudata' ], + 'toolsets': [ 'target', 'host' ], + 'conditions' : [ + ['_toolset=="host"', { + 'dependencies': [ 'icutools' ], + 'export_dependent_settings': [ 'icutools' ], + }], + ['_toolset=="target"', { + 'dependencies': [ 'icuucx', 'icudata' ], + 'export_dependent_settings': [ 'icuucx', 'icudata' ], + }], + ], }, # This is the 'real' icuuc. - # tools can depend on 'icuuc + stubdata' { 'target_name': 'icuucx', 'type': '<(library)', - 'dependencies': [ 'icu_implementation', 'icu_uconfig' ], + 'dependencies': [ 'icu_implementation', 'icu_uconfig', 'icu_uconfig_target' ], 'toolsets': [ 'target' ], 'sources': [ - '<@(icu_src_common)' + '<@(icu_src_common)', + ], + 'conditions': [ + [ 'icu_ver_major == 54', { 'sources!': [ + ## Strip out the following for ICU 54 only. + ## add more conditions in the future? + ## if your compiler can dead-strip, this will + ## make ZERO difference to binary size. + ## Made ICU-specific for future-proofing. + + # bidi- not needed (yet!) + '../../deps/icu/source/common/ubidi.c', + '../../deps/icu/source/common/ubidiimp.h', + '../../deps/icu/source/common/ubidiln.c', + '../../deps/icu/source/common/ubidiwrt.c', + #'../../deps/icu/source/common/ubidi_props.c', + #'../../deps/icu/source/common/ubidi_props.h', + #'../../deps/icu/source/common/ubidi_props_data.h', + # and the callers + '../../deps/icu/source/common/ushape.cpp', + '../../deps/icu/source/common/usprep.cpp', + '../../deps/icu/source/common/uts46.cpp', + ]}], + [ 'OS == "solaris"', { 'defines': [ + '_XOPEN_SOURCE_EXTENDED=0', + ]}], ], 'include_dirs': [ '../../deps/icu/source/common', @@ -304,7 +399,8 @@ 'defines': [ 'U_COMMON_IMPLEMENTATION=1', ], - 'export_dependent_settings': [ 'icu_uconfig' ], + 'cflags_c': ['-std=c99'], + 'export_dependent_settings': [ 'icu_uconfig', 'icu_uconfig_target' ], 'direct_dependent_settings': { 'include_dirs': [ '../../deps/icu/source/common', @@ -331,6 +427,12 @@ '<@(icu_src_io)', '<@(icu_src_stubdata)', ], + 'sources!': [ + '../../deps/icu/source/tools/toolutil/udbgutil.cpp', + '../../deps/icu/source/tools/toolutil/udbgutil.h', + '../../deps/icu/source/tools/toolutil/dbgutil.cpp', + '../../deps/icu/source/tools/toolutil/dbgutil.h', + ], 'include_dirs': [ '../../deps/icu/source/common', '../../deps/icu/source/i18n', @@ -344,6 +446,12 @@ 'U_TOOLUTIL_IMPLEMENTATION=1', #'DEBUG=0', # http://bugs.icu-project.org/trac/ticket/10977 ], + 'cflags_c': ['-std=c99'], + 'conditions': [ + ['OS == "solaris"', { + 'defines': [ '_XOPEN_SOURCE_EXTENDED=0' ] + }] + ], 'direct_dependent_settings': { 'include_dirs': [ '../../deps/icu/source/common', @@ -359,7 +467,7 @@ }], ], }, - 'export_dependent_settings': [ 'icu_implementation', 'icu_uconfig' ], + 'export_dependent_settings': [ 'icu_uconfig' ], }, # This tool is needed to rebuild .res files from .txt, # or to build index (res_index.txt) files for small-icu diff --git a/tools/icu/icu_small.json b/tools/icu/icu_small.json index ddf7d1204e8..e434794e91c 100644 --- a/tools/icu/icu_small.json +++ b/tools/icu/icu_small.json @@ -1,11 +1,11 @@ { "copyright": "Copyright (c) 2014 IBM Corporation and Others. All Rights Reserved.", - "comment": "icutrim.py config: Trim down ICU to just English, needed for node.js use.", + "comment": "icutrim.py config: Trim down ICU to just a certain locale set, needed for node.js use.", "variables": { "none": { "only": [] }, - "en_only": { + "locales": { "only": [ "root", "en" @@ -15,20 +15,21 @@ } }, "trees": { - "ROOT": "en_only", + "ROOT": "locales", "brkitr": "none", - "coll": "en_only", - "curr": "en_only", + "coll": "locales", + "curr": "locales", "lang": "none", "rbnf": "none", "region": "none", - "zone": "en_only", + "zone": "locales", "converters": "none", "stringprep": "none", "translit": "none", "brkfiles": "none", "brkdict": "none", - "confusables": "none" + "confusables": "none", + "unit": "none" }, "remove": [ "cnvalias.icu", diff --git a/tools/icu/icutrim.py b/tools/icu/icutrim.py index 7f0fb3752e4..517bf39bad3 100755 --- a/tools/icu/icutrim.py +++ b/tools/icu/icutrim.py @@ -65,6 +65,12 @@ parser.add_option("-v","--verbose", action="count", default=0) +parser.add_option('-L',"--locales", + action="store", + dest="locales", + help="sets the 'locales.only' variable", + default=None) + parser.add_option('-e', '--endian', action='store', dest='endian', help='endian, big, little or host, your default is "%s".' % endian, default=endian, metavar='endianness') (options, args) = parser.parse_args() @@ -147,6 +153,13 @@ fi= open(options.filterfile, "rb") config=json.load(fi) fi.close() +if (options.locales): + if not config.has_key("variables"): + config["variables"] = {} + if not config["variables"].has_key("locales"): + config["variables"]["locales"] = {} + config["variables"]["locales"]["only"] = options.locales.split(',') + if (options.verbose > 6): print config diff --git a/vcbuild.bat b/vcbuild.bat index 616b5bb1145..39c656f1878 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -36,6 +36,7 @@ set noperfctr= set noperfctr_arg= set noperfctr_msi_arg= set i18n_arg= +set download_arg= :next-arg if "%1"=="" goto args-done @@ -65,6 +66,8 @@ if /i "%1"=="upload" set upload=1&goto arg-ok if /i "%1"=="jslint" set jslint=1&goto arg-ok if /i "%1"=="small-icu" set i18n_arg=%1&goto arg-ok if /i "%1"=="full-icu" set i18n_arg=%1&goto arg-ok +if /i "%1"=="intl-none" set i18n_arg=%1&goto arg-ok +if /i "%1"=="download-all" set download_arg="--download=all"&goto arg-ok echo Warning: ignoring invalid command line option `%1`. @@ -85,6 +88,7 @@ if defined noperfctr set noperfctr_arg=--without-perfctr& set noperfctr_msi_arg= if "%i18n_arg%"=="full-icu" set i18n_arg=--with-intl=full-icu if "%i18n_arg%"=="small-icu" set i18n_arg=--with-intl=small-icu +if "%i18n_arg%"=="intl-none" set i18n_arg=--with-intl=none :project-gen @rem Skip project generation if requested. @@ -95,7 +99,7 @@ if defined NIGHTLY set TAG=nightly-%NIGHTLY% @rem Generate the VS project. SETLOCAL if defined VS100COMNTOOLS call "%VS100COMNTOOLS%\VCVarsQueryRegistry.bat" - python configure %i18n_arg% %debug_arg% %nosnapshot_arg% %noetw_arg% %noperfctr_arg% --dest-cpu=%target_arch% --tag=%TAG% + python configure %download_arg% %i18n_arg% %debug_arg% %nosnapshot_arg% %noetw_arg% %noperfctr_arg% --dest-cpu=%target_arch% --tag=%TAG% if errorlevel 1 goto create-msvs-files-failed if not exist node.sln goto create-msvs-files-failed echo Project files generated. @@ -232,7 +236,7 @@ python tools/closure_linter/closure_linter/gjslint.py --unix_mode --strict --noj goto exit :help -echo vcbuild.bat [debug/release] [msi] [test-all/test-uv/test-internet/test-pummel/test-simple/test-message] [clean] [noprojgen] [nobuild] [nosign] [x86/x64] +echo vcbuild.bat [debug/release] [msi] [test-all/test-uv/test-internet/test-pummel/test-simple/test-message] [clean] [noprojgen] [small-icu/full-icu/intl-none] [nobuild] [nosign] [x86/x64] [download-all] echo Examples: echo vcbuild.bat : builds release build echo vcbuild.bat debug : builds debug build From 8cfbeed27ab98dffff68c13188d4f3dc1a506c3c Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 2 Jan 2015 17:16:47 -0800 Subject: [PATCH 14/16] docs: update to authors file PR-URL: https://github.com/joyent/node/pull/8964 Reviewed-by: Trevor Norris --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 5aa6137928e..ce538de7b5c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -568,3 +568,4 @@ Kevin Simper Jackson Tian Tristan Berger Mathias Schreck +Steven R. Loomis From b636ba8186d191c52ee36f2f2b1aebbbb4d95575 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Sat, 3 Jan 2015 18:26:26 -0500 Subject: [PATCH 15/16] net: make connect() input validation synchronous Socket.prototype.connect() sometimes throws on bad inputs after an asynchronous operation. This commit makes the input validation synchronous. This commit also removes some hard coded IP addresses. PR-URL: https://github.com/joyent/node/pull/8180 Fixes: https://github.com/joyent/node/issues/8140 Reviewed-By: Trevor Norris Reviewed-By: Fedor Indutny Reviewed-By: Timothy J Fontaine --- lib/net.js | 77 +++++++++--------------- test/simple/test-net-localerror.js | 53 +++++++--------- test/simple/test-process-active-wraps.js | 16 ++++- 3 files changed, 65 insertions(+), 81 deletions(-) diff --git a/lib/net.js b/lib/net.js index f0075b5a33e..0ece1b0f97c 100644 --- a/lib/net.js +++ b/lib/net.js @@ -785,34 +785,18 @@ function connect(self, address, port, addressType, localAddress, localPort) { assert.ok(self._connecting); var err; + if (localAddress || localPort) { - if (localAddress && !exports.isIP(localAddress)) - err = new TypeError( - 'localAddress should be a valid IP: ' + localAddress); - - if (localPort && !util.isNumber(localPort)) - err = new TypeError('localPort should be a number: ' + localPort); - var bind; - switch (addressType) { - case 4: - if (!localAddress) - localAddress = '0.0.0.0'; - bind = self._handle.bind; - break; - case 6: - if (!localAddress) - localAddress = '::'; - bind = self._handle.bind6; - break; - default: - err = new TypeError('Invalid addressType: ' + addressType); - break; - } - - if (err) { - self._destroy(err); + if (addressType === 4) { + localAddress = localAddress || '0.0.0.0'; + bind = self._handle.bind; + } else if (addressType === 6) { + localAddress = localAddress || '::'; + bind = self._handle.bind6; + } else { + self._destroy(new TypeError('Invalid addressType: ' + addressType)); return; } @@ -832,15 +816,12 @@ function connect(self, address, port, addressType, localAddress, localPort) { if (addressType === 6 || addressType === 4) { var req = new TCPConnectWrap(); req.oncomplete = afterConnect; - port = port | 0; - if (port <= 0 || port > 65535) - throw new RangeError('Port should be > 0 and < 65536'); - if (addressType === 6) { - err = self._handle.connect6(req, address, port); - } else if (addressType === 4) { + if (addressType === 4) err = self._handle.connect(req, address, port); - } + else + err = self._handle.connect6(req, address, port); + } else { var req = new PipeConnectWrap(); req.oncomplete = afterConnect; @@ -898,19 +879,26 @@ Socket.prototype.connect = function(options, cb) { if (pipe) { connect(self, options.path); - } else if (!options.host) { - debug('connect: missing host'); - self._host = '127.0.0.1'; - connect(self, self._host, options.port, 4); - } else { var dns = require('dns'); - var host = options.host; + var host = options.host || 'localhost'; + var port = options.port | 0; + var localAddress = options.localAddress; + var localPort = options.localPort; var dnsopts = { family: options.family, hints: 0 }; + if (localAddress && !exports.isIP(localAddress)) + throw new TypeError('localAddress must be a valid IP: ' + localAddress); + + if (localPort && !util.isNumber(localPort)) + throw new TypeError('localPort should be a number: ' + localPort); + + if (port <= 0 || port > 65535) + throw new RangeError('port should be > 0 and < 65536: ' + port); + if (dnsopts.family !== 4 && dnsopts.family !== 6) dnsopts.hints = dns.ADDRCONFIG | dns.V4MAPPED; @@ -936,19 +924,12 @@ Socket.prototype.connect = function(options, cb) { }); } else { timers._unrefActive(self); - - addressType = addressType || 4; - - // node_net.cc handles null host names graciously but user land - // expects remoteAddress to have a meaningful value - ip = ip || (addressType === 4 ? '127.0.0.1' : '0:0:0:0:0:0:0:1'); - connect(self, ip, - options.port, + port, addressType, - options.localAddress, - options.localPort); + localAddress, + localPort); } }); } diff --git a/test/simple/test-net-localerror.js b/test/simple/test-net-localerror.js index c4d04aa921a..d04d9c70720 100644 --- a/test/simple/test-net-localerror.js +++ b/test/simple/test-net-localerror.js @@ -23,39 +23,30 @@ var common = require('../common'); var assert = require('assert'); var net = require('net'); -var server = net.createServer(function(socket) { - assert.ok(false, 'no clients should connect'); -}).listen(common.PORT).on('listening', function() { - server.unref(); + connect({ + host: 'localhost', + port: common.PORT, + localPort: 'foobar', + }, 'localPort should be a number: foobar'); - function test1(next) { - connect({ - host: '127.0.0.1', - port: common.PORT, - localPort: 'foobar', - }, - 'localPort should be a number: foobar', - next); - } + connect({ + host: 'localhost', + port: common.PORT, + localAddress: 'foobar', + }, 'localAddress should be a valid IP: foobar'); - function test2(next) { - connect({ - host: '127.0.0.1', - port: common.PORT, - localAddress: 'foobar', - }, - 'localAddress should be a valid IP: foobar', - next) - } + connect({ + host: 'localhost', + port: 65536 + }, 'port should be > 0 and < 65536: 65536'); - test1(test2); -}) + connect({ + host: 'localhost', + port: 0 + }, 'port should be > 0 and < 65536: 0'); -function connect(opts, msg, cb) { - var client = net.connect(opts).on('connect', function() { - assert.ok(false, 'we should never connect'); - }).on('error', function(err) { - assert.strictEqual(err.message, msg); - if (cb) cb(); - }); +function connect(opts, msg) { + assert.throws(function() { + var client = net.connect(opts); + }, msg); } diff --git a/test/simple/test-process-active-wraps.js b/test/simple/test-process-active-wraps.js index 63fc218debc..bd4941b786e 100644 --- a/test/simple/test-process-active-wraps.js +++ b/test/simple/test-process-active-wraps.js @@ -41,9 +41,16 @@ var handles = []; })(); (function() { + function onlookup() { + setImmediate(function() { + assert.equal(process._getActiveRequests().length, 0); + }); + }; + expect(1, 0); var conn = net.createConnection(common.PORT); - conn.on('error', function() { /* ignore */ }); + conn.on('lookup', onlookup); + conn.on('error', function() { assert(false); }); expect(2, 1); conn.destroy(); expect(2, 1); // client handle doesn't shut down until next tick @@ -52,10 +59,15 @@ var handles = []; (function() { var n = 0; + handles.forEach(function(handle) { handle.once('close', onclose); }); function onclose() { - if (++n === handles.length) setImmediate(expect, 0, 0); + if (++n === handles.length) { + setImmediate(function() { + assert.equal(process._getActiveHandles().length, 0); + }); + } } })(); From 372a2f56bed341a23c435c5a94fbb77dbbd6c600 Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Mon, 5 Jan 2015 02:20:31 -0800 Subject: [PATCH 16/16] smalloc: fix bad assert for zero length data If the data length passed to smalloc.alloc() the array_length will be zero, causing an overflow check to fail. This prevents that from happening. Signed-off-by: Trevor Norris --- src/smalloc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smalloc.cc b/src/smalloc.cc index 7dc3510a0bf..0cd8f3eb9e4 100644 --- a/src/smalloc.cc +++ b/src/smalloc.cc @@ -132,7 +132,7 @@ void CallbackInfo::WeakCallback(Isolate* isolate, Local object) { object->GetIndexedPropertiesExternalArrayDataType(); size_t array_size = ExternalArraySize(array_type); CHECK_GT(array_size, 0); - if (array_size > 1) { + if (array_size > 1 && array_data != NULL) { CHECK_GT(array_length * array_size, array_length); // Overflow check. array_length *= array_size; }