Skip to content

Commit

Permalink
Actively end idle persistent connections on server.stop()
Browse files Browse the repository at this point in the history
  • Loading branch information
kanongil committed Feb 29, 2016
1 parent 018d86c commit 68710da
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 12 deletions.
23 changes: 20 additions & 3 deletions lib/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,16 @@ internals.Connection.prototype._stop = function (options, callback) {
this._init();
return callback();
});

// Tell idle keep-alive connections to close

Object.keys(this._connections).forEach((key) => {

const connection = this._connections[key];
if (!connection._isHapiProcessing) {
connection.end();
}
});
};


Expand All @@ -218,10 +228,17 @@ internals.Connection.prototype._dispatch = function (options) {

return (req, res) => {

if (!this._started &&
!Shot.isInjection(req)) {
// Track socket request processing state

if (req.socket) {
req.socket._isHapiProcessing = true;
res.on('finish', () => {

return req.connection.end();
req.socket._isHapiProcessing = false;
if (!this._started) {
req.socket.end();
}
});
}

// Create request
Expand Down
7 changes: 6 additions & 1 deletion lib/transmit.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,11 @@ internals.transmit = function (response, callback) {
}
}

const isInjection = Shot.isInjection(request.raw.req);
if (!isInjection && !request.connection._started) {
request.raw.res.setHeader('connection', 'close');
}

request.raw.res.writeHead(response.statusCode);

// Write payload
Expand Down Expand Up @@ -350,7 +355,7 @@ internals.transmit = function (response, callback) {

// Injection

if (Shot.isInjection(request.raw.req)) {
if (isInjection) {
request.raw.res._hapi = {
request: request
};
Expand Down
64 changes: 56 additions & 8 deletions test/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ describe('Connection', () => {
});
});

it('waits to destroy unhandled connections until after the timeout', (done) => {
it('immediately destroys unhandled connections', (done) => {

const server = new Hapi.Server();
server.connection();
Expand Down Expand Up @@ -524,7 +524,7 @@ describe('Connection', () => {
server.stop({ timeout: 20 }, (err) => {

expect(err).to.not.exist();
expect(timer.elapsed()).to.be.at.least(19);
expect(timer.elapsed()).to.be.at.most(19);
done();
});
});
Expand Down Expand Up @@ -612,6 +612,52 @@ describe('Connection', () => {
});
});

it('immediately destroys idle keep-alive connections', (done) => {

const server = new Hapi.Server();
server.connection();

const handler = (request, reply) => {

return reply();
};

server.route({ method: 'GET', path: '/', handler: handler });

server.start((err) => {

expect(err).to.not.exist();

const socket = new Net.Socket();

socket.once('error', (err) => {

expect(err.errno).to.equal('ECONNRESET');
});

socket.connect(server.info.port, server.connections[0].settings.host, () => {

socket.write('GET / HTTP/1.1\nHost: test\nConnection: Keep-Alive\n\n\n');
socket.on('data', (data) => {

server.listener.getConnections((err, count) => {

expect(err).to.not.exist();
expect(count).to.be.greaterThan(0);
const timer = new Hoek.Bench();

server.stop({ timeout: 20 }, (err) => {

expect(err).to.not.exist();
expect(timer.elapsed()).to.be.at.most(19);
done();
});
});
});
});
});
});

it('refuses to handle new incoming requests on persistent connections', (done) => {

const handler = (request, reply) => {
Expand All @@ -635,6 +681,7 @@ describe('Connection', () => {

expect(err3).to.not.exist();
expect(err1).to.not.exist();
expect(res.headers.connection).to.equal('keep-alive');
expect(body.toString()).to.equal('ok');
expect(server.connections[0]._started).to.equal(false);
expect(err2).to.exist();
Expand All @@ -651,21 +698,19 @@ describe('Connection', () => {

it('finishes in-progress requests and ends connection', (done) => {

let err1;
let err1 = true;

const handler = (request, reply) => {

server.stop({ timeout: 200 }, (err) => {

expect(err).to.not.exist();
expect(err1).to.exist();
done();
err1 = err;
});

setImmediate(() => {

return reply('ok');
})
});
};

const server = new Hapi.Server();
Expand All @@ -680,12 +725,15 @@ describe('Connection', () => {
Wreck.get('http://localhost:' + server.info.port + '/', { agent: agent }, (err, res, body) => {

expect(err).to.not.exist();
expect(res.headers.connection).to.equal('close');
expect(body.toString()).to.equal('ok');
});

Wreck.get('http://localhost:' + server.info.port + '/404', { agent: agent }, (err, res, body) => {

err1 = err;
expect(err).to.exist();
expect(err1).to.not.exist();
done();
});
});
});
Expand Down
19 changes: 19 additions & 0 deletions test/transmit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2282,6 +2282,25 @@ describe('transmission', () => {
done();
});
});

it('does not add connection close header to normal requests', (done) => {

const server = new Hapi.Server();
server.connection();

const handler = function (request, reply) {

return reply('ok');
};

server.route({ method: 'GET', path: '/', handler: handler });
server.inject('/', (res) => {

expect(res.statusCode).to.equal(200);
expect(res.headers.connection).to.not.equal('close');
done();
});
});
});

describe('cache()', () => {
Expand Down

0 comments on commit 68710da

Please sign in to comment.