More or less satisfied with the SPDY server push implementation in node-spdy, I switch back to some errors that I was seeing the other day:
➜ node-spdy git:(reply-compression) ✗ node ./test/spdy-server.jsI spent some time investigating the SSL negotiation that caused this the other night. Tonight I would like to see what is going on inside the code.
TLS NPN Server is running on port : 8081
node.js:183
throw e; // process.nextTick error, or 'error' event on first tick
^
TypeError: Object [object Object] has no method 'destroy'
at CleartextStream.onclose (stream.js:92:10)
at CleartextStream.emit (events.js:81:20)
at Array.(tls.js:654:22)
at EventEmitter._tickCallback (node.js:175:26)
Working up, through the stacktrace, I start with tls.js:654, which is a
destroy
of the secure pair:The
SecurePair.prototype.destroy = function() {
var self = this;
if (!this._doneFlag) {
this._doneFlag = true;
//..
process.nextTick(function() {
self.encrypted.emit('close');
self.cleartext.emit('close');
});
}
};
process.nextTick()
is just a node equivalent of setTimeout(fn, 0)
. It does have the effect of disassociating the event from the code. The stacktrace that I get indicates that the error is just one of many callbacks being invoked at the nextTick of the reactor loop. Given what I saw the other night, it looks as though the renegotiation of the SSL handshake is causing the secure pair of streams in node to be destroyed and rebuilt multiple times. The end result being the crash with stacktrace. My solution the other night was to punt. Specifically, I added a
process.on
handler:// Don't crash on errorsTo come up with a better solution, I need to understand the rest of the stacktrace. At the top,
process.on('uncaughtException', function (err) {
console.log('Caught uncaughtException: ' + err.stack);
});
stream.js:92
is, not surprisingly, a destroy()
call:function onclose() {But where does that
//...
dest.destroy();
}
dest
object get defined? It is not passed into onclose
. Rather, it is passed into the pipe()
method:Stream.prototype.pipe = function(dest, options) {Ah, that then, is the explanation. The node-spdy parser is piped to the connecton stream in spdy/core:
//...
function onclose() {
//...
dest.destroy();
}
}
var Server = core.Server = function(options, requestListener) {I could not quite bring myself to add a
//...
tls.Server.call(this, options, function(c) {
//...
var parser = createParser(c.zlib)
c.pipe(parser);
//...
destroy()
to the Parser class the other night without understanding why I was doing so. Now I know. If you are going to pipe()
stuff to a thing, that thing simply must support it.So I add a NOP
destroy()
function to Parser and call it a night:/**
* End of stream
*/
Parser.prototype.end = function() {};
Parser.prototype.destroy = function() {};
Day #45
No comments:
Post a Comment