From 3883f22ad18998e24246358e0d87e68bd9d2b2b0 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 9 May 2012 23:11:14 +0200 Subject: [PATCH 01/19] pipe_wrap: don't assert() on pipe accept errors Pass errors to the onconnection callback. --- src/pipe_wrap.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index c99fe473976..b062b40f4f6 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -191,8 +191,8 @@ void PipeWrap::OnConnection(uv_stream_t* handle, int status) { assert(wrap->object_.IsEmpty() == false); if (status != 0) { - // TODO Handle server error (set errno and call onconnection with NULL) - assert(0); + SetErrno(uv_last_error(uv_default_loop())); + MakeCallback(wrap->object_, "onconnection", 0, NULL); return; } From 493beb23f234e629ab9f929ebb03969966355dc3 Mon Sep 17 00:00:00 2001 From: Philip Tellis Date: Wed, 9 May 2012 16:39:27 -0400 Subject: [PATCH 02/19] doc: fs.ReadableStream does not have a destroySoon method --- doc/api/stream.markdown | 5 ----- 1 file changed, 5 deletions(-) diff --git a/doc/api/stream.markdown b/doc/api/stream.markdown index aaf321977fe..33c79bf5813 100644 --- a/doc/api/stream.markdown +++ b/doc/api/stream.markdown @@ -67,11 +67,6 @@ Resumes the incoming `'data'` events after a `pause()`. Closes the underlying file descriptor. Stream will not emit any more events. - -### stream.destroySoon() - -After the write queue is drained, close the file descriptor. - ### stream.pipe(destination, [options]) This is a `Stream.prototype` method available on all `Stream`s. From 52f0c37d0912f3a348ef52788be604ccbd9d1a07 Mon Sep 17 00:00:00 2001 From: "vegorov@chromium.org" Date: Wed, 12 Oct 2011 10:35:42 +0000 Subject: [PATCH 03/19] Runtime_NotifyDeoptimized should search for function activation in all thread stacks. R=fschneider@chromium.org BUG=v8:1763 Review URL: http://codereview.chromium.org/8240004 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@9588 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- deps/v8/src/frames-inl.h | 9 +++++++++ deps/v8/src/frames.h | 2 ++ deps/v8/src/runtime.cc | 40 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/deps/v8/src/frames-inl.h b/deps/v8/src/frames-inl.h index 7ba79bf1b55..98a943a0e39 100644 --- a/deps/v8/src/frames-inl.h +++ b/deps/v8/src/frames-inl.h @@ -197,6 +197,15 @@ inline JavaScriptFrameIteratorTemp::JavaScriptFrameIteratorTemp( if (!done()) Advance(); } + +template +inline JavaScriptFrameIteratorTemp::JavaScriptFrameIteratorTemp( + Isolate* isolate, ThreadLocalTop* top) + : iterator_(isolate, top) { + if (!done()) Advance(); +} + + template inline JavaScriptFrame* JavaScriptFrameIteratorTemp::frame() const { // TODO(1233797): The frame hierarchy needs to change. It's diff --git a/deps/v8/src/frames.h b/deps/v8/src/frames.h index fed11c4faf5..8d7d6a32608 100644 --- a/deps/v8/src/frames.h +++ b/deps/v8/src/frames.h @@ -710,6 +710,8 @@ class JavaScriptFrameIteratorTemp BASE_EMBEDDED { inline explicit JavaScriptFrameIteratorTemp(Isolate* isolate); + inline JavaScriptFrameIteratorTemp(Isolate* isolate, ThreadLocalTop* top); + // Skip frames until the frame with the given id is reached. explicit JavaScriptFrameIteratorTemp(StackFrame::Id id) { AdvanceToId(id); } diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index b1c4c102ec3..9153312bec5 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/src/runtime.cc @@ -8201,6 +8201,31 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_LazyRecompile) { } +class ActivationsFinder : public ThreadVisitor { + public: + explicit ActivationsFinder(JSFunction* function) + : function_(function), has_activations_(false) {} + + void VisitThread(Isolate* isolate, ThreadLocalTop* top) { + if (has_activations_) return; + + for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) { + JavaScriptFrame* frame = it.frame(); + if (frame->is_optimized() && frame->function() == function_) { + has_activations_ = true; + return; + } + } + } + + bool has_activations() { return has_activations_; } + + private: + JSFunction* function_; + bool has_activations_; +}; + + RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyDeoptimized) { HandleScope scope(isolate); ASSERT(args.length() == 1); @@ -8247,17 +8272,24 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyDeoptimized) { return isolate->heap()->undefined_value(); } - // Count the number of optimized activations of the function. - int activations = 0; + // Find other optimized activations of the function. + bool has_other_activations = false; while (!it.done()) { JavaScriptFrame* frame = it.frame(); if (frame->is_optimized() && frame->function() == *function) { - activations++; + has_other_activations = true; + break; } it.Advance(); } - if (activations == 0) { + if (!has_other_activations) { + ActivationsFinder activations_finder(*function); + isolate->thread_manager()->IterateArchivedThreads(&activations_finder); + has_other_activations = activations_finder.has_activations(); + } + + if (!has_other_activations) { if (FLAG_trace_deopt) { PrintF("[removing optimized code for: "); function->PrintName(); From cc8cfb145a2298868bd1b2520021544da5a93712 Mon Sep 17 00:00:00 2001 From: Shigeki Ohtsu Date: Fri, 11 May 2012 19:07:08 +0900 Subject: [PATCH 04/19] doc: fix typo in buffer documentation Fixes #3253. --- doc/api/buffer.markdown | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/api/buffer.markdown b/doc/api/buffer.markdown index 018664224d9..564b715d1ad 100644 --- a/doc/api/buffer.markdown +++ b/doc/api/buffer.markdown @@ -66,7 +66,7 @@ Allocates a new buffer containing the given `str`. * `string` String - data to be written to buffer * `offset` Number, Optional, Default: 0 -* `length` Number, Optional +* `length` Number, Optional, Default: `buffer.length - offset` * `encoding` String, Optional, Default: 'utf8' Writes `string` to the buffer at `offset` using the given encoding. @@ -89,7 +89,7 @@ next time `buf.write()` is called. * `encoding` String, Optional, Default: 'utf8' * `start` Number, Optional, Default: 0 -* `end` Number, Optional +* `end` Number, Optional, Default: `buffer.length` Decodes and returns a string from buffer data encoded with `encoding` (defaults to `'utf8'`) beginning at `start` (defaults to `0`) and ending at @@ -167,7 +167,7 @@ buffer object. It does not change when the contents of the buffer are changed. * `targetBuffer` Buffer object - Buffer to copy into * `targetStart` Number, Optional, Default: 0 * `sourceStart` Number, Optional, Default: 0 -* `sourceEnd` Number, Optional, Default: 0 +* `sourceEnd` Number, Optional, Default: `buffer.length` Does copy between buffers. The source and target regions can be overlapped. `targetStart` and `sourceStart` default to `0`. @@ -193,7 +193,7 @@ into `buf2`, starting at the 8th byte in `buf2`. ### buf.slice([start], [end]) * `start` Number, Optional, Default: 0 -* `end` Number, Optional, Default: 0 +* `end` Number, Optional, Default: `buffer.length` Returns a new buffer which references the same memory as the old, but offset and cropped by the `start` (defaults to `0`) and `end` (defaults to From 884499d37e7cf6f553c0110dcfa4691f2c44ad80 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 11 May 2012 03:09:51 +0200 Subject: [PATCH 05/19] build: fix cross-compiling Take arch cflags in account when building libuv. --- wscript | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/wscript b/wscript index a63db7d3aeb..fb8cb1cae2a 100644 --- a/wscript +++ b/wscript @@ -255,7 +255,19 @@ def get_node_version(): node_is_release == "0" and "-pre" or "" ) +def arch_cflags(conf): + flags = [] + if 'DEST_CPU' in conf.env: + arch = conf.env['DEST_CPU'] + if arch == 'ia32': + flags += ['-m32'] + elif arch == 'x64': + flags += ['-m64'] + if sys.platform.startswith('darwin'): + flags += ['-arch', {'ia32':'i386', 'x64':'x86_64'}.get(arch, arch)] + + return flags def configure(conf): conf.check_tool('compiler_cxx') @@ -440,30 +452,15 @@ def configure(conf): conf.env.append_value ('CCFLAGS', threadflags) conf.env.append_value ('CXXFLAGS', threadflags) conf.env.append_value ('LINKFLAGS', threadflags) - if sys.platform.startswith("darwin"): + + if sys.platform.startswith('darwin'): # used by platform_darwin_*.cc conf.env.append_value('LINKFLAGS', ['-framework','Carbon']) - # cross compile for architecture specified by DEST_CPU - if 'DEST_CPU' in conf.env: - arch = conf.env['DEST_CPU'] - # map supported_archs to GCC names: - arch_mappings = {'ia32': 'i386', 'x64': 'x86_64'} - if arch in arch_mappings: - arch = arch_mappings[arch] - flags = ['-arch', arch] - conf.env.append_value('CCFLAGS', flags) - conf.env.append_value('CXXFLAGS', flags) - conf.env.append_value('LINKFLAGS', flags) - if 'DEST_CPU' in conf.env: - arch = conf.env['DEST_CPU'] - # TODO: -m32 is only available on 64 bit machines, so check host type - flags = None - if arch == 'ia32': - flags = '-m32' - if flags: - conf.env.append_value('CCFLAGS', flags) - conf.env.append_value('CXXFLAGS', flags) - conf.env.append_value('LINKFLAGS', flags) + + flags = arch_cflags(conf) + conf.env.append_value('CCFLAGS', flags) + conf.env.append_value('CXXFLAGS', flags) + conf.env.append_value('LINKFLAGS', flags) # LFS conf.env.append_value('CPPFLAGS', '-D_LARGEFILE_SOURCE') @@ -647,6 +644,8 @@ def uv_cmd(bld, variant): if not sys.platform.startswith('win32'): make = ('if [ -z "$NODE_MAKE" ]; then NODE_MAKE=make; fi; ' '$NODE_MAKE -C ' + sh_escape(blddir)) + flags = arch_cflags(bld) + if flags: make += ' CFLAGS=\'%s\'' % ' '.join(flags) # don't escape else: make = 'make -C ' + sh_escape(blddir) return '%s && (%s clean) && (%s all)' % (cmd, make, make) From 07d8a4650e9e499d479715b6031cdba7096b7385 Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 10 May 2012 17:55:28 -0700 Subject: [PATCH 06/19] Break up huge function in ClientRequest.onSocket --- lib/http.js | 344 ++++++++++++++++++++++++++++------------------------ 1 file changed, 187 insertions(+), 157 deletions(-) diff --git a/lib/http.js b/lib/http.js index 070942ef245..61204cad4bd 100644 --- a/lib/http.js +++ b/lib/http.js @@ -105,6 +105,7 @@ function parserOnBody(b, start, len) { parser.incoming.emit('data', slice); } } + function parserOnMessageComplete() { var parser = this; parser.incoming.complete = true; @@ -490,7 +491,9 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) { // keep-alive logic if (sentConnectionHeader === false) { if (this.shouldKeepAlive && - (sentContentLengthHeader || this.useChunkedEncodingByDefault || this.agent)) { + (sentContentLengthHeader || + this.useChunkedEncodingByDefault || + this.agent)) { messageHeader += 'Connection: keep-alive\r\n'; } else { this._last = true; @@ -523,7 +526,7 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) { OutgoingMessage.prototype.setHeader = function(name, value) { if (arguments.length < 2) { - throw new Error("`name` and `value` are required for setHeader()."); + throw new Error('`name` and `value` are required for setHeader().'); } if (this._header) { @@ -540,7 +543,7 @@ OutgoingMessage.prototype.setHeader = function(name, value) { OutgoingMessage.prototype.getHeader = function(name) { if (arguments.length < 1) { - throw new Error("`name` is required for getHeader()."); + throw new Error('`name` is required for getHeader().'); } if (!this._headers) return; @@ -552,7 +555,7 @@ OutgoingMessage.prototype.getHeader = function(name) { OutgoingMessage.prototype.removeHeader = function(name) { if (arguments.length < 1) { - throw new Error("`name` is required for removeHeader()."); + throw new Error('`name` is required for removeHeader().'); } if (this._header) { @@ -1078,7 +1081,9 @@ function ClientRequest(options, cb) { self._last = true; self.shouldKeepAlive = false; if (options.createConnection) { - self.onSocket(options.createConnection(options.port, options.host, options)); + self.onSocket(options.createConnection(options.port, + options.host, + options)); } else { self.onSocket(net.createConnection(options.port, options.host)); } @@ -1095,7 +1100,8 @@ util.inherits(ClientRequest, OutgoingMessage); exports.ClientRequest = ClientRequest; ClientRequest.prototype._implicitHeader = function() { - this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n', this._renderHeaders()); + this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n', + this._renderHeaders()); }; ClientRequest.prototype.abort = function() { @@ -1131,6 +1137,7 @@ function freeParser(parser, req) { if (parser.socket) { parser.socket.onend = null; parser.socket.ondata = null; + parser.socket.parser = null; } parser.socket = null; parser.incoming = null; @@ -1140,175 +1147,198 @@ function freeParser(parser, req) { if (req) { req.parser = null; } -}; +} + +function socketCloseListener() { + var socket = this; + var parser = socket.parser; + var req = socket._httpMessage; + debug('HTTP socket close'); + req.emit('close'); + if (req.res && req.res.readable) { + // Socket closed before we emitted "end" below. + req.res.emit('aborted'); + req.res.emit('end'); + req.res.emit('close'); + } else if (!req.res && !req._hadError) { + // This socket error fired before we started to + // receive a response. The error needs to + // fire on the request. + req.emit('error', createHangUpError()); + } + + if (parser) { + parser.finish(); + freeParser(parser, req); + } +} + +function socketErrorListener(err) { + var socket = this; + var parser = socket.parser; + var req = socket._httpMessage; + debug('HTTP SOCKET ERROR: ' + err.message + '\n' + err.stack); + if (req) { + req.emit('error', err); + // For Safety. Some additional errors might fire later on + // and we need to make sure we don't double-fire the error event. + req._hadError = true; + } + if (parser) { + parser.finish(); + freeParser(parser, req); + } + socket.destroy(); +} + +function responseOnEnd() { + var req = this.req; + var socket = req.socket; + + if (req.shouldKeepAlive) { + debug('AGENT socket keep-alive'); + socket.removeListener('close', socketCloseListener); + socket.removeListener('error', socketErrorListener); + socket.emit('free'); + } else { + if (socket.writable) { + debug('AGENT socket.destroySoon()'); + socket.destroySoon(); + } + assert(!socket.writable); + } +} + +function parserOnIncomingClient(res, shouldKeepAlive) { + var parser = this; + var socket = this.socket; + var req = socket._httpMessage; + + debug('AGENT incoming response!'); + + if (req.res) { + // We already have a response object, this means the server + // sent a double response. + socket.destroy(); + return; + } + req.res = res; + + // Responses to HEAD requests are crazy. + // HEAD responses aren't allowed to have an entity-body + // but *can* have a content-length which actually corresponds + // to the content-length of the entity-body had the request + // been a GET. + var isHeadResponse = req.method == 'HEAD'; + debug('AGENT isHeadResponse ' + isHeadResponse); + + if (res.statusCode == 100) { + // restart the parser, as this is a continue message. + delete req.res; // Clear res so that we don't hit double-responses. + req.emit('continue'); + return true; + } + + if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgraded) { + // Server MUST respond with Connection:keep-alive for us to enable it. + // If we've been upgraded (via WebSockets) we also shouldn't try to + // keep the connection open. + req.shouldKeepAlive = false; + } + + DTRACE_HTTP_CLIENT_RESPONSE(socket, req); + req.emit('response', res); + req.res = res; + res.req = req; + + res.on('end', responseOnEnd); + + return isHeadResponse; +} + +function socketOnEnd() { + var socket = this; + var req = this._httpMessage; + var parser = this.parser; + if (!req.res) { + // If we don't have a response then we know that the socket + // ended prematurely and we need to emit an error on the request. + req.emit('error', createHangUpError()); + req._hadError = true; + } + if (parser) { + parser.finish(); + freeParser(parser, req); + } + socket.destroy(); +} +function socketOnData(d, start, end) { + var socket = this; + var req = this._httpMessage; + var parser = this.parser; + + var ret = parser.execute(d, start, end - start); + if (ret instanceof Error) { + debug('parse error'); + freeParser(parser, req); + socket.destroy(ret); + } else if (parser.incoming && parser.incoming.upgrade) { + var bytesParsed = ret; + socket.ondata = null; + socket.onend = null; + + var res = parser.incoming; + req.res = res; + + // This is start + byteParsed + var upgradeHead = d.slice(start + bytesParsed, end); + if (req.listeners('upgrade').length) { + // Emit 'upgrade' on the Agent. + req.upgraded = true; + req.emit('upgrade', res, socket, upgradeHead); + socket.emit('agentRemove'); + } else { + // Got upgrade header, but have no handler. + socket.destroy(); + } + freeParser(parser, req); + } else if (parser.incoming && parser.incoming.complete && + // When the status code is 100 (Continue), the server will + // send a final response after this client sends a request + // body. So, we must not free the parser. + parser.incoming.statusCode !== 100) { + freeParser(parser, req); + } +} + ClientRequest.prototype.onSocket = function(socket) { var req = this; + process.nextTick(function() { var parser = parsers.alloc(); + req.socket = socket; req.connection = socket; - parser.reinitialize(HTTPParser.RESPONSE); parser.socket = socket; + socket.parser = parser; + parser.reinitialize(HTTPParser.RESPONSE); parser.incoming = null; req.parser = parser; socket._httpMessage = req; + // Setup "drain" propogation. httpSocketSetup(socket); + socket.ondata = socketOnData; + socket.onend = socketOnEnd; + socket.on('error', socketErrorListener); + socket.on('close', socketCloseListener); + parser.onIncoming = parserOnIncomingClient; - function errorListener(err) { - debug('HTTP SOCKET ERROR: ' + err.message + '\n' + err.stack); - if (req) { - req.emit('error', err); - // For Safety. Some additional errors might fire later on - // and we need to make sure we don't double-fire the error event. - req._hadError = true; - } - if (parser) { - parser.finish(); - freeParser(parser, req); - } - socket.destroy(); - } - - socket.on('error', errorListener); - - socket.ondata = function(d, start, end) { - var ret = parser.execute(d, start, end - start); - if (ret instanceof Error) { - debug('parse error'); - freeParser(parser, req); - socket.destroy(ret); - } else if (parser.incoming && parser.incoming.upgrade) { - var bytesParsed = ret; - socket.ondata = null; - socket.onend = null; - - var res = parser.incoming; - req.res = res; - - // This is start + byteParsed - var upgradeHead = d.slice(start + bytesParsed, end); - if (req.listeners('upgrade').length) { - // Emit 'upgrade' on the Agent. - req.upgraded = true; - req.emit('upgrade', res, socket, upgradeHead); - socket.emit('agentRemove'); - } else { - // Got upgrade header, but have no handler. - socket.destroy(); - } - freeParser(parser, req); - } else if (parser.incoming && parser.incoming.complete && - // When the status code is 100 (Continue), the server will - // send a final response after this client sends a request - // body. So, we must not free the parser. - parser.incoming.statusCode !== 100) { - freeParser(parser, req); - } - }; - - socket.onend = function() { - if (!req.res) { - // If we don't have a response then we know that the socket - // ended prematurely and we need to emit an error on the request. - req.emit('error', createHangUpError()); - req._hadError = true; - } - if (parser) { - parser.finish(); - freeParser(parser, req); - } - socket.destroy(); - }; - - var closeListener = function() { - debug('HTTP socket close'); - req.emit('close'); - if (req.res && req.res.readable) { - // Socket closed before we emitted "end" below. - req.res.emit('aborted'); - req.res.emit('end'); - req.res.emit('close'); - } else if (!req.res && !req._hadError) { - // This socket error fired before we started to - // receive a response. The error needs to - // fire on the request. - req.emit('error', createHangUpError()); - } - - // Nothing more to be done with this req, since the socket - // is closed, and we've emitted the appropriate abort/end/close/error - // events. Disavow all knowledge, and break the references to - // the variables trapped by closures and on the socket object. - req = null; - socket._httpMessage = null; - } - socket.on('close', closeListener); - - parser.onIncoming = function(res, shouldKeepAlive) { - debug('AGENT incoming response!'); - - if (req.res) { - // We already have a response object, this means the server - // sent a double response. - socket.destroy(); - return; - } - req.res = res; - - // Responses to HEAD requests are crazy. - // HEAD responses aren't allowed to have an entity-body - // but *can* have a content-length which actually corresponds - // to the content-length of the entity-body had the request - // been a GET. - var isHeadResponse = req.method == 'HEAD'; - debug('AGENT isHeadResponse ' + isHeadResponse); - - if (res.statusCode == 100) { - // restart the parser, as this is a continue message. - delete req.res; // Clear res so that we don't hit double-responses. - req.emit('continue'); - return true; - } - - if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgraded) { - // Server MUST respond with Connection:keep-alive for us to enable it. - // If we've been upgraded (via WebSockets) we also shouldn't try to - // keep the connection open. - req.shouldKeepAlive = false; - } - - res.addListener('end', function() { - if (!req.shouldKeepAlive) { - if (socket.writable) { - debug('AGENT socket.destroySoon()'); - socket.destroySoon(); - } - assert(!socket.writable); - } else { - debug('AGENT socket keep-alive'); - } - }); - - DTRACE_HTTP_CLIENT_RESPONSE(socket, req); - req.emit('response', res); - - res.on('end', function() { - if (req.shouldKeepAlive) { - socket.removeListener('close', closeListener); - socket.removeListener('error', errorListener); - socket.emit('free'); - } - }); - - return isHeadResponse; - }; req.emit('socket', socket); }); - }; ClientRequest.prototype._deferToConnect = function(method, arguments_, cb) { From 9239088e873bb9570b9f21a2e819536bfb3e52ed Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 10 May 2012 17:55:54 -0700 Subject: [PATCH 07/19] 500 is a magic number for the GC for some reason --- test/gc/test-http-client-timeout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/gc/test-http-client-timeout.js b/test/gc/test-http-client-timeout.js index c98a008a2c3..6d705b47eb3 100644 --- a/test/gc/test-http-client-timeout.js +++ b/test/gc/test-http-client-timeout.js @@ -13,7 +13,7 @@ var http = require('http'), done = 0, count = 0, countGC = 0, - todo = 500, + todo = 550, common = require('../common.js'), assert = require('assert'), PORT = common.PORT; From 5f9ffa17b171e1a62004f426247332f662001cf9 Mon Sep 17 00:00:00 2001 From: koichik Date: Sat, 12 May 2012 10:24:46 +0900 Subject: [PATCH 08/19] fs: fix ReadStream.pause() emits duplicate data event Fixes #3258. --- lib/fs.js | 3 +- test/simple/test-fs-read-stream-resume.js | 51 +++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 test/simple/test-fs-read-stream-resume.js diff --git a/lib/fs.js b/lib/fs.js index 5b04e97572d..13ee60a5197 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -1205,8 +1205,9 @@ ReadStream.prototype.resume = function() { this.paused = false; if (this.buffer) { - this._emitData(this.buffer); + var buffer = this.buffer; this.buffer = null; + this._emitData(buffer); } // hasn't opened yet. diff --git a/test/simple/test-fs-read-stream-resume.js b/test/simple/test-fs-read-stream-resume.js new file mode 100644 index 00000000000..c3ace49e9ff --- /dev/null +++ b/test/simple/test-fs-read-stream-resume.js @@ -0,0 +1,51 @@ +// 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 fs = require('fs'); +var path = require('path'); + +var file = path.join(common.fixturesDir, 'x.txt'); +var data = ''; +var first = true; + +var stream = fs.createReadStream(file); +stream.setEncoding('utf8'); +stream.on('data', function(chunk) { + data += chunk; + if (first) { + first = false; + stream.resume(); + } +}); + +process.nextTick(function() { + stream.pause(); + setTimeout(function() { + stream.resume(); + }, 100); +}); + +process.on('exit', function() { + assert.equal(data, 'xyz\n'); +}); From d8351a2ef45756fb3458d3cdf964955e39fe4a76 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Sat, 12 May 2012 19:37:33 +0200 Subject: [PATCH 09/19] Automatically close FSWatcher on error Closes #3250 --- lib/fs.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/fs.js b/lib/fs.js index 13ee60a5197..b48de3a5d20 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -659,6 +659,7 @@ function FSWatcher() { this._handle.onchange = function(status, event, filename) { if (status) { + self._handle.close(); self.emit('error', errnoException(errno, 'watch')); } else { self.emit('change', event, filename); From d91004a73d7ae11829f06aea7f630129e8d2683c Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Sun, 13 May 2012 03:18:09 +0200 Subject: [PATCH 10/19] Windows: make path.normalize convert all slashes to backslashes Closes #3066 --- lib/path.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/path.js b/lib/path.js index 5d69d2270fe..5fb6656dc4b 100644 --- a/lib/path.js +++ b/lib/path.js @@ -177,6 +177,9 @@ if (isWindows) { tail += '\\'; } + // Convert slashes to backslashes when `device` points to an UNC root. + device = device.replace(/\//g, '\\'); + return device + (isAbsolute ? '\\' : '') + tail; }; From a475e62a3e6bcec3be6ff03f1c4bdbf5b188065c Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Sun, 13 May 2012 03:29:44 +0200 Subject: [PATCH 11/19] Windows: add test for path.normalize with UNC paths --- test/simple/test-path.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/simple/test-path.js b/test/simple/test-path.js index 92c2647fd26..6105f2302cd 100644 --- a/test/simple/test-path.js +++ b/test/simple/test-path.js @@ -197,6 +197,8 @@ if (isWindows) { assert.equal(path.normalize('a//b//../b'), 'a\\b'); assert.equal(path.normalize('a//b//./c'), 'a\\b\\c'); assert.equal(path.normalize('a//b//.'), 'a\\b'); + assert.equal(path.normalize('//server/share/dir/file.ext'), + '\\\\server\\share\\dir\\file.ext'); } else { assert.equal(path.normalize('./fixtures///b/../b/c.js'), 'fixtures/b/c.js'); From bd907174e8082afd6dd8553940aac09cae3dcfb7 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 14 May 2012 07:14:18 +0200 Subject: [PATCH 12/19] node: delete NODE_CHANNEL_FD from env Prevents accidental inheritance by child processes. If the child process is a node process, it would try to set up a channel with the parent and consequently never quit because the channel kept the event loop alive. Fixes #3240. --- src/node.js | 4 ++ .../test-child-process-fork-and-spawn.js | 53 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 test/simple/test-child-process-fork-and-spawn.js diff --git a/src/node.js b/src/node.js index 9179d8146d4..6b46394f57b 100644 --- a/src/node.js +++ b/src/node.js @@ -415,6 +415,10 @@ // start parsing data from that stream. if (process.env.NODE_CHANNEL_FD) { assert(parseInt(process.env.NODE_CHANNEL_FD) >= 0); + + // Make sure it's not accidentally inherited by child processes. + delete process.env.NODE_CHANNEL_FD; + var cp = NativeModule.require('child_process'); // Load tcp_wrap to avoid situation where we might immediately receive diff --git a/test/simple/test-child-process-fork-and-spawn.js b/test/simple/test-child-process-fork-and-spawn.js new file mode 100644 index 00000000000..989bf7ee3a1 --- /dev/null +++ b/test/simple/test-child-process-fork-and-spawn.js @@ -0,0 +1,53 @@ +// 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 spawn = require('child_process').spawn; +var fork = require('child_process').fork; + +// Fork, then spawn. The spawned process should not hang. +switch (process.argv[2] || '') { +case '': + fork(__filename, ['fork']).on('exit', checkExit); + process.on('exit', haveExit); + break; +case 'fork': + spawn(process.execPath, [__filename, 'spawn']).on('exit', checkExit); + process.on('exit', haveExit); + break; +case 'spawn': + break; +default: + assert(0); +} + +var seenExit = false; + +function checkExit(statusCode) { + seenExit = true; + assert.equal(statusCode, 0); + process.nextTick(process.exit); +} + +function haveExit() { + assert.equal(seenExit, true); +} From fb7348ae060bf9dd4b0521ac2533fc1d66c44d8b Mon Sep 17 00:00:00 2001 From: ssuda Date: Mon, 14 May 2012 01:08:23 +0530 Subject: [PATCH 13/19] crypto: add PKCS12/PFX support Fixes #2845. --- doc/api/crypto.markdown | 2 + doc/api/https.markdown | 16 +++++++- doc/api/tls.markdown | 66 +++++++++++++++++++++++++++++- lib/crypto.js | 8 ++++ lib/tls.js | 2 + src/node_crypto.cc | 79 ++++++++++++++++++++++++++++++++++++ src/node_crypto.h | 2 + test/fixtures/test_cert.pfx | Bin 0 -> 1970 bytes test/simple/test-crypto.js | 19 ++++++++- 9 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/test_cert.pfx diff --git a/doc/api/crypto.markdown b/doc/api/crypto.markdown index 683a7ea4022..9700c58d6af 100644 --- a/doc/api/crypto.markdown +++ b/doc/api/crypto.markdown @@ -14,8 +14,10 @@ It also offers a set of wrappers for OpenSSL's hash, hmac, cipher, decipher, sig Creates a credentials object, with the optional details being a dictionary with keys: +* `pfx` : A string or buffer holding the PFX or PKCS12 encoded private key, certificate and CA certificates * `key` : a string holding the PEM encoded private key * `cert` : a string holding the PEM encoded certificate +* `passphrase` : A string of passphrase for the private key or pfx * `ca` : either a string or list of strings of PEM encoded CA certificates to trust. * `ciphers`: a string describing the ciphers to use or exclude. Consult for details diff --git a/doc/api/https.markdown b/doc/api/https.markdown index 32724ca576c..9f505a41d2b 100644 --- a/doc/api/https.markdown +++ b/doc/api/https.markdown @@ -32,6 +32,19 @@ Example: res.end("hello world\n"); }).listen(8000); +Or + + var https = require('https'); + var fs = require('fs'); + + var options = { + pfx: fs.readFileSync('server.pfx') + }; + + https.createServer(options, function (req, res) { + res.writeHead(200); + res.end("hello world\n"); + }).listen(8000); ## https.request(options, callback) @@ -91,8 +104,9 @@ The options argument has the following options The following options from [tls.connect()](tls.html#tls.connect) can also be specified. However, a [globalAgent](#https.globalAgent) silently ignores these. +- `pfx`: Certificate, Private key and CA certificates to use for SSL. Default `null`. - `key`: Private key to use for SSL. Default `null`. -- `passphrase`: A string of passphrase for the private key. Default `null`. +- `passphrase`: A string of passphrase for the private key or pfx. Default `null`. - `cert`: Public x509 certificate to use. Default `null`. - `ca`: An authority certificate or array of authority certificates to check the remote host against. diff --git a/doc/api/tls.markdown b/doc/api/tls.markdown index c991973c6fc..d8ed91ada65 100644 --- a/doc/api/tls.markdown +++ b/doc/api/tls.markdown @@ -28,6 +28,17 @@ Alternatively you can send the CSR to a Certificate Authority for signing. (TODO: docs on creating a CA, for now interested users should just look at `test/fixtures/keys/Makefile` in the Node source code) +To create .pfx or .p12, do this: + + openssl pkcs12 -export -in agent5-cert.pem -inkey agent5-key.pem \ + -certfile ca-cert.pem -out agent5.pfx + + - `in`: certificate + - `inkey`: private key + - `certfile`: all CA certs concatenated in one file like + `cat ca1-cert.pem ca2-cert.pem > ca-cert.pem` + + ## Client-initiated renegotiation attack mitigation @@ -72,10 +83,14 @@ The `connectionListener` argument is automatically set as a listener for the [secureConnection](#event_secureConnection_) event. The `options` object has these possibilities: + - `pfx`: A string or `Buffer` containing the private key, certificate and + CA certs of the server in PFX or PKCS12 format. (Mutually exclusive with + the `key`, `cert` and `ca` options.) + - `key`: A string or `Buffer` containing the private key of the server in PEM format. (Required) - - `passphrase`: A string of passphrase for the private key. + - `passphrase`: A string of passphrase for the private key or pfx. - `cert`: A string or `Buffer` containing the certificate key of the server in PEM format. (Required) @@ -137,7 +152,29 @@ Here is a simple example echo server: console.log('server bound'); }); +Or + + var tls = require('tls'); + var fs = require('fs'); + + var options = { + pfx: fs.readFileSync('server.pfx'), + // This is necessary only if using the client certificate authentication. + requestCert: true, + + }; + + var server = tls.createServer(options, function(cleartextStream) { + console.log('server connected', + cleartextStream.authorized ? 'authorized' : 'unauthorized'); + cleartextStream.write("welcome!\n"); + cleartextStream.setEncoding('utf8'); + cleartextStream.pipe(cleartextStream); + }); + server.listen(8000, function() { + console.log('server bound'); + }); You can test this server by connecting to it with `openssl s_client`: @@ -149,10 +186,13 @@ You can test this server by connecting to it with `openssl s_client`: Creates a new client connection to the given `port` and `host`. (If `host` defaults to `localhost`.) `options` should be an object which specifies + - `pfx`: A string or `Buffer` containing the private key, certificate and + CA certs of the server in PFX or PKCS12 format. + - `key`: A string or `Buffer` containing the private key of the client in PEM format. - - `passphrase`: A string of passphrase for the private key. + - `passphrase`: A string of passphrase for the private key or pfx. - `cert`: A string or `Buffer` containing the certificate key of the client in PEM format. @@ -206,6 +246,28 @@ Here is an example of a client of echo server as described previously: server.close(); }); +Or + + var tls = require('tls'); + var fs = require('fs'); + + var options = { + pfx: fs.readFileSync('client.pfx') + }; + + var cleartextStream = tls.connect(8000, options, function() { + console.log('client connected', + cleartextStream.authorized ? 'authorized' : 'unauthorized'); + process.stdin.pipe(cleartextStream); + process.stdin.resume(); + }); + cleartextStream.setEncoding('utf8'); + cleartextStream.on('data', function(data) { + console.log(data); + }); + cleartextStream.on('end', function() { + server.close(); + }); ## tls.createSecurePair([credentials], [isServer], [requestCert], [rejectUnauthorized]) diff --git a/lib/crypto.js b/lib/crypto.js index 6244f2c8d4b..d1c308dbac5 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -114,6 +114,14 @@ exports.createCredentials = function(options, context) { c.context.setSessionIdContext(options.sessionIdContext); } + if (options.pfx) { + if (options.passphrase) { + c.context.loadPKCS12(options.pfx, options.passphrase); + } else { + c.context.loadPKCS12(options.pfx); + } + } + return c; }; diff --git a/lib/tls.js b/lib/tls.js index f9bb63c6a11..96c52269c1a 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -912,6 +912,7 @@ function Server(/* [options], listener */) { this.setOptions(options); var sharedCreds = crypto.createCredentials({ + pfx : self.pfx, key: self.key, passphrase: self.passphrase, cert: self.cert, @@ -996,6 +997,7 @@ Server.prototype.setOptions = function(options) { this.rejectUnauthorized = false; } + if (options.pfx) this.pfx = options.pfx; if (options.key) this.key = options.key; if (options.passphrase) this.passphrase = options.passphrase; if (options.cert) this.cert = options.cert; diff --git a/src/node_crypto.cc b/src/node_crypto.cc index bf95d79701f..b2858f50880 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -43,6 +43,7 @@ # include #endif + #if OPENSSL_VERSION_NUMBER >= 0x10000000L # define OPENSSL_CONST const #else @@ -169,6 +170,7 @@ void SecureContext::Initialize(Handle target) { NODE_SET_PROTOTYPE_METHOD(t, "setSessionIdContext", SecureContext::SetSessionIdContext); NODE_SET_PROTOTYPE_METHOD(t, "close", SecureContext::Close); + NODE_SET_PROTOTYPE_METHOD(t, "loadPKCS12", SecureContext::LoadPKCS12); target->Set(String::NewSymbol("SecureContext"), t->GetFunction()); } @@ -595,6 +597,83 @@ Handle SecureContext::Close(const Arguments& args) { return False(); } +//Takes .pfx or .p12 and password in string or buffer format +Handle SecureContext::LoadPKCS12(const Arguments& args) { + HandleScope scope; + + BIO* in = NULL; + PKCS12* p12 = NULL; + EVP_PKEY* pkey = NULL; + X509* cert = NULL; + STACK_OF(X509)* extraCerts = NULL; + char* pass = NULL; + bool ret = false; + + SecureContext *sc = ObjectWrap::Unwrap(args.Holder()); + + if (args.Length() < 1) { + return ThrowException(Exception::TypeError( + String::New("Bad parameter"))); + } + + in = LoadBIO(args[0]); + if (in == NULL) { + return ThrowException(Exception::Error( + String::New("Unable to load BIO"))); + } + + if (args.Length() >= 2) { + ASSERT_IS_STRING_OR_BUFFER(args[1]); + + int passlen = DecodeBytes(args[1], BINARY); + if (passlen < 0) { + BIO_free(in); + return ThrowException(Exception::TypeError( + String::New("Bad password"))); + } + pass = new char[passlen + 1]; + int pass_written = DecodeWrite(pass, passlen, args[1], BINARY); + + assert(pass_written == passlen); + pass[passlen] = '\0'; + } + + if (d2i_PKCS12_bio(in, &p12) && + PKCS12_parse(p12, pass, &pkey, &cert, &extraCerts) && + SSL_CTX_use_certificate(sc->ctx_, cert) && + SSL_CTX_use_PrivateKey(sc->ctx_, pkey)) + { + // set extra certs + while (X509* x509 = sk_X509_pop(extraCerts)) { + if (!sc->ca_store_) { + sc->ca_store_ = X509_STORE_new(); + SSL_CTX_set_cert_store(sc->ctx_, sc->ca_store_); + } + + X509_STORE_add_cert(sc->ca_store_, x509); + SSL_CTX_add_client_CA(sc->ctx_, x509); + } + + EVP_PKEY_free(pkey); + X509_free(cert); + sk_X509_free(extraCerts); + + ret = true; + } + + PKCS12_free(p12); + BIO_free(in); + delete[] pass; + + if (!ret) { + unsigned long err = ERR_get_error(); + const char *str = ERR_reason_error_string(err); + return ThrowException(Exception::Error(String::New(str))); + } + + return True(); +} + #ifdef SSL_PRINT_DEBUG # define DEBUG_PRINT(...) fprintf (stderr, __VA_ARGS__) diff --git a/src/node_crypto.h b/src/node_crypto.h index 87a5340147c..6fa3de1df21 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -35,6 +35,7 @@ #include #include #include +#include #ifdef OPENSSL_NPN_NEGOTIATED #include @@ -68,6 +69,7 @@ class SecureContext : ObjectWrap { static v8::Handle SetOptions(const v8::Arguments& args); static v8::Handle SetSessionIdContext(const v8::Arguments& args); static v8::Handle Close(const v8::Arguments& args); + static v8::Handle LoadPKCS12(const v8::Arguments& args); SecureContext() : ObjectWrap() { ctx_ = NULL; diff --git a/test/fixtures/test_cert.pfx b/test/fixtures/test_cert.pfx new file mode 100644 index 0000000000000000000000000000000000000000..fd28781a051e19d99ac266bf9d98ef3fd7694971 GIT binary patch literal 1970 zcmY+Ec{tRI8pnULm@$K)W*8yLw4f6sV;z^0#*)Z#S}bK96v^1ma7<%trBc%<;UvaB z%2qf<$TrQ5!Hgwj97iH#U$b=eJon!2{o{S!_xXOm&->T=e((%<2^a*yGvJ{xghqm8 z!m1Pq4$5M{eV`1u$ByiVXF#=nTCgkzbl;Ao1O@>+P4=e)!Yjd$|K1=8g5gn6arX-r zsuy5h=fGfbfX;wSFdVsVTBS4kMYj%nRufGRb1k-S&}TNuZ+5TtYTOFZ zA>aat!_Csmu7bGdKEep=N%!GE?nryI*QG4;@Kq4`olexHjE<4ygy`>YPE3i~bDX3- zjKuM=HGc6gVrws2wZ91%eR%!6EfGE2_lEUF4ER*72lb*Fhz*csosYK?y*X^*WTwm-_qVs#Ofv|%)zbR|u8qfGve3PTuvS~))B z?CsILw4)zmkKnexH3hF-iPF^04ulzj1d2~k+EYj0NfE!OS162U#?@JrFw0HWFY>0} z6ZDPD;umQd6BKedYN^;2*a=+JT*$0VFN zN(6Ti)n{r^LssM7voBC?<8&)#(Dy*qbIlo3qUe26iRjhb^shsPLjf(PZvJH(w8X3M zc8+T(B@K@Rr^mK2*Pre7-=rtaoan0JS_Kafy zUuQz_CtaE3>g-wS)#@U}&F?MDD_YA|>X%!sH*L^+U!rTVQN!4V921Zbu=7~2iNk5; zU+KTE>4GSS9*j@kXF?1Yca@`egKQdKlY%CT+2e#cA!g-7HG=1{6EBrNTFj+OURVbqN9414x zXq&Z!96=9QX@8x?-eWIbfE_;n{^zY#0n@FnOnj-hhYOIY2)B@phF4luy`2x-Wk1CA zuekDIRlLN!BkH#a#+5u&n8>(ryii#{so4;F79fUL@HL1(ZqAx&uOD8}0XSOvpXk%kkRw@ko?D@>Oh+NXP zEz{vayRe$l_&GIsjo$^x2!AuZr2^xXwH zcD%x~u>zrXRNEz+b1xI;Pt+1xBJMk0PyJ9|U8s@2cN3wM=UW zW3qg5xBF&q744EZDiGyb8y-?z#T3VyZ3*>9YP?19b0PA57=Z~(zyu(^a;(NG8>)RW zuh&?J{dVdDofg}luJYUDOp0dx!NY6ODTtFp4f1WsjDZA-Y0VYVlBT^KdljY_Va4?u~i!A>Zb7RpkZuafi`9D(S}fz0y4Ci21UPmZb~Pf2$2D zisk=;I;~0QWQD32VKQ}nC`;tF8Z~*Llfs>t{xABlA^+^Qxa?%O4W)u-ZE;$x1J^C(8Rl$8}R&+f_+Qx z35XI7+90r~ID5A6Vd@Plf2#5WE{@rf5oS7;w=3&aA@L>M4RN(Sw=5Swlr`5;_jjEh zoTmP@9N46;9q!A;(r)oZjvc+stORqlPXvLGo8J+RD;u8hG zhAt$=J_nw{hrNQ;Pq*V-V$2@aNrJlpM825eJIJ$>hko?%0N-mX8$sE@;k80Kx z6t!77owgkJ&;DE-R8gtGcOma`itpE(Z@RA^cJ^M!9YuCn`d4!fYoK;mVn}c04llPo z`{X(XXZ>`z{IZLD;Ry~!q0E~*fsgYF)@p)V4#}n{1ny2V_vm^nM@@IIPC7U@Lr(9W z$m=}*?#z%0p)JyrV)swx_M}&G>Q{VMp3#imAF9_!)U+M&xc^=?LRMQ2iqVK{92#41 zbEN2|MS`7!4IHMEhVe&!hA`U7%1Xc);3BXOI0^Uw6d)Av2gp0+1$gYtK6q{Xemnvu tb_4|hD~JJ5%=fGLxYQd=zDdL*j?EyiytotSfQnC;#nAn{;z2)N;a^BNk`Vv^ literal 0 HcmV?d00001 diff --git a/test/simple/test-crypto.js b/test/simple/test-crypto.js index 6bd1e02ca7f..dded025cc99 100644 --- a/test/simple/test-crypto.js +++ b/test/simple/test-crypto.js @@ -38,6 +38,7 @@ var path = require('path'); // Test Certificates var caPem = fs.readFileSync(common.fixturesDir + '/test_ca.pem', 'ascii'); var certPem = fs.readFileSync(common.fixturesDir + '/test_cert.pem', 'ascii'); +var certPfx = fs.readFileSync(common.fixturesDir + '/test_cert.pfx'); var keyPem = fs.readFileSync(common.fixturesDir + '/test_key.pem', 'ascii'); var rsaPubPem = fs.readFileSync(common.fixturesDir + '/test_rsa_pubkey.pem', 'ascii'); @@ -54,8 +55,24 @@ try { process.exit(); } -// Test HMAC +// PFX tests +assert.doesNotThrow(function() { + crypto.createCredentials({pfx:certPfx, passphrase:'sample'}); +}); +assert.throws(function() { + crypto.createCredentials({pfx:certPfx}); +}, 'mac verify failure'); + +assert.throws(function() { + crypto.createCredentials({pfx:certPfx, passphrase:'test'}); +}, 'mac verify failure'); + +assert.throws(function() { + crypto.createCredentials({pfx:'sample', passphrase:'test'}); +}, 'not enough data'); + +// Test HMAC var h1 = crypto.createHmac('sha1', 'Node') .update('some data') .update('to hmac') From 9b42d7daaf2685d967b47e56c155225756a8cfec Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 14 May 2012 17:34:33 +0200 Subject: [PATCH 14/19] test: add https + .pfx client/server test --- test/simple/test-https-pfx.js | 56 +++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 test/simple/test-https-pfx.js diff --git a/test/simple/test-https-pfx.js b/test/simple/test-https-pfx.js new file mode 100644 index 00000000000..bfed64afd64 --- /dev/null +++ b/test/simple/test-https-pfx.js @@ -0,0 +1,56 @@ +// 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 https = require('https'); +var fs = require('fs'); + +var pfx = fs.readFileSync(common.fixturesDir + '/test_cert.pfx'); + +var options = { + host: '127.0.0.1', + port: common.PORT, + path: '/', + pfx: pfx, + passphrase: 'sample', + requestCert: true +}; + +var server = https.createServer(options, function(req, res) { + assert.equal(req.socket.authorized, false); // not a client cert + assert.equal(req.socket.authorizationError, 'UNABLE_TO_GET_ISSUER_CERT'); + res.writeHead(200); + res.end('OK'); +}); + +server.listen(options.port, options.host, function() { + var data = ''; + + https.get(options, function(res) { + res.on('data', function(data_) { data += data_ }); + res.on('end', function() { server.close() }); + }); + + process.on('exit', function() { + assert.equal(data, 'OK'); + }); +}); From ea4b1c1c0c2bf6e86385a84d49995553cc58360f Mon Sep 17 00:00:00 2001 From: isaacs Date: Mon, 14 May 2012 12:01:38 -0700 Subject: [PATCH 15/19] Upgrade libuv to bc4126b --- deps/uv/include/uv-private/uv-unix.h | 20 +++++++--- deps/uv/src/unix/core.c | 14 ++++++- deps/uv/src/unix/sunos.c | 56 ++++++++++++++++++---------- deps/uv/src/win/fs.c | 6 +-- 4 files changed, 66 insertions(+), 30 deletions(-) diff --git a/deps/uv/include/uv-private/uv-unix.h b/deps/uv/include/uv-private/uv-unix.h index 1c33684aaba..46b549fbe98 100644 --- a/deps/uv/include/uv-private/uv-unix.h +++ b/deps/uv/include/uv-private/uv-unix.h @@ -36,6 +36,11 @@ #include #include +#if __sun +# include +# include +#endif + /* Note: May be cast to struct iovec. See writev(2). */ typedef struct { char* base; @@ -52,6 +57,14 @@ typedef uid_t uv_uid_t; typedef void* uv_lib_t; #define UV_DYNAMIC /* empty */ +#if defined(PORT_SOURCE_FILE) +# define UV_LOOP_PRIVATE_PLATFORM_FIELDS \ + ev_io fs_event_watcher; \ + int fs_fd; +#else +# define UV_LOOP_PRIVATE_PLATFORM_FIELDS +#endif + #define UV_LOOP_PRIVATE_FIELDS \ ares_channel channel; \ /* \ @@ -60,7 +73,8 @@ typedef void* uv_lib_t; * definition of ares_timeout(). \ */ \ ev_timer timer; \ - struct ev_loop* ev; + struct ev_loop* ev; \ + UV_LOOP_PRIVATE_PLATFORM_FIELDS #define UV_REQ_BUFSML_SIZE (4) @@ -206,12 +220,8 @@ typedef void* uv_lib_t; #elif defined(__sun) -#include -#include - #ifdef PORT_SOURCE_FILE # define UV_FS_EVENT_PRIVATE_FIELDS \ - ev_io event_watcher; \ uv_fs_event_cb cb; \ file_obj_t fo; #else /* !PORT_SOURCE_FILE */ diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index 28a82a3aa96..d9e43ba07e8 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -19,7 +19,7 @@ */ #include "uv.h" -#include "unix/internal.h" +#include "internal.h" #include /* NULL */ #include /* printf */ @@ -151,6 +151,9 @@ uv_loop_t* uv_loop_new(void) { uv_loop_t* loop = calloc(1, sizeof(uv_loop_t)); loop->ev = ev_loop_new(0); ev_set_userdata(loop->ev, loop); +#if HAVE_PORTS_FS + loop->fs_fd = -1; +#endif return loop; } @@ -163,6 +166,12 @@ void uv_loop_delete(uv_loop_t* loop) { memset(loop, 0, sizeof *loop); #endif +#if HAVE_PORTS_FS + if (loop->fs_fd != -1) { + uv__close(loop->fs_fd); + } +#endif + if (loop == default_loop_ptr) default_loop_ptr = NULL; else @@ -182,6 +191,9 @@ uv_loop_t* uv_default_loop(void) { default_loop_struct.ev = ev_default_loop(EVBACKEND_KQUEUE); #else default_loop_struct.ev = ev_default_loop(EVFLAG_AUTO); +#endif +#if HAVE_PORTS_FS + default_loop_struct.fs_fd = -1; #endif ev_set_userdata(default_loop_struct.ev, default_loop_ptr); } diff --git a/deps/uv/src/unix/sunos.c b/deps/uv/src/unix/sunos.c index dbdad3ce7b8..e4f48c7a57b 100644 --- a/deps/uv/src/unix/sunos.c +++ b/deps/uv/src/unix/sunos.c @@ -36,6 +36,11 @@ #if HAVE_PORTS_FS # include # include + +# define PORT_FIRED 0x69 +# define PORT_UNUSED 0x0 +# define PORT_LOADED 0x99 +# define PORT_DELETED -1 #endif @@ -90,36 +95,41 @@ void uv_loadavg(double avg[3]) { #if HAVE_PORTS_FS static void uv__fs_event_rearm(uv_fs_event_t *handle) { - if (port_associate(handle->fd, + if (handle->fd == -1) + return; + + if (port_associate(handle->loop->fs_fd, PORT_SOURCE_FILE, (uintptr_t) &handle->fo, FILE_ATTRIB | FILE_MODIFIED, - NULL) == -1) { + handle) == -1) { uv__set_sys_error(handle->loop, errno); } + handle->fd = PORT_LOADED; } static void uv__fs_event_read(EV_P_ ev_io* w, int revents) { uv_fs_event_t *handle; + uv_loop_t *loop_; timespec_t timeout; port_event_t pe; int events; int r; - handle = container_of(w, uv_fs_event_t, event_watcher); + loop_ = container_of(w, uv_loop_t, fs_event_watcher); do { /* TODO use port_getn() */ do { memset(&timeout, 0, sizeof timeout); - r = port_get(handle->fd, &pe, &timeout); + r = port_get(loop_->fs_fd, &pe, &timeout); } while (r == -1 && errno == EINTR); if (r == -1 && errno == ETIME) break; - + handle = (uv_fs_event_t *)pe.portev_user; assert((r == 0) && "unexpected port_get() error"); events = 0; @@ -128,12 +138,12 @@ static void uv__fs_event_read(EV_P_ ev_io* w, int revents) { if (pe.portev_events & ~(FILE_ATTRIB | FILE_MODIFIED)) events |= UV_RENAME; assert(events != 0); - + handle->fd = PORT_FIRED; handle->cb(handle, NULL, events, 0); } - while (handle->fd != -1); + while (handle->fd != PORT_DELETED); - if (handle->fd != -1) + if (handle->fd != PORT_DELETED) uv__fs_event_rearm(handle); } @@ -144,39 +154,45 @@ int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_cb cb, int flags) { int portfd; + int first_run = 0; loop->counters.fs_event_init++; /* We don't support any flags yet. */ assert(!flags); - - if ((portfd = port_create()) == -1) { - uv__set_sys_error(loop, errno); - return -1; + if (loop->fs_fd == -1) { + if ((portfd = port_create()) == -1) { + uv__set_sys_error(loop, errno); + return -1; + } + loop->fs_fd = portfd; + first_run = 1; } uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); handle->filename = strdup(filename); - handle->fd = portfd; + handle->fd = PORT_UNUSED; handle->cb = cb; memset(&handle->fo, 0, sizeof handle->fo); handle->fo.fo_name = handle->filename; uv__fs_event_rearm(handle); - ev_io_init(&handle->event_watcher, uv__fs_event_read, portfd, EV_READ); - ev_io_start(loop->ev, &handle->event_watcher); - ev_unref(loop->ev); + if (first_run) { + ev_io_init(&loop->fs_event_watcher, uv__fs_event_read, portfd, EV_READ); + ev_io_start(loop->ev, &loop->fs_event_watcher); + ev_unref(loop->ev); + } return 0; } void uv__fs_event_destroy(uv_fs_event_t* handle) { - ev_ref(handle->loop->ev); - ev_io_stop(handle->loop->ev, &handle->event_watcher); - uv__close(handle->fd); - handle->fd = -1; + if (handle->fd == PORT_FIRED) { + port_dissociate(handle->loop->fs_fd, PORT_SOURCE_FILE, (uintptr_t)&handle->fo); + } + handle->fd = PORT_DELETED; free(handle->filename); handle->filename = NULL; handle->fo.fo_name = NULL; diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c index 9e8a99515be..68bb210c9a4 100644 --- a/deps/uv/src/win/fs.c +++ b/deps/uv/src/win/fs.c @@ -248,10 +248,8 @@ void fs__open(uv_fs_t* req, const wchar_t* path, int flags, int mode) { goto end; } - /* Figure out whether path is a file or a directory. */ - if (GetFileAttributesW(path) & FILE_ATTRIBUTE_DIRECTORY) { - attributes |= FILE_FLAG_BACKUP_SEMANTICS; - } + /* Setting this flag makes it possible to open a directory. */ + attributes |= FILE_FLAG_BACKUP_SEMANTICS; file = CreateFileW(path, access, From 14a5b45c06c327c838d93130cdfeec03e8fad1fb Mon Sep 17 00:00:00 2001 From: isaacs Date: Mon, 14 May 2012 17:21:20 -0700 Subject: [PATCH 16/19] Guard against emitting 'end' twice on http responses --- lib/http.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/http.js b/lib/http.js index 61204cad4bd..83d0c556ce9 100644 --- a/lib/http.js +++ b/lib/http.js @@ -125,7 +125,10 @@ function parserOnMessageComplete() { if (!parser.incoming.upgrade) { // For upgraded connections, also emit this after parser.execute parser.incoming.readable = false; - parser.incoming.emit('end'); + if (!parser.incoming._ended) { + parser.incoming._ended = true; + parser.incoming.emit('end'); + } } if (parser.socket.readable) { @@ -1158,7 +1161,10 @@ function socketCloseListener() { if (req.res && req.res.readable) { // Socket closed before we emitted "end" below. req.res.emit('aborted'); - req.res.emit('end'); + if (!req.res._ended) { + req.res._ended = true; + req.res.emit('end'); + } req.res.emit('close'); } else if (!req.res && !req._hadError) { // This socket error fired before we started to From 8068f9bf3883bcf2768d47fe885961a0223e0c45 Mon Sep 17 00:00:00 2001 From: isaacs Date: Mon, 14 May 2012 16:14:12 -0700 Subject: [PATCH 17/19] Escape leading # signs in 'make email.md' --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index acc957dda7a..cce1857a1ae 100644 --- a/Makefile +++ b/Makefile @@ -136,7 +136,7 @@ out/doc/api/%.html: doc/api/%.markdown out/Release/node tools/doc/generate.js --format=html --template=doc/template.html $< > $@ email.md: ChangeLog tools/email-footer.md - bash tools/changelog-head.sh > $@ + bash tools/changelog-head.sh | sed 's|^\* #|* \\#|g' > $@ cat tools/email-footer.md | sed -e 's|__VERSION__|'$(VERSION)'|g' >> $@ blog.html: email.md From 4bc1d395de6abed2cf1e4d0b7b3a1480a21c368f Mon Sep 17 00:00:00 2001 From: isaacs Date: Mon, 14 May 2012 16:12:15 -0700 Subject: [PATCH 18/19] 2012.05.15 Version 0.6.18 (stable) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * windows: skip GetFileAttributes call when opening a file (Bert Belder) * crypto: add PKCS12/PFX support (Sambasiva Suda) * #3240: child_process: delete NODE_CHANNEL_FD from env in spawn (Ben Noordhuis) * windows: add test for path.normalize with UNC paths (Bert Belder) * windows: make path.normalize convert all slashes to backslashes (Bert Belder) * fs: Automatically close FSWatcher on error (Bert Belder) * #3258: fs.ReadStream.pause() emits duplicate data event (koichik) * pipe_wrap: don't assert() on pipe accept errors (Ben Noordhuis) * Better exception output for module load and process.nextTick (Felix Geisendörfer) * zlib: fix error reporting (Ben Noordhuis) * http: Don't destroy on timeout (isaacs) * #3231: http: Don't try to emit error on a null'ed req object (isaacs) * #3236: http: Refactor ClientRequest.onSocket (isaacs) --- ChangeLog | 31 ++++++++++++++++++++++++++++++- src/node_version.h | 2 +- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index dc676bae841..e6e055f5655 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,33 @@ -2012.05.04 Version 0.6.17 (stable) +2012.05.15 Version 0.6.18 (stable) + +* windows: skip GetFileAttributes call when opening a file (Bert Belder) + +* crypto: add PKCS12/PFX support (Sambasiva Suda) + +* #3240: child_process: delete NODE_CHANNEL_FD from env in spawn (Ben Noordhuis) + +* windows: add test for path.normalize with UNC paths (Bert Belder) + +* windows: make path.normalize convert all slashes to backslashes (Bert Belder) + +* fs: Automatically close FSWatcher on error (Bert Belder) + +* #3258: fs.ReadStream.pause() emits duplicate data event (koichik) + +* pipe_wrap: don't assert() on pipe accept errors (Ben Noordhuis) + +* Better exception output for module load and process.nextTick (Felix Geisendörfer) + +* zlib: fix error reporting (Ben Noordhuis) + +* http: Don't destroy on timeout (isaacs) + +* #3231: http: Don't try to emit error on a null'ed req object (isaacs) + +* #3236: http: Refactor ClientRequest.onSocket (isaacs) + + +2012.05.04 Version 0.6.17 (stable), 4ced23deaf36493f4303a18f6fdce768c58becc0 * Upgrade npm to 1.1.21 diff --git a/src/node_version.h b/src/node_version.h index 84b1e0e6583..1603177023a 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -29,7 +29,7 @@ #define NODE_MAJOR_VERSION 0 #define NODE_MINOR_VERSION 6 #define NODE_PATCH_VERSION 18 -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n) From f19f980724fa07347a0d0a9d92e48b267555da5d Mon Sep 17 00:00:00 2001 From: isaacs Date: Tue, 15 May 2012 10:21:57 -0700 Subject: [PATCH 19/19] Now working on 0.6.19 --- src/node_version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node_version.h b/src/node_version.h index 1603177023a..fd560b6d909 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -28,8 +28,8 @@ #define NODE_MAJOR_VERSION 0 #define NODE_MINOR_VERSION 6 -#define NODE_PATCH_VERSION 18 -#define NODE_VERSION_IS_RELEASE 1 +#define NODE_PATCH_VERSION 19 +#define NODE_VERSION_IS_RELEASE 0 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)