diff --git a/lib/http.js b/lib/http.js index 2019514645e..d286fa8e848 100644 --- a/lib/http.js +++ b/lib/http.js @@ -1296,12 +1296,14 @@ ClientRequest.prototype.onSocket = function(socket) { // Setup 'drain' propogation. httpSocketSetup(socket); - var errorListener = function(err) { + function errorListener(err) { debug('HTTP SOCKET ERROR: ' + err.message + '\n' + err.stack); - 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 (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); @@ -1515,7 +1517,6 @@ ClientRequest.prototype.setTimeout = function(msecs, callback) { var self = this; function emitTimeout() { self.emit('timeout'); - self.destroy(new Error('timeout')); } if (this.socket && this.socket.writable) { diff --git a/lib/module.js b/lib/module.js index f6f6285fb61..bbab6b9e669 100644 --- a/lib/module.js +++ b/lib/module.js @@ -305,11 +305,16 @@ Module._load = function(request, parent, isMain) { } Module._cache[filename] = module; + + var hadException = true; + try { module.load(filename); - } catch (err) { - delete Module._cache[filename]; - throw err; + hadException = false; + } finally { + if (hadException) { + delete Module._cache[filename]; + } } return module.exports; diff --git a/src/node.cc b/src/node.cc index 5afeb55fafd..556fd0d8e3a 100644 --- a/src/node.cc +++ b/src/node.cc @@ -90,7 +90,6 @@ extern char **environ; namespace node { - static Persistent process; static Persistent errno_symbol; @@ -256,9 +255,7 @@ static void Spin(uv_idle_t* handle, int status) { Tick(); } - -static Handle NeedTickCallback(const Arguments& args) { - HandleScope scope; +static void StartTickSpinner() { need_tick_cb = true; // TODO: this tick_spinner shouldn't be necessary. An ev_prepare should be // sufficent, the problem is only in the case of the very last "tick" - @@ -269,9 +266,12 @@ static Handle NeedTickCallback(const Arguments& args) { uv_idle_start(&tick_spinner, Spin); uv_ref(uv_default_loop()); } - return Undefined(); } +static Handle NeedTickCallback(const Arguments& args) { + StartTickSpinner(); + return Undefined(); +} static void PrepareTick(uv_prepare_t* handle, int status) { assert(handle == &prepare_tick_watcher); @@ -1834,11 +1834,15 @@ void FatalException(TryCatch &try_catch) { TryCatch event_try_catch; emit->Call(process, 2, event_argv); + if (event_try_catch.HasCaught()) { // the uncaught exception event threw, so we must exit. ReportException(event_try_catch, true); exit(1); } + + // This makes sure uncaught exceptions don't interfere with process.nextTick + StartTickSpinner(); } @@ -2288,7 +2292,6 @@ void Load(Handle process_l) { // source code.) // The node.js file returns a function 'f' - atexit(AtExit); TryCatch try_catch; diff --git a/src/node.js b/src/node.js index bbda8c80cc6..5f2dd87bda2 100644 --- a/src/node.js +++ b/src/node.js @@ -219,35 +219,27 @@ startup.processNextTick = function() { var nextTickQueue = []; + var nextTickIndex = 0; process._tickCallback = function() { - var l = nextTickQueue.length; - if (l === 0) return; + var nextTickLength = nextTickQueue.length; + if (nextTickLength === 0) return; - var q = nextTickQueue; - nextTickQueue = []; - - try { - for (var i = 0; i < l; i++) { - var tock = q[i]; - var callback = tock.callback; - if (tock.domain) { - if (tock.domain._disposed) continue; - tock.domain.enter(); - } - callback(); - if (tock.domain) tock.domain.exit(); + while (nextTickIndex < nextTickLength) { + var tock = nextTickQueue[nextTickIndex++]; + var callback = tock.callback; + if (tock.domain) { + if (tock.domain._disposed) continue; + tock.domain.enter(); + } + callback(); + if (tock.domain) { + tock.domain.exit(); } } - catch (e) { - if (i + 1 < l) { - nextTickQueue = q.slice(i + 1).concat(nextTickQueue); - } - if (nextTickQueue.length) { - process._needTickCallback(); - } - throw e; // process.nextTick error, or 'error' event on first tick - } + + nextTickQueue.splice(0, nextTickIndex); + nextTickIndex = 0; }; process.nextTick = function(callback) { diff --git a/test/message/stack_overflow.out b/test/message/stack_overflow.out index f09f10bb5ac..72dc019ff1a 100644 --- a/test/message/stack_overflow.out +++ b/test/message/stack_overflow.out @@ -1,6 +1,6 @@ before -node.js:* - throw e; // process.nextTick error, or 'error' event on first tick - ^ +*test*message*stack_overflow.js:31 +function stackOverflow() { + ^ RangeError: Maximum call stack size exceeded diff --git a/test/message/throw_custom_error.out b/test/message/throw_custom_error.out index 357afdb76b6..e8c9903a3ab 100644 --- a/test/message/throw_custom_error.out +++ b/test/message/throw_custom_error.out @@ -1,6 +1,6 @@ before -node.js:* - throw e; // process.nextTick error, or 'error' event on first tick - ^ +*test*message*throw_custom_error.js:31 +throw ({ name: 'MyCustomError', message: 'This is a custom message' }); +^ MyCustomError: This is a custom message diff --git a/test/message/throw_non_error.out b/test/message/throw_non_error.out index 477b13cd8aa..23e3d928e83 100644 --- a/test/message/throw_non_error.out +++ b/test/message/throw_non_error.out @@ -1,6 +1,6 @@ before -node.js:* - throw e; // process.nextTick error, or 'error' event on first tick - ^ +*/test/message/throw_non_error.js:31 +throw ({ foo: 'bar' }); +^ [object Object] diff --git a/test/message/undefined_reference_in_new_context.out b/test/message/undefined_reference_in_new_context.out index a32a4d3a2b8..fc8c13eb59f 100644 --- a/test/message/undefined_reference_in_new_context.out +++ b/test/message/undefined_reference_in_new_context.out @@ -1,8 +1,8 @@ before -node.js:* - throw e; // process.nextTick error, or 'error' event on first tick - ^ +*test*message*undefined_reference_in_new_context.js:34 +script.runInNewContext(); + ^ ReferenceError: foo is not defined at evalmachine.:* at Object. (*test*message*undefined_reference_in_new_context.js:*) diff --git a/test/simple/test-http-client-timeout.js b/test/simple/test-http-client-timeout.js new file mode 100644 index 00000000000..98eb5539ebf --- /dev/null +++ b/test/simple/test-http-client-timeout.js @@ -0,0 +1,50 @@ +// 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 http = require('http'); + +var options = { + method: 'GET', + port: common.PORT, + host: '127.0.0.1', + path: '/' +}; + +var server = http.createServer(function(req, res) { + // this space intentionally left blank +}); + +server.listen(options.port, options.host, function() { + var req = http.request(options, function(res) { + // this space intentionally left blank + }); + req.on('close', function() { + server.close(); + }); + function destroy() { + req.destroy(); + } + req.setTimeout(1, destroy); + req.on('error', destroy); + req.end(); +});