When tracking requests of a HTTP-Server - with server.on('request') and res.on('finish', ...) or res.on('close', ...) - we noticed inconsistent results over time (requests that are never finished or closed).
We tracked this race condition down to the following reproducable test case:
- connection is aborted from client side
- socket gets destroyed (but socket-"close"-event still not delivered)
- trying to send response
- socket-"close" event delivered
-> No "close" and no "finish" event on response!
varcommon=require('../common');varassert=require('assert');varhttp=require('http');varclientRequest=null;varserverResponseFinishedOrClosed=0;varserver=http.createServer(function(req,res){console.log('server: request');res.on('finish',function(){console.log('server: response finish');serverResponseFinishedOrClosed++;});res.on('close',function(){console.log('server: response close');serverResponseFinishedOrClosed++;});console.log('client: aborting request');clientRequest.abort();setImmediate(function(){console.log('server: tick 1'+(req.connection.destroyed ? ' (connection destroyed!)' : ''));setImmediate(function(){console.log('server: tick 2'+(req.connection.destroyed ? ' (connection destroyed!)' : ''));console.log('server: sending response');res.writeHead(200,{'Content-Type': 'text/plain'});res.end('Response\n');console.log('server: res.end() returned');setImmediate(function(){server.close();});});});});server.on('listening',function(){console.log('server: listening on port '+common.PORT);console.log('client: starting request');varoptions={port: common.PORT,path: '/'};clientRequest=http.get(options,function(){});clientRequest.on('error',function(){});});server.on('connection',function(connection){console.log('server: connection');connection.on('close',function(){console.log('server: connection close');});});server.on('close',function(){console.log('server: close');assert.equal(serverResponseFinishedOrClosed,1,'Expected either one "finish" or one "close" event on the response for aborted connections (got '+serverResponseFinishedOrClosed+')');});server.listen(common.PORT);- With one more
setImmediate() call, you get a response close event (which is fine) - With one less
setImmediate() call, you get a response finish event (which is fine) - Reproducable with io.js 1.6.3, io.js 1.3.0, io.js 1.1.0, node 0.12.0
- In node 0.10 the race-condition was different: This test case works fine, but with one more
setImmediate(), two events are delivered (finish AND close)
When tracking requests of a HTTP-Server - with
server.on('request')andres.on('finish', ...)orres.on('close', ...)- we noticed inconsistent results over time (requests that are never finished or closed).We tracked this race condition down to the following reproducable test case:
-> No "close" and no "finish" event on response!
setImmediate()call, you get a response close event (which is fine)setImmediate()call, you get a response finish event (which is fine)setImmediate(), two events are delivered (finish AND close)