Created
September 7, 2012 20:57
-
-
Save Jimbly/3669555 to your computer and use it in GitHub Desktop.
Test case for getting a dead socket because of node timing issues.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var assert = require('assert'); | |
var http = require('http'); | |
var agent = new http.Agent({maxSockets: 1}); | |
var headers = {'connection': 'keep-alive'}; | |
var PORT = 8080; | |
var responses = 0; | |
var errors = 0; | |
setTimeout(function() { | |
function xticks(ticks, f) { | |
if (ticks === 0) { | |
f(); | |
} else { | |
process.nextTick(xticks.bind(undefined, ticks - 1, f)); | |
} | |
} | |
// Make the onSocket .nextTick take more than a single tick | |
var proto = http.ClientRequest.prototype; | |
proto.orig_onSocket = proto.onSocket; | |
proto.onSocket = function(socket) { | |
var req = this; | |
var ticks = 4; | |
// <=1 does not get a destroyed socket | |
// 2 gets a destroyed socket and two closes if we emit one, works fine if we do not emit one | |
// >=3 gets a destroyed socket, no node-level close emit, so this works if emitting close | |
xticks(ticks, req.orig_onSocket.bind(req, socket)); | |
}; | |
function doTry() { | |
var req = http.get({ | |
path: '/', headers: headers, port: PORT, agent: agent | |
}, function(response) { | |
++responses; | |
var start = Date.now(); | |
// sleep 200ms without allowing events to process | |
while (Date.now() - start < 200) { | |
} | |
}); | |
req.on('socket', function(socket) { | |
if (socket.destroyed) { | |
console.log('Got DESTROYED socket'); | |
if (!'manual fix') { | |
// We listen for a close event, and if in a few ticks we have not received one, then we emit one | |
var emitted = false; | |
socket.on('close', function() { | |
emitted = true; | |
}); | |
xticks(5, function() { | |
if (!emitted) { | |
socket.emit('close'); | |
} | |
}); | |
} | |
} else { | |
console.log('Got socket'); | |
} | |
}); | |
req.on('error', function(err) { | |
++errors; | |
if (responses === 1 && errors === 1) { | |
console.log('Got expected error: ' + err + ', retrying...'); | |
doTry(); | |
} else { | |
console.log('Got UNEXPECTED error: '); | |
throw err; | |
} | |
}); | |
} | |
for (var ii = 0; ii < 2; ++ii) { | |
doTry(); | |
} | |
process.on('exit', function() { | |
assert.equal(responses, 2); | |
assert.equal(errors, 1); | |
}); | |
}, 200); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var assert = require('assert'); | |
var http = require('http'); | |
var body = 'hello world\n'; | |
var PORT = 8080; | |
var requests = 0; | |
var timeout_id; | |
var server = http.createServer(function(req, res) { | |
var socket = res.socket; | |
res.writeHead(200, {'Content-Length': body.length}); | |
res.write(body); | |
res.end(); | |
++requests; | |
setTimeout(function() { | |
socket.destroy(); | |
if (requests === 2) { | |
clearTimeout(timeout_id); | |
server.close(); | |
} | |
}, 100); | |
}); | |
server.listen(PORT, function() {}); | |
process.on('exit', function() { | |
assert.equal(requests, 2); | |
}); | |
timeout_id = setTimeout(function() { | |
process.exit(); | |
}, 3000); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This forces a reproduction of the issue reported at https://groups.google.com/forum/?fromgroups=#!topic/nodejs/k-qltQkBBnw
This is done by manually adding additional delays to the "onSocket" event handler, to effectively emulate what would have happened if the socket was actually disconnected N ticks before that event. This is not perfectly accurate since, were it actually disconnected a few ticks ago, other code may have cleaned up the socket and not passed it on, but it is effective enough to test my fix here: Jimbly/node@ea583f6
This can also be worked around with the code in the "if (!'manual fix') {" block, but you would need to add that to every place you create an HTTP Request.
Run the client and server simultaneously:
node test-http-deadsocket-server.js & node est-http-deadsocket-client.js
To test various conditions, modify "ticks" in est-http-deadsocket-client.js to values between 1 and 4.