Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions test/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use strict';

const ChildProcess = require('child_process');

const internals = {};

internals.hasLsof = () => {

try {
ChildProcess.execSync(`lsof -p ${process.pid}`, { stdio: 'ignore' });
}
catch (err) {
return false;
}

return true;
};

exports.hasLsof = internals.hasLsof();
10 changes: 2 additions & 8 deletions test/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const Stream = require('stream');
const TLS = require('tls');

const Boom = require('@hapi/boom');
const Bounce = require('@hapi/bounce');
const CatboxMemory = require('@hapi/catbox-memory');
const Code = require('@hapi/code');
const Handlebars = require('handlebars');
Expand All @@ -22,6 +21,7 @@ const Lab = require('@hapi/lab');
const Vision = require('@hapi/vision');
const Wreck = require('@hapi/wreck');

const Common = require('./common');

const internals = {};

Expand Down Expand Up @@ -1679,7 +1679,7 @@ describe('Core', () => {
expect(res.result.isBoom).to.equal(true);
});

it('cleans unused file stream when response is overridden', { skip: process.platform === 'win32' }, async () => {
it('cleans unused file stream when response is overridden', { skip: !Common.hasLsof }, async () => {

const server = Hapi.server();
await server.register(Inert);
Expand All @@ -1702,12 +1702,6 @@ describe('Core', () => {
const cmd = ChildProcess.spawn('lsof', ['-p', process.pid]);
let lsof = '';

cmd.on('error', (err) => {

// Allow the test to pass on platforms with no lsof
Bounce.ignore(err, { errno: 'ENOENT' });
});

cmd.stdout.on('data', (buffer) => {

lsof += buffer.toString();
Expand Down
115 changes: 69 additions & 46 deletions test/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const Http = require('http');
const Net = require('net');
const Stream = require('stream');
const Url = require('url');
const Events = require('events');

const Boom = require('@hapi/boom');
const Code = require('@hapi/code');
Expand Down Expand Up @@ -275,45 +276,57 @@ describe('Request', () => {

describe('active()', () => {

it('exits handler early when request is no longer active', async () => {
it('exits handler early when request is no longer active', { retry: true }, async (flags) => {

let testComplete = false;

const onCleanup = [];
flags.onCleanup = async () => {

testComplete = true;

for (const cleanup of onCleanup) {
await cleanup();
}
};

const server = Hapi.server();
const team = new Teamwork.Team();
const team2 = new Teamwork.Team();
const leaveHandlerTeam = new Teamwork.Team();

let rounds = 0;
server.route({
method: 'GET',
path: '/',
options: {
handler: async (request, h) => {

team2.attend();
for (let i = 0; i < 100; ++i) {
++rounds;
await Hoek.wait(10);
req.abort();

if (!request.active()) {
break;
}
while (request.active() && !testComplete) {
await Hoek.wait(10);
}

team.attend();
leaveHandlerTeam.attend({
active: request.active(),
testComplete
});

return null;
}
}
});

await server.start();
onCleanup.unshift(() => server.stop());

const req = Http.get(server.info.uri, (res) => { });
const req = Http.get(server.info.uri, Hoek.ignore);
req.on('error', Hoek.ignore);
await team2.work;
req.abort();
await server.stop();

await team.work;
expect(rounds).to.be.below(10);
const note = await leaveHandlerTeam.work;

expect(note).to.equal({
active: false,
testComplete: false
});
});
});

Expand Down Expand Up @@ -847,10 +860,10 @@ describe('Request', () => {

describe('_postCycle()', () => {

it('skips onPreResponse when validation terminates request', async () => {
it('skips onPreResponse when validation terminates request', { retry: true }, async (flags) => {

const server = Hapi.server();
const team = new Teamwork.Team();
const abortedReqTeam = new Teamwork.Team();

let called = false;
server.ext('onPreResponse', (request, h) => {
Expand All @@ -863,28 +876,38 @@ describe('Request', () => {
method: 'GET',
path: '/',
options: {
handler: () => null,
handler: (request) => {

// Stash raw so that we can access it on response validation
Object.assign(request.app, request.raw);

return null;
},
response: {
status: {
200: async () => {
200: async (_, { context }) => {

req.abort();
await Hoek.wait(10);
team.attend();

const raw = context.app.request;
await Events.once(raw.req, 'aborted');

abortedReqTeam.attend();
}
}
}
}
});

await server.start();
flags.onCleanup = () => server.stop();

const req = Http.get(server.info.uri, (res) => { });
const req = Http.get(server.info.uri, Hoek.ignore);
req.on('error', Hoek.ignore);

await team.work;
await Hoek.wait(100);
await server.stop();
await abortedReqTeam.work;

await server.events.once('response');

expect(called).to.be.false();
});
Expand Down Expand Up @@ -2279,41 +2302,41 @@ describe('Request', () => {
expect(res.statusCode).to.equal(200);
});

it('handles race condition between equal client and server timeouts', async () => {
it('handles race condition between equal client and server timeouts', async (flags) => {

const onCleanup = [];
flags.onCleanup = async () => {

for (const cleanup of onCleanup) {
await cleanup();
}
};

const server = Hapi.server({ routes: { timeout: { server: 100 }, payload: { timeout: 100 } } });
server.route({ method: 'POST', path: '/timeout', options: { handler: Hoek.block } });

await server.start();
onCleanup.unshift(() => server.stop());

const timer = new Hoek.Bench();
const options = {
hostname: '127.0.0.1',
hostname: 'localhost',
port: server.info.port,
path: '/timeout',
method: 'POST'
};

await new Promise(async (resolve) => {
const req = Http.request(options);
onCleanup.unshift(() => req.destroy());

const req = Http.request(options, (res) => {
req.write('\n');

expect([503, 408]).to.contain(res.statusCode);
expect(timer.elapsed()).to.be.at.least(80);
resolve();
});
const [res] = await Events.once(req, 'response');

req.on('error', (err) => {
expect([503, 408]).to.contain(res.statusCode);
expect(timer.elapsed()).to.be.at.least(80);

expect(err).to.not.exist();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some (flaky) cases this error was being triggered late and causing the next test to fail.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, lab does not bind the expect to a specific test, so it can only assume that errors are caused by the current test.

});

req.write('\n');
await Hoek.wait(200);
req.end();
});

await server.stop({ timeout: 1 });
await Events.once(req, 'close'); // Ensures that req closes without error
});
});

Expand Down
18 changes: 3 additions & 15 deletions test/transmit.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ const Stream = require('stream');
const Zlib = require('zlib');

const Boom = require('@hapi/boom');
const Bounce = require('@hapi/bounce');
const Code = require('@hapi/code');
const Hapi = require('..');
const Hoek = require('@hapi/hoek');
Expand All @@ -17,6 +16,7 @@ const Lab = require('@hapi/lab');
const Teamwork = require('@hapi/teamwork');
const Wreck = require('@hapi/wreck');

const Common = require('./common');

const internals = {};

Expand Down Expand Up @@ -86,7 +86,7 @@ describe('transmission', () => {
expect(res.statusCode).to.equal(200);
});

it('closes file handlers when not reading file stream', { skip: process.platform === 'win32' }, async () => {
it('closes file handlers when not reading file stream', { skip: !Common.hasLsof }, async () => {

const server = Hapi.server();
await server.register(Inert);
Expand All @@ -101,12 +101,6 @@ describe('transmission', () => {
const cmd = ChildProcess.spawn('lsof', ['-p', process.pid]);
let lsof = '';

cmd.on('error', (err) => {

// Allow the test to pass on platforms with no lsof
Bounce.ignore(err, { errno: 'ENOENT' });
});

cmd.stdout.on('data', (buffer) => {

lsof += buffer.toString();
Expand All @@ -128,7 +122,7 @@ describe('transmission', () => {
});
});

it('closes file handlers when not using a manually open file stream', { skip: process.platform === 'win32' }, async () => {
it('closes file handlers when not using a manually open file stream', { skip: !Common.hasLsof }, async () => {

const server = Hapi.server();
server.route({ method: 'GET', path: '/file', handler: (request, h) => h.response(Fs.createReadStream(__dirname + '/../package.json')).header('etag', 'abc') });
Expand All @@ -142,12 +136,6 @@ describe('transmission', () => {
const cmd = ChildProcess.spawn('lsof', ['-p', process.pid]);
let lsof = '';

cmd.on('error', (err) => {

// Allow the test to pass on platforms with no lsof
Bounce.ignore(err, { errno: 'ENOENT' });
});

cmd.stdout.on('data', (buffer) => {

lsof += buffer.toString();
Expand Down