From 028c630ecd920d59fc0e8ad295e0fe0eac8e1ef1 Mon Sep 17 00:00:00 2001 From: Adam Malcontenti-Wilson Date: Thu, 14 Mar 2013 13:17:07 +1100 Subject: [PATCH 01/30] doc: change dgram to socket for properties of dgram.Socket Fixes #4919. --- doc/api/dgram.markdown | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/api/dgram.markdown b/doc/api/dgram.markdown index 707f3488659..bb102b9484b 100644 --- a/doc/api/dgram.markdown +++ b/doc/api/dgram.markdown @@ -38,7 +38,7 @@ to the "all interfaces" address on a random port (it does the right thing for both `udp4` and `udp6` sockets). You can then retrieve the address and port with `socket.address().address` and `socket.address().port`. -## Class: Socket +## Class: dgram.Socket The dgram Socket class encapsulates the datagram functionality. It should be created via `dgram.createSocket(type, [callback])`. @@ -67,7 +67,7 @@ on this socket. Emitted when an error occurs. -### dgram.send(buf, offset, length, port, address, [callback]) +### socket.send(buf, offset, length, port, address, [callback]) * `buf` Buffer object. Message to be sent * `offset` Integer. Offset in the buffer where the message starts. @@ -123,7 +123,7 @@ a packet might travel, and that generally sending a datagram greater than the (receiver) `MTU` won't work (the packet gets silently dropped, without informing the source that the data did not reach its intended recipient). -### dgram.bind(port, [address], [callback]) +### socket.bind(port, [address], [callback]) * `port` Integer * `address` String, Optional @@ -156,23 +156,23 @@ Example of a UDP server listening on port 41234: // server listening 0.0.0.0:41234 -### dgram.close() +### socket.close() Close the underlying socket and stop listening for data on it. -### dgram.address() +### socket.address() Returns an object containing the address information for a socket. For UDP sockets, this object will contain `address` , `family` and `port`. -### dgram.setBroadcast(flag) +### socket.setBroadcast(flag) * `flag` Boolean Sets or clears the `SO_BROADCAST` socket option. When this option is set, UDP packets may be sent to a local interface's broadcast address. -### dgram.setTTL(ttl) +### socket.setTTL(ttl) * `ttl` Integer @@ -185,7 +185,7 @@ probes or when multicasting. The argument to `setTTL()` is a number of hops between 1 and 255. The default on most systems is 64. -### dgram.setMulticastTTL(ttl) +### socket.setMulticastTTL(ttl) * `ttl` Integer @@ -197,14 +197,14 @@ decrements the TTL. If the TTL is decremented to 0 by a router, it will not be f The argument to `setMulticastTTL()` is a number of hops between 0 and 255. The default on most systems is 1. -### dgram.setMulticastLoopback(flag) +### socket.setMulticastLoopback(flag) * `flag` Boolean Sets or clears the `IP_MULTICAST_LOOP` socket option. When this option is set, multicast packets will also be received on the local interface. -### dgram.addMembership(multicastAddress, [multicastInterface]) +### socket.addMembership(multicastAddress, [multicastInterface]) * `multicastAddress` String * `multicastInterface` String, Optional @@ -214,7 +214,7 @@ Tells the kernel to join a multicast group with `IP_ADD_MEMBERSHIP` socket optio If `multicastInterface` is not specified, the OS will try to add membership to all valid interfaces. -### dgram.dropMembership(multicastAddress, [multicastInterface]) +### socket.dropMembership(multicastAddress, [multicastInterface]) * `multicastAddress` String * `multicastInterface` String, Optional @@ -227,13 +227,13 @@ this. If `multicastInterface` is not specified, the OS will try to drop membership to all valid interfaces. -### dgram.unref() +### socket.unref() Calling `unref` on a socket will allow the program to exit if this is the only active socket in the event system. If the socket is already `unref`d calling `unref` again will have no effect. -### dgram.ref() +### socket.ref() Opposite of `unref`, calling `ref` on a previously `unref`d socket will *not* let the program exit if it's the only socket left (the default behavior). If From e99dff461772ea16586448667860edd13b4190e4 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 14 Mar 2013 13:40:27 +0100 Subject: [PATCH 02/30] deps: upgrade libuv to 7b66ea1 --- deps/uv/include/uv.h | 6 +++--- deps/uv/src/unix/tcp.c | 8 ++++---- deps/uv/src/unix/tty.c | 43 +++++++++++++++++++++++++++++++-------- deps/uv/test/runner-win.c | 30 +++++++++++++++++++++++++-- lib/net.js | 1 + src/node.js | 2 ++ src/tty_wrap.cc | 6 ++++++ 7 files changed, 79 insertions(+), 17 deletions(-) diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index de375d4b0ee..ea11a13bfd7 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -47,7 +47,7 @@ extern "C" { #define UV_VERSION_MAJOR 0 -#define UV_VERSION_MINOR 9 +#define UV_VERSION_MINOR 10 #if defined(_MSC_VER) && _MSC_VER < 1600 @@ -660,12 +660,12 @@ UV_EXTERN int uv_tcp_keepalive(uv_tcp_t* handle, unsigned int delay); /* - * This setting applies to Windows only. * Enable/disable simultaneous asynchronous accept requests that are * queued by the operating system when listening for new tcp connections. * This setting is used to tune a tcp server for the desired performance. * Having simultaneous accepts can significantly improve the rate of - * accepting connections (which is why it is enabled by default). + * accepting connections (which is why it is enabled by default) but + * may lead to uneven load distribution in multi-process setups. */ UV_EXTERN int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable); diff --git a/deps/uv/src/unix/tcp.c b/deps/uv/src/unix/tcp.c index a51576ba1bb..26ab53dbb84 100644 --- a/deps/uv/src/unix/tcp.c +++ b/deps/uv/src/unix/tcp.c @@ -343,11 +343,11 @@ int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) { } -int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int on) { - if (on) - handle->flags |= UV_TCP_SINGLE_ACCEPT; - else +int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { + if (enable) handle->flags &= ~UV_TCP_SINGLE_ACCEPT; + else + handle->flags |= UV_TCP_SINGLE_ACCEPT; return 0; } diff --git a/deps/uv/src/unix/tty.c b/deps/uv/src/unix/tty.c index 49efee7f53f..df32e67345d 100644 --- a/deps/uv/src/unix/tty.c +++ b/deps/uv/src/unix/tty.c @@ -118,25 +118,52 @@ int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { uv_handle_type uv_guess_handle(uv_file file) { + struct sockaddr sa; struct stat s; + socklen_t len; + int type; - if (file < 0) { + if (file < 0) return UV_UNKNOWN_HANDLE; - } - if (isatty(file)) { + if (isatty(file)) return UV_TTY; - } - if (fstat(file, &s)) { + if (fstat(file, &s)) return UV_UNKNOWN_HANDLE; - } - if (!S_ISSOCK(s.st_mode) && !S_ISFIFO(s.st_mode)) { + if (S_ISREG(s.st_mode)) return UV_FILE; + + if (S_ISCHR(s.st_mode)) + return UV_FILE; /* XXX UV_NAMED_PIPE? */ + + if (S_ISFIFO(s.st_mode)) + return UV_NAMED_PIPE; + + if (!S_ISSOCK(s.st_mode)) + return UV_UNKNOWN_HANDLE; + + len = sizeof(type); + if (getsockopt(file, SOL_SOCKET, SO_TYPE, &type, &len)) + return UV_UNKNOWN_HANDLE; + + len = sizeof(sa); + if (getsockname(file, &sa, &len)) + return UV_UNKNOWN_HANDLE; + + if (type == SOCK_DGRAM) + if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6) + return UV_UDP; + + if (type == SOCK_STREAM) { + if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6) + return UV_TCP; + if (sa.sa_family == AF_UNIX) + return UV_NAMED_PIPE; } - return UV_NAMED_PIPE; + return UV_UNKNOWN_HANDLE; } diff --git a/deps/uv/test/runner-win.c b/deps/uv/test/runner-win.c index 8f534bcdb7c..3aae1c3e9cb 100644 --- a/deps/uv/test/runner-win.c +++ b/deps/uv/test/runner-win.c @@ -44,6 +44,11 @@ /* Do platform-specific initialization. */ void platform_init(int argc, char **argv) { + const char* tap; + + tap = getenv("UV_TAP_OUTPUT"); + tap_output = (tap != NULL && atoi(tap) > 0); + /* Disable the "application crashed" popup. */ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); @@ -207,13 +212,34 @@ long int process_output_size(process_info_t *p) { int process_copy_output(process_info_t *p, int fd) { DWORD read; char buf[1024]; + char *line, *start; if (SetFilePointer(p->stdio_out, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return -1; + if (tap_output) + write(fd, "#", 1); + while (ReadFile(p->stdio_out, (void*)&buf, sizeof(buf), &read, NULL) && - read > 0) - write(fd, buf, read); + read > 0) { + if (tap_output) { + start = buf; + + while ((line = strchr(start, '\n')) != NULL) { + write(fd, start, line - start + 1); + write(fd, "#", 1); + start = line + 1; + } + + if (start < buf + read) + write(fd, start, buf + read - start); + } else { + write(fd, buf, read); + } + } + + if (tap_output) + write(fd, "\n", 1); if (GetLastError() != ERROR_HANDLE_EOF) return -1; diff --git a/lib/net.js b/lib/net.js index 9541e1beea5..c58af2a5667 100644 --- a/lib/net.js +++ b/lib/net.js @@ -934,6 +934,7 @@ var createServerHandle = exports._createServerHandle = var type = tty_wrap.guessHandleType(fd); switch (type) { case 'PIPE': + case 'TCP': debug('listen pipe fd=' + fd); // create a PipeWrap handle = createPipe(); diff --git a/src/node.js b/src/node.js index 49aedaee00a..86e9df6a878 100644 --- a/src/node.js +++ b/src/node.js @@ -569,6 +569,7 @@ break; case 'PIPE': + case 'TCP': var net = NativeModule.require('net'); stream = new net.Socket({ fd: fd, @@ -654,6 +655,7 @@ break; case 'PIPE': + case 'TCP': var net = NativeModule.require('net'); stdin = new net.Socket({ fd: fd, diff --git a/src/tty_wrap.cc b/src/tty_wrap.cc index 4be53c8e7b1..132b3071d05 100644 --- a/src/tty_wrap.cc +++ b/src/tty_wrap.cc @@ -105,9 +105,15 @@ Handle TTYWrap::GuessHandleType(const Arguments& args) { uv_handle_type t = uv_guess_handle(fd); switch (t) { + case UV_TCP: + return scope.Close(String::New("TCP")); + case UV_TTY: return scope.Close(String::New("TTY")); + case UV_UDP: + return scope.Close(String::New("UDP")); + case UV_NAMED_PIPE: return scope.Close(String::New("PIPE")); From ca5022b8f10c95d834678108adcd12f1c907016e Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 14 Mar 2013 15:13:58 +0100 Subject: [PATCH 03/30] net: improve arbitrary tcp socket support Consider this example: // fd 3 is a bound tcp socket var s = net.createServer(cb); s.listen({ fd: 3 }); console.log(s.address()); // prints null This commit makes net.Server#address() print the actual address. Ditto for non-listen sockets; properties like net.Socket#localAddress and net.Socket#remoteAddress now return the correct value. Fixes #5009. --- lib/net.js | 41 +++++++++++++++++++++-------------------- src/tcp_wrap.cc | 10 ++++++++++ 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/lib/net.js b/lib/net.js index c58af2a5667..6174b40eac5 100644 --- a/lib/net.js +++ b/lib/net.js @@ -42,6 +42,15 @@ function createTCP() { } +function createHandle(fd) { + var tty = process.binding('tty_wrap'); + var type = tty.guessHandleType(fd); + if (type === 'PIPE') return createPipe(); + if (type === 'TCP') return createTCP(); + throw new TypeError('Unsupported fd type: ' + type); +} + + var debug; if (process.env.NODE_DEBUG && /net/.test(process.env.NODE_DEBUG)) { var pid = process.pid; @@ -144,7 +153,7 @@ function Socket(options) { if (options.handle) { this._handle = options.handle; // private } else if (typeof options.fd !== 'undefined') { - this._handle = createPipe(); + this._handle = createHandle(options.fd); this._handle.open(options.fd); this.readable = options.readable !== false; this.writable = options.writable !== false; @@ -930,26 +939,18 @@ var createServerHandle = exports._createServerHandle = var handle; if (typeof fd === 'number' && fd >= 0) { - var tty_wrap = process.binding('tty_wrap'); - var type = tty_wrap.guessHandleType(fd); - switch (type) { - case 'PIPE': - case 'TCP': - debug('listen pipe fd=' + fd); - // create a PipeWrap - handle = createPipe(); - handle.open(fd); - handle.readable = true; - handle.writable = true; - break; - - default: - // Not a fd we can listen on. This will trigger an error. - debug('listen invalid fd=' + fd + ' type=' + type); - process._errno = 'EINVAL'; // hack, callers expect that errno is set - handle = null; - break; + try { + handle = createHandle(fd); } + catch (e) { + // Not a fd we can listen on. This will trigger an error. + debug('listen invalid fd=' + fd + ': ' + e.message); + process._errno = 'EINVAL'; // hack, callers expect that errno is set + return null; + } + handle.open(fd); + handle.readable = true; + handle.writable = true; return handle; } else if (port == -1 && addressType == -1) { diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index 4c55f2eff50..b48f8acd701 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -104,6 +104,7 @@ void TCPWrap::Initialize(Handle target) { NODE_SET_PROTOTYPE_METHOD(t, "writeUtf8String", StreamWrap::WriteUtf8String); NODE_SET_PROTOTYPE_METHOD(t, "writeUcs2String", StreamWrap::WriteUcs2String); + NODE_SET_PROTOTYPE_METHOD(t, "open", Open); NODE_SET_PROTOTYPE_METHOD(t, "bind", Bind); NODE_SET_PROTOTYPE_METHOD(t, "listen", Listen); NODE_SET_PROTOTYPE_METHOD(t, "connect", Connect); @@ -256,6 +257,15 @@ Handle TCPWrap::SetSimultaneousAccepts(const Arguments& args) { #endif +Handle TCPWrap::Open(const Arguments& args) { + HandleScope scope; + UNWRAP(TCPWrap) + int fd = args[0]->IntegerValue(); + uv_tcp_open(&wrap->handle_, fd); + return Null(); +} + + Handle TCPWrap::Bind(const Arguments& args) { HandleScope scope; From d62cf59dc152f8df6954ee7ffe9381fc9b524e32 Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 14 Mar 2013 07:48:18 -0700 Subject: [PATCH 04/30] http: Don't hot-path end() for large buffers The benefits of the hot-path optimization below start to fall off when the buffer size gets up near 128KB, because the cost of the copy is more than the cost of the extra write() call. Switch to the write/end method at that point. Heuristics and magic numbers are awful, but slow http responses are worse. Fix #4975 --- benchmark/http/end-vs-write-end.js | 59 ++++++++++++++++++++++++++++++ lib/http.js | 8 ++++ 2 files changed, 67 insertions(+) create mode 100644 benchmark/http/end-vs-write-end.js diff --git a/benchmark/http/end-vs-write-end.js b/benchmark/http/end-vs-write-end.js new file mode 100644 index 00000000000..06fce6f4686 --- /dev/null +++ b/benchmark/http/end-vs-write-end.js @@ -0,0 +1,59 @@ +// When calling .end(buffer) right away, this triggers a "hot path" +// optimization in http.js, to avoid an extra write call. +// +// However, the overhead of copying a large buffer is higher than +// the overhead of an extra write() call, so the hot path was not +// always as hot as it could be. +// +// Verify that our assumptions are valid. + +var common = require('../common.js'); +var PORT = common.PORT; + +var bench = common.createBenchmark(main, { + type: ['asc', 'utf', 'buf'], + kb: [64, 128, 256, 1024], + c: [100], + method: ['write', 'end '] // two spaces added to line up each row +}); + +function main(conf) { + http = require('http'); + var chunk; + var len = conf.kb * 1024; + switch (conf.type) { + case 'buf': + chunk = new Buffer(len); + chunk.fill('x'); + break; + case 'utf': + encoding = 'utf8'; + chunk = new Array(len / 2 + 1).join('ü'); + break; + case 'asc': + chunk = new Array(len + 1).join('a'); + break; + } + + function write(res) { + res.write(chunk); + res.end(); + } + + function end(res) { + res.end(chunk); + } + + var method = conf.method === 'write' ? write : end; + var args = ['-r', 5000, '-t', 8, '-c', conf.c]; + + var server = http.createServer(function(req, res) { + method(res); + }); + + server.listen(common.PORT, function() { + bench.http('/', args, function() { + server.close(); + }); + }); +} diff --git a/lib/http.js b/lib/http.js index 2bceaa01527..dacedd4477f 100644 --- a/lib/http.js +++ b/lib/http.js @@ -855,6 +855,14 @@ OutgoingMessage.prototype.end = function(data, encoding) { this.connection.writable && this.connection._httpMessage === this; + // The benefits of the hot-path optimization below start to fall + // off when the buffer size gets up near 128KB, because the cost + // of the copy is more than the cost of the extra write() call. + // Switch to the write/end method at that point. Heuristics and + // magic numbers are awful, but slow http responses are worse. + if (hot && Buffer.isBuffer(data) && data.length > 120 * 1024) + hot = false; + if (hot) { // Hot path. They're doing // res.writeHead(); From 1f53cfdeae5549a0ecacbd8cd89d60d4faf17768 Mon Sep 17 00:00:00 2001 From: koichik Date: Thu, 14 Mar 2013 23:57:13 +0900 Subject: [PATCH 05/30] doc: don't mark fs callbacks as optional Refs #5005, #5008 --- doc/api/fs.markdown | 63 ++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/doc/api/fs.markdown b/doc/api/fs.markdown index e7a562d17a7..6730b7c9fc0 100644 --- a/doc/api/fs.markdown +++ b/doc/api/fs.markdown @@ -70,7 +70,7 @@ warning. errors as exceptions. -## fs.rename(oldPath, newPath, [callback]) +## fs.rename(oldPath, newPath, callback) Asynchronous rename(2). No arguments other than a possible exception are given to the completion callback. @@ -79,7 +79,7 @@ to the completion callback. Synchronous rename(2). -## fs.ftruncate(fd, len, [callback]) +## fs.ftruncate(fd, len, callback) Asynchronous ftruncate(2). No arguments other than a possible exception are given to the completion callback. @@ -88,7 +88,7 @@ given to the completion callback. Synchronous ftruncate(2). -## fs.truncate(path, len, [callback]) +## fs.truncate(path, len, callback) Asynchronous truncate(2). No arguments other than a possible exception are given to the completion callback. @@ -97,7 +97,7 @@ given to the completion callback. Synchronous truncate(2). -## fs.chown(path, uid, gid, [callback]) +## fs.chown(path, uid, gid, callback) Asynchronous chown(2). No arguments other than a possible exception are given to the completion callback. @@ -106,7 +106,7 @@ to the completion callback. Synchronous chown(2). -## fs.fchown(fd, uid, gid, [callback]) +## fs.fchown(fd, uid, gid, callback) Asynchronous fchown(2). No arguments other than a possible exception are given to the completion callback. @@ -115,7 +115,7 @@ to the completion callback. Synchronous fchown(2). -## fs.lchown(path, uid, gid, [callback]) +## fs.lchown(path, uid, gid, callback) Asynchronous lchown(2). No arguments other than a possible exception are given to the completion callback. @@ -124,7 +124,7 @@ to the completion callback. Synchronous lchown(2). -## fs.chmod(path, mode, [callback]) +## fs.chmod(path, mode, callback) Asynchronous chmod(2). No arguments other than a possible exception are given to the completion callback. @@ -133,7 +133,7 @@ to the completion callback. Synchronous chmod(2). -## fs.fchmod(fd, mode, [callback]) +## fs.fchmod(fd, mode, callback) Asynchronous fchmod(2). No arguments other than a possible exception are given to the completion callback. @@ -142,7 +142,7 @@ are given to the completion callback. Synchronous fchmod(2). -## fs.lchmod(path, mode, [callback]) +## fs.lchmod(path, mode, callback) Asynchronous lchmod(2). No arguments other than a possible exception are given to the completion callback. @@ -153,20 +153,20 @@ Only available on Mac OS X. Synchronous lchmod(2). -## fs.stat(path, [callback]) +## fs.stat(path, callback) Asynchronous stat(2). The callback gets two arguments `(err, stats)` where `stats` is a [fs.Stats](#fs_class_fs_stats) object. See the [fs.Stats](#fs_class_fs_stats) section below for more information. -## fs.lstat(path, [callback]) +## fs.lstat(path, callback) Asynchronous lstat(2). The callback gets two arguments `(err, stats)` where `stats` is a `fs.Stats` object. `lstat()` is identical to `stat()`, except that if `path` is a symbolic link, then the link itself is stat-ed, not the file that it refers to. -## fs.fstat(fd, [callback]) +## fs.fstat(fd, callback) Asynchronous fstat(2). The callback gets two arguments `(err, stats)` where `stats` is a `fs.Stats` object. `fstat()` is identical to `stat()`, except that @@ -184,7 +184,7 @@ Synchronous lstat(2). Returns an instance of `fs.Stats`. Synchronous fstat(2). Returns an instance of `fs.Stats`. -## fs.link(srcpath, dstpath, [callback]) +## fs.link(srcpath, dstpath, callback) Asynchronous link(2). No arguments other than a possible exception are given to the completion callback. @@ -193,7 +193,7 @@ the completion callback. Synchronous link(2). -## fs.symlink(srcpath, dstpath, [type], [callback]) +## fs.symlink(srcpath, dstpath, [type], callback) Asynchronous symlink(2). No arguments other than a possible exception are given to the completion callback. @@ -206,7 +206,7 @@ Note that Windows junction points require the destination path to be absolute. Synchronous symlink(2). -## fs.readlink(path, [callback]) +## fs.readlink(path, callback) Asynchronous readlink(2). The callback gets two arguments `(err, linkString)`. @@ -234,7 +234,7 @@ Example: Synchronous realpath(2). Returns the resolved path. -## fs.unlink(path, [callback]) +## fs.unlink(path, callback) Asynchronous unlink(2). No arguments other than a possible exception are given to the completion callback. @@ -243,7 +243,7 @@ to the completion callback. Synchronous unlink(2). -## fs.rmdir(path, [callback]) +## fs.rmdir(path, callback) Asynchronous rmdir(2). No arguments other than a possible exception are given to the completion callback. @@ -252,7 +252,7 @@ to the completion callback. Synchronous rmdir(2). -## fs.mkdir(path, [mode], [callback]) +## fs.mkdir(path, [mode], callback) Asynchronous mkdir(2). No arguments other than a possible exception are given to the completion callback. `mode` defaults to `0777`. @@ -261,7 +261,7 @@ to the completion callback. `mode` defaults to `0777`. Synchronous mkdir(2). -## fs.readdir(path, [callback]) +## fs.readdir(path, callback) Asynchronous readdir(3). Reads the contents of a directory. The callback gets two arguments `(err, files)` where `files` is an array of @@ -272,7 +272,7 @@ the names of the files in the directory excluding `'.'` and `'..'`. Synchronous readdir(3). Returns an array of filenames excluding `'.'` and `'..'`. -## fs.close(fd, [callback]) +## fs.close(fd, callback) Asynchronous close(2). No arguments other than a possible exception are given to the completion callback. @@ -281,7 +281,7 @@ to the completion callback. Synchronous close(2). -## fs.open(path, flags, [mode], [callback]) +## fs.open(path, flags, [mode], callback) Asynchronous file open. See open(2). `flags` can be: @@ -334,18 +334,18 @@ not followed. Exclusive mode may or may not work with network file systems. Synchronous open(2). -## fs.utimes(path, atime, mtime, [callback]) +## fs.utimes(path, atime, mtime, callback) ## fs.utimesSync(path, atime, mtime) Change file timestamps of the file referenced by the supplied path. -## fs.futimes(fd, atime, mtime, [callback]) +## fs.futimes(fd, atime, mtime, callback) ## fs.futimesSync(fd, atime, mtime) Change the file timestamps of a file referenced by the supplied file descriptor. -## fs.fsync(fd, [callback]) +## fs.fsync(fd, callback) Asynchronous fsync(2). No arguments other than a possible exception are given to the completion callback. @@ -354,7 +354,7 @@ to the completion callback. Synchronous fsync(2). -## fs.write(fd, buffer, offset, length, position, [callback]) +## fs.write(fd, buffer, offset, length, position, callback) Write `buffer` to the file specified by `fd`. @@ -376,7 +376,7 @@ without waiting for the callback. For this scenario, Synchronous version of `fs.write()`. Returns the number of bytes written. -## fs.read(fd, buffer, offset, length, position, [callback]) +## fs.read(fd, buffer, offset, length, position, callback) Read data from the file specified by `fd`. @@ -395,12 +395,13 @@ The callback is given the three arguments, `(err, bytesRead, buffer)`. Synchronous version of `fs.read`. Returns the number of `bytesRead`. -## fs.readFile(filename, [options], [callback]) +## fs.readFile(filename, [options], callback) * `filename` {String} * `options` {Object} * `encoding` {String | Null} default = `null` * `flag` {String} default = `'r'` +* `callback` {Function} Asynchronously reads the entire contents of a file. Example: @@ -423,7 +424,7 @@ If the `encoding` option is specified then this function returns a string. Otherwise it returns a buffer. -## fs.writeFile(filename, data, [options], [callback]) +## fs.writeFile(filename, data, [options], callback) * `filename` {String} * `data` {String | Buffer} @@ -431,6 +432,7 @@ string. Otherwise it returns a buffer. * `encoding` {String | Null} default = `'utf8'` * `mode` {Number} default = `438` (aka `0666` in Octal) * `flag` {String} default = `'w'` +* `callback` {Function} Asynchronously writes data to a file, replacing the file if it already exists. `data` can be a string or a buffer. @@ -449,7 +451,7 @@ Example: The synchronous version of `fs.writeFile`. -## fs.appendFile(filename, data, [options], [callback]) +## fs.appendFile(filename, data, [options], callback) * `filename` {String} * `data` {String | Buffer} @@ -457,6 +459,7 @@ The synchronous version of `fs.writeFile`. * `encoding` {String | Null} default = `'utf8'` * `mode` {Number} default = `438` (aka `0666` in Octal) * `flag` {String} default = `'a'` +* `callback` {Function} Asynchronously append data to a file, creating the file if it not yet exists. `data` can be a string or a buffer. @@ -571,7 +574,7 @@ callback, and have some fallback logic if it is null. } }); -## fs.exists(path, [callback]) +## fs.exists(path, callback) Test whether or not the given path exists by checking with the file system. Then call the `callback` argument with either true or false. Example: From e8f80bf479d79bcd3c8bff7fba70aea483e66449 Mon Sep 17 00:00:00 2001 From: Gil Pedersen Date: Thu, 14 Mar 2013 14:01:14 +0100 Subject: [PATCH 06/30] stream: Never call decoder.end() multiple times Updated version that does what it says without assigning state.decoder. --- lib/_stream_readable.js | 8 +-- test/simple/test-stream2-set-encoding.js | 73 ++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index b09694c0715..ad0a783985a 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -342,14 +342,14 @@ function chunkInvalid(state, chunk) { function onEofChunk(stream, state) { - state.ended = true; - if (state.decoder) { + if (state.decoder && !state.ended) { var chunk = state.decoder.end(); if (chunk && chunk.length) { state.buffer.push(chunk); state.length += state.objectMode ? 1 : chunk.length; } } + state.ended = true; // if we've ended and we have some data left, then emit // 'readable' now to make sure it gets picked up. @@ -733,12 +733,12 @@ Readable.prototype.wrap = function(stream) { var self = this; stream.on('end', function() { - state.ended = true; - if (state.decoder) { + if (state.decoder && !state.ended) { var chunk = state.decoder.end(); if (chunk && chunk.length) self.push(chunk); } + state.ended = true; self.push(null); }); diff --git a/test/simple/test-stream2-set-encoding.js b/test/simple/test-stream2-set-encoding.js index 8c5973fb637..931cf48f51e 100644 --- a/test/simple/test-stream2-set-encoding.js +++ b/test/simple/test-stream2-set-encoding.js @@ -74,11 +74,15 @@ TestReader.prototype._read = function(n) { setTimeout(function() { if (this.pos >= this.len) { + // double push(null) to test eos handling + this.push(null); return this.push(null); } n = Math.min(n, this.len - this.pos); if (n <= 0) { + // double push(null) to test eos handling + this.push(null); return this.push(null); } @@ -204,6 +208,41 @@ test('setEncoding hex with read(13)', function(t) { tr.emit('readable'); }); +test('setEncoding base64', function(t) { + var tr = new TestReader(100); + tr.setEncoding('base64'); + var out = []; + var expect = + [ 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYQ==' ]; + + tr.on('readable', function flow() { + var chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', function() { + t.same(out, expect); + t.end(); + }); + + // just kick it off. + tr.emit('readable'); +}); + test('encoding: utf8', function(t) { var tr = new TestReader(100, { encoding: 'utf8' }); var out = []; @@ -310,3 +349,37 @@ test('encoding: hex with read(13)', function(t) { // just kick it off. tr.emit('readable'); }); + +test('encoding: base64', function(t) { + var tr = new TestReader(100, { encoding: 'base64' }); + var out = []; + var expect = + [ 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYWFhYWFh', + 'YWFhYWFhYW', + 'FhYQ==' ]; + + tr.on('readable', function flow() { + var chunk; + while (null !== (chunk = tr.read(10))) + out.push(chunk); + }); + + tr.on('end', function() { + t.same(out, expect); + t.end(); + }); + + // just kick it off. + tr.emit('readable'); +}); From 3537b57f3e6cdf385f0df44dd2a03f1a1ee06b09 Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 14 Mar 2013 16:18:42 -0700 Subject: [PATCH 07/30] test: No need for kicking in streams2 test This was necessary when we weren't auto-starting when a 'readable' listener is added. --- test/simple/test-stream2-set-encoding.js | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/test/simple/test-stream2-set-encoding.js b/test/simple/test-stream2-set-encoding.js index 931cf48f51e..5d2c32a2d0e 100644 --- a/test/simple/test-stream2-set-encoding.js +++ b/test/simple/test-stream2-set-encoding.js @@ -122,9 +122,6 @@ test('setEncoding utf8', function(t) { t.same(out, expect); t.end(); }); - - // just kick it off. - tr.emit('readable'); }); @@ -164,9 +161,6 @@ test('setEncoding hex', function(t) { t.same(out, expect); t.end(); }); - - // just kick it off. - tr.emit('readable'); }); test('setEncoding hex with read(13)', function(t) { @@ -203,9 +197,6 @@ test('setEncoding hex with read(13)', function(t) { t.same(out, expect); t.end(); }); - - // just kick it off. - tr.emit('readable'); }); test('setEncoding base64', function(t) { @@ -238,9 +229,6 @@ test('setEncoding base64', function(t) { t.same(out, expect); t.end(); }); - - // just kick it off. - tr.emit('readable'); }); test('encoding: utf8', function(t) { @@ -268,9 +256,6 @@ test('encoding: utf8', function(t) { t.same(out, expect); t.end(); }); - - // just kick it off. - tr.emit('readable'); }); @@ -309,9 +294,6 @@ test('encoding: hex', function(t) { t.same(out, expect); t.end(); }); - - // just kick it off. - tr.emit('readable'); }); test('encoding: hex with read(13)', function(t) { @@ -345,9 +327,6 @@ test('encoding: hex with read(13)', function(t) { t.same(out, expect); t.end(); }); - - // just kick it off. - tr.emit('readable'); }); test('encoding: base64', function(t) { @@ -379,7 +358,4 @@ test('encoding: base64', function(t) { t.same(out, expect); t.end(); }); - - // just kick it off. - tr.emit('readable'); }); From 14947b6c5e12ec65e6df75d4db5bcf47f7b29819 Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 14 Mar 2013 16:43:19 -0700 Subject: [PATCH 08/30] stream: Return self from readable.wrap Also, set paused=false *before* calling resume(). Otherwise, there's an edge case where an immediately-emitted chunk might make it call pause() again incorrectly. --- lib/_stream_readable.js | 4 +- test/simple/test-stream2-readable-wrap.js | 94 +++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 test/simple/test-stream2-readable-wrap.js diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index ad0a783985a..b81f2a82a13 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -777,10 +777,12 @@ Readable.prototype.wrap = function(stream) { // underlying stream. self._read = function(n) { if (paused) { - stream.resume(); paused = false; + stream.resume(); } }; + + return self; }; diff --git a/test/simple/test-stream2-readable-wrap.js b/test/simple/test-stream2-readable-wrap.js new file mode 100644 index 00000000000..5fa5d185b55 --- /dev/null +++ b/test/simple/test-stream2-readable-wrap.js @@ -0,0 +1,94 @@ +// 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 Readable = require('_stream_readable'); +var Writable = require('_stream_writable'); +var EE = require('events').EventEmitter; + +var old = new EE; +var r = new Readable({ highWaterMark: 10 }); +assert.equal(r, r.wrap(old)); + +var ended = false; +r.on('end', function() { + ended = true; +}); + +var pauses = 0; +var resumes = 0; + +old.pause = function() { + pauses++; + old.emit('pause'); + flowing = false; +}; + +old.resume = function() { + resumes++; + old.emit('resume'); + flow(); +}; + +var flowing; +var chunks = 10; +var oldEnded = false; +function flow() { + flowing = true; + while (flowing && chunks-- > 0) { + old.emit('data', new Buffer('xxxxxxxxxx')); + } + if (chunks <= 0) { + oldEnded = true; + old.emit('end'); + } +} + +var w = new Writable({ highWaterMark: 20 }); +var written = []; +w._write = function(chunk, encoding, cb) { + written.push(chunk.toString()); + setTimeout(cb); +}; + +var finished = false; +w.on('finish', function() { + finished = true; +}); + + +var expect = new Array(11).join('xxxxxxxxxx'); + +r.pipe(w); + +flow(); + +process.on('exit', function() { + assert.equal(pauses, 10); + assert.equal(resumes, 9); + assert(ended); + assert(finished); + assert(oldEnded); + assert.equal(written.join(''), expect); + console.log('ok'); +}); From 4432dc81879eddb39aee6aee19c525f53e2f4250 Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Thu, 14 Mar 2013 16:06:59 -0700 Subject: [PATCH 09/30] v8: move 32 bit heap hint on sunos Setting the V8 heap at or near 0x20000000 on 32bit sunos only allows 512 MB of heap space, instead on sunos move this to 0x80000000. Fixes #4010. --- deps/v8/src/platform-posix.cc | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/deps/v8/src/platform-posix.cc b/deps/v8/src/platform-posix.cc index 2b80015161e..ad74eba8d99 100644 --- a/deps/v8/src/platform-posix.cc +++ b/deps/v8/src/platform-posix.cc @@ -110,19 +110,25 @@ void* OS::GetRandomMmapAddr() { #else uint32_t raw_addr = V8::RandomPrivate(isolate); - // For our 32-bit mmap() hint, we pick a random address in the bottom + raw_addr &= 0x3ffff000; + +# ifdef __sun + // For our Solaris/illumos mmap hint, we pick a random address in the bottom // half of the top half of the address space (that is, the third quarter). // Because we do not MAP_FIXED, this will be treated only as a hint -- the // system will not fail to mmap() because something else happens to already // be mapped at our random address. We deliberately set the hint high enough - // to get well above the system's break (that is, the heap); systems will - // either try the hint and if that fails move higher (MacOS and other BSD - // derivatives) or try the hint and if that fails allocate as if there were - // no hint at all (Linux, Solaris, illumos and derivatives). The high hint - // prevents the break from getting hemmed in at low values, ceding half of - // the address space to the system heap. - raw_addr &= 0x3ffff000; + // to get well above the system's break (that is, the heap); Solaris and + // illumos will try the hint and if that fails allocate as if there were + // no hint at all. The high hint prevents the break from getting hemmed in + // at low values, ceding half of the address space to the system heap. raw_addr += 0x80000000; +# else + // The range 0x20000000 - 0x60000000 is relatively unpopulated across a + // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macos + // 10.6 and 10.7. + raw_addr += 0x20000000; +# endif #endif return reinterpret_cast(raw_addr); } From 2b5bc8e0d60209679d6b17de97e1f8ef29434aa4 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 16 Mar 2013 23:18:50 +0100 Subject: [PATCH 10/30] install: don't install man page twice Looks like a merge conflict in 77ed12f left in the old, unconditional install rule. Remove it, the new and improved rule is a few lines down. Fixes #5044. --- tools/install.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/install.py b/tools/install.py index c8ad828c1de..48943de91ba 100755 --- a/tools/install.py +++ b/tools/install.py @@ -119,7 +119,6 @@ def npm_files(action): assert(0) # unhandled action type def files(action): - action(['doc/node.1'], 'share/man/man1/') action(['out/Release/node'], 'bin/node') # install unconditionally, checking if the platform supports dtrace doesn't From f5a337e09ca971b5d9675aaef4cc3afe66e7196c Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 16 Mar 2013 23:32:42 +0100 Subject: [PATCH 11/30] deps: upgrade libuv to b45a74f --- deps/uv/src/unix/internal.h | 3 +++ deps/uv/src/unix/pipe.c | 5 +++++ deps/uv/src/unix/stream.c | 22 ++++------------------ deps/uv/src/unix/tty.c | 5 +++++ 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index 35b3b8caa8e..4053d424309 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -148,6 +148,9 @@ void uv__stream_init(uv_loop_t* loop, uv_stream_t* stream, uv_handle_type type); int uv__stream_open(uv_stream_t*, int fd, int flags); void uv__stream_destroy(uv_stream_t* stream); +#if defined(__APPLE__) +int uv__stream_try_select(uv_stream_t* stream, int* fd); +#endif /* defined(__APPLE__) */ void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); int uv__accept(int sockfd); diff --git a/deps/uv/src/unix/pipe.c b/deps/uv/src/unix/pipe.c index 1185b91a1b4..4b7f966bcbd 100644 --- a/deps/uv/src/unix/pipe.c +++ b/deps/uv/src/unix/pipe.c @@ -155,6 +155,11 @@ void uv__pipe_close(uv_pipe_t* handle) { int uv_pipe_open(uv_pipe_t* handle, uv_file fd) { +#if defined(__APPLE__) + if (uv__stream_try_select((uv_stream_t*) handle, &fd)) + return -1; +#endif /* defined(__APPLE__) */ + return uv__stream_open((uv_stream_t*)handle, fd, UV_STREAM_READABLE | UV_STREAM_WRITABLE); diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index 8137d24eee7..d00fe23cb50 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -268,7 +268,7 @@ static void uv__stream_osx_cb_close(uv_handle_t* async) { } -static int uv__stream_try_select(uv_stream_t* stream, int fd) { +int uv__stream_try_select(uv_stream_t* stream, int* fd) { /* * kqueue doesn't work with some files from /dev mount on osx. * select(2) in separate thread for those fds @@ -288,7 +288,7 @@ static int uv__stream_try_select(uv_stream_t* stream, int fd) { return uv__set_sys_error(stream->loop, errno); } - EV_SET(&filter[0], fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0); + EV_SET(&filter[0], *fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0); /* Use small timeout, because we only want to capture EINVALs */ timeout.tv_sec = 0; @@ -308,7 +308,7 @@ static int uv__stream_try_select(uv_stream_t* stream, int fd) { if (s == NULL) return uv__set_artificial_error(stream->loop, UV_ENOMEM); - s->fd = fd; + s->fd = *fd; if (uv_async_init(stream->loop, &s->async, uv__stream_osx_select_cb)) { SAVE_ERRNO(free(s)); @@ -336,6 +336,7 @@ static int uv__stream_try_select(uv_stream_t* stream, int fd) { s->stream = stream; stream->select = s; + *fd = s->fake_fd; return 0; @@ -368,21 +369,6 @@ int uv__stream_open(uv_stream_t* stream, int fd, int flags) { return uv__set_sys_error(stream->loop, errno); } -#if defined(__APPLE__) - { - uv__stream_select_t* s; - int r; - - r = uv__stream_try_select(stream, fd); - if (r == -1) - return r; - - s = stream->select; - if (s != NULL) - fd = s->fake_fd; - } -#endif /* defined(__APPLE__) */ - stream->io_watcher.fd = fd; return 0; diff --git a/deps/uv/src/unix/tty.c b/deps/uv/src/unix/tty.c index df32e67345d..a2b76add671 100644 --- a/deps/uv/src/unix/tty.c +++ b/deps/uv/src/unix/tty.c @@ -36,6 +36,11 @@ static struct termios orig_termios; int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) { uv__stream_init(loop, (uv_stream_t*)tty, UV_TTY); +#if defined(__APPLE__) + if (uv__stream_try_select((uv_stream_t*) tty, &fd)) + return -1; +#endif /* defined(__APPLE__) */ + if (readable) { uv__nonblock(fd, 1); uv__stream_open((uv_stream_t*)tty, fd, UV_STREAM_READABLE); From 852444a72072dc7bf62e1e4df49a92d766b01268 Mon Sep 17 00:00:00 2001 From: Yi EungJun Date: Sat, 16 Mar 2013 01:31:48 +0900 Subject: [PATCH 12/30] doc: https: Fix the link to tls.connect --- doc/api/https.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/https.markdown b/doc/api/https.markdown index 4ae61e6a7dd..9efe2065ea0 100644 --- a/doc/api/https.markdown +++ b/doc/api/https.markdown @@ -205,5 +205,5 @@ Global instance of [https.Agent][] for all HTTPS client requests. [http.request()]: http.html#http_http_request_options_callback [https.Agent]: #https_class_https_agent [https.request()]: #https_https_request_options_callback -[tls.connect()]: tls.html#tls_tls_connect_options_secureconnectlistener +[tls.connect()]: tls.html#tls_tls_connect_options_callback [tls.createServer()]: tls.html#tls_tls_createserver_options_secureconnectionlistener From f217b5ed62f3f020e8cf346787e9ad5822d81b91 Mon Sep 17 00:00:00 2001 From: JeongHoon Byun Date: Tue, 12 Mar 2013 21:21:43 +0900 Subject: [PATCH 13/30] doc: fix typo in crypto docs --- doc/api/crypto.markdown | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/api/crypto.markdown b/doc/api/crypto.markdown index dfbedca229b..7cc4bdd920e 100644 --- a/doc/api/crypto.markdown +++ b/doc/api/crypto.markdown @@ -110,7 +110,7 @@ Calculates the digest of all of the passed data to be hashed. The `encoding` can be `'hex'`, `'binary'` or `'base64'`. If no encoding is provided, then a buffer is returned. -Note: `hash` object can not be used after `digest()` method been +Note: `hash` object can not be used after `digest()` method has been called. @@ -144,7 +144,7 @@ Calculates the digest of all of the passed data to the hmac. The `encoding` can be `'hex'`, `'binary'` or `'base64'`. If no encoding is provided, then a buffer is returned. -Note: `hmac` object can not be used after `digest()` method been +Note: `hmac` object can not be used after `digest()` method has been called. @@ -194,7 +194,7 @@ encoding is provided, then a buffer is expected. The `output_encoding` specifies the output format of the enciphered data, and can be `'binary'`, `'base64'` or `'hex'`. If no encoding is -provided, then a buffer iis returned. +provided, then a buffer is returned. Returns the enciphered contents, and can be called many times with new data as it is streamed. @@ -205,7 +205,7 @@ Returns any remaining enciphered contents, with `output_encoding` being one of: `'binary'`, `'base64'` or `'hex'`. If no encoding is provided, then a buffer is returned. -Note: `cipher` object can not be used after `final()` method been +Note: `cipher` object can not be used after `final()` method has been called. ### cipher.setAutoPadding(auto_padding=true) @@ -254,7 +254,7 @@ Returns any remaining plaintext which is deciphered, with `output_encoding` being one of: `'binary'`, `'ascii'` or `'utf8'`. If no encoding is provided, then a buffer is returned. -Note: `decipher` object can not be used after `final()` method been +Note: `decipher` object can not be used after `final()` method has been called. ### decipher.setAutoPadding(auto_padding=true) @@ -297,7 +297,7 @@ Returns the signature in `output_format` which can be `'binary'`, `'hex'` or `'base64'`. If no encoding is provided, then a buffer is returned. -Note: `sign` object can not be used after `sign()` method been +Note: `sign` object can not be used after `sign()` method has been called. ## crypto.createVerify(algorithm) @@ -334,7 +334,7 @@ If no encoding is specified, then a buffer is expected. Returns true or false depending on the validity of the signature for the data and public key. -Note: `verifier` object can not be used after `verify()` method been +Note: `verifier` object can not be used after `verify()` method has been called. ## crypto.createDiffieHellman(prime_length) From a0867e1c9395bc9c8af37c290f032bc8dbd847ce Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Sat, 16 Mar 2013 21:59:47 -0700 Subject: [PATCH 14/30] node: revert removal of MakeCallback In 0168109 an implementation of MakeCallback was accidently removed. It has been re-added. --- src/node.cc | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/node.cc b/src/node.cc index e17baec825e..7aba02bd35e 100644 --- a/src/node.cc +++ b/src/node.cc @@ -901,9 +901,11 @@ Handle FromConstructorTemplate(Persistent t, Handle MakeDomainCallback(const Handle object, - const Handle callback, - int argc, - Handle argv[]) { + const Handle callback, + int argc, + Handle argv[]) { + // TODO Hook for long stack traces to be made here. + // lazy load _tickDomainCallback if (process_tickDomainCallback.IsEmpty()) { Local cb_v = process->Get(String::New("_tickDomainCallback")); @@ -980,20 +982,11 @@ MakeDomainCallback(const Handle object, Handle MakeCallback(const Handle object, - const Handle symbol, + const Handle callback, int argc, Handle argv[]) { - HandleScope scope; - - Local callback = object->Get(symbol).As(); - Local domain = object->Get(domain_symbol); - // TODO Hook for long stack traces to be made here. - // has domain, off with you - if (!domain->IsNull() && !domain->IsUndefined()) - return scope.Close(MakeDomainCallback(object, callback, argc, argv)); - // lazy load no domain next tick callbacks if (process_tickCallback.IsEmpty()) { Local cb_v = process->Get(String::New("_tickCallback")); @@ -1017,7 +1010,7 @@ MakeCallback(const Handle object, if (tick_infobox.length == 0) { tick_infobox.index = 0; tick_infobox.depth = 0; - return scope.Close(ret); + return ret; } // process nextTicks after call @@ -1028,7 +1021,24 @@ MakeCallback(const Handle object, return Undefined(); } - return scope.Close(ret); + return ret; +} + + +Handle +MakeCallback(const Handle object, + const Handle symbol, + int argc, + Handle argv[]) { + HandleScope scope; + + Local callback = object->Get(symbol).As(); + Local domain = object->Get(domain_symbol); + + // has domain, off with you + if (!domain->IsNull() && !domain->IsUndefined()) + return scope.Close(MakeDomainCallback(object, callback, argc, argv)); + return scope.Close(MakeCallback(object, callback, argc, argv)); } From b5ddc0cf9658499e04c10b8e3158b81d2be0d5ac Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 15 Mar 2013 22:49:31 +0400 Subject: [PATCH 15/30] tls: write pending data of opposite side Fix stucked CryptoStream behaviour, happening when one of the sides locks-up in queued state. fix #5023 --- lib/tls.js | 1 + test/simple/test-tls-server-large-request.js | 74 ++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 test/simple/test-tls-server-large-request.js diff --git a/lib/tls.js b/lib/tls.js index 515761410b0..ab80fb18175 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -435,6 +435,7 @@ CryptoStream.prototype._read = function read(size) { // Try writing pending data if (this._pending !== null) this._writePending(); + if (this._opposite._pending !== null) this._opposite._writePending(); if (bytesRead === 0) { // EOF when cleartext has finished and we have nothing to read diff --git a/test/simple/test-tls-server-large-request.js b/test/simple/test-tls-server-large-request.js new file mode 100644 index 00000000000..9928e8547ed --- /dev/null +++ b/test/simple/test-tls-server-large-request.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. + +var common = require('../common'); +var assert = require('assert'); +var tls = require('tls'); +var fs = require('fs'); +var stream = require('stream'); +var util = require('util'); + +var clientConnected = 0; +var serverConnected = 0; +var request = new Buffer(new Array(1024 * 256).join('ABCD')); // 1mb + +var options = { + key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'), + cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem') +}; + +function Mediator() { + stream.Writable.call(this); + this.buf = ''; +}; +util.inherits(Mediator, stream.Writable); + +Mediator.prototype._write = function write(data, enc, cb) { + this.buf += data; + setTimeout(cb, 0); + + if (this.buf.length >= request.length) { + assert.equal(this.buf, request.toString()); + server.close(); + } +}; + +var mediator = new Mediator(); + +var server = tls.Server(options, function(socket) { + socket.pipe(mediator); + serverConnected++; +}); + +server.listen(common.PORT, function() { + var client1 = tls.connect({ + port: common.PORT, + rejectUnauthorized: false + }, function() { + ++clientConnected; + client1.end(request); + }); +}); + +process.on('exit', function() { + assert.equal(clientConnected, 1); + assert.equal(serverConnected, 1); +}); From 808b7ada07dfcf293ec40900240329efa7dc841c Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 18 Mar 2013 14:40:03 +0100 Subject: [PATCH 16/30] doc: fix broken links in blog footer The blog lives at blog.nodejs.org while the main website lives at nodejs.org. Ergo, use absolute URLs for links to the main website. Fixes #5062. --- doc/blog.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/blog.html b/doc/blog.html index 153e3ba9ae7..6a353cee3fc 100644 --- a/doc/blog.html +++ b/doc/blog.html @@ -209,14 +209,14 @@ - +