Skip to content

Commit

Permalink
✨ Added support to set network interface to bind (#1447)
Browse files Browse the repository at this point in the history
* Added custom binding support

* Fixed tests

* Fixed for windows
  • Loading branch information
ninadbstack authored Nov 30, 2023
1 parent 9ea4b1f commit f92b871
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 5 deletions.
13 changes: 11 additions & 2 deletions packages/core/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ export class Server extends http.Server {
});
}

// return host bind address - defaults to 0.0.0.0
get host() {
return process.env.PERCY_SERVER_HOST || '0.0.0.0';
}

// return the listening port or any default port
get port() {
return super.address()?.port ?? this.#defaultPort;
Expand All @@ -122,7 +127,11 @@ export class Server extends http.Server {
// return a string representation of the server address
address() {
let port = this.port;
let host = 'http://localhost';
// we need to specifically map 0.0.0.0 to localhost on windows as even though we
// can listen to all interfaces using 0.0.0.0 we cant make a request on 0.0.0.0 as
// its an invalid ip address as per spec, but unix systems allow request to it and
// falls back to localhost
let host = `http://${this.host === '0.0.0.0' ? 'localhost' : this.host}`;
return port ? `${host}:${port}` : host;
}

Expand All @@ -131,7 +140,7 @@ export class Server extends http.Server {
return new Promise((resolve, reject) => {
let handle = err => off() && err ? reject(err) : resolve(this);
let off = () => this.off('error', handle).off('listening', handle);
super.listen(port, handle).once('error', handle);
super.listen(port, this.host, handle).once('error', handle);
});
}

Expand Down
11 changes: 8 additions & 3 deletions packages/core/test/discovery.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1493,7 +1493,7 @@ describe('Discovery', () => {

server2.reply('/img.gif', () => new Promise(resolve => {
// should not resolve within the test timeout
setTimeout(resolve, 10000, [200, 'image/gif', pixel]);
setTimeout(resolve, jasmine.DEFAULT_TIMEOUT_INTERVAL + 5000, [200, 'image/gif', pixel]);
}));

await percy.snapshot({
Expand All @@ -1507,8 +1507,13 @@ describe('Discovery', () => {

await percy.idle();

let paths2 = server2.requests.map(r => r[0]);
expect(paths2).toContain('/img.gif');
expect(captured[0]).not.toContain(
jasmine.objectContaining({
attributes: jasmine.objectContaining({
'resource-url': 'http://ex.localhost:8001/img.gif'
})
})
);
});

it('captures remote resources from allowed hostnames', async () => {
Expand Down
67 changes: 67 additions & 0 deletions packages/core/test/unit/server.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,26 @@ describe('Unit / Server', () => {
await new Promise(r => setImmediate(setImmediate, r));
});

describe('#host', () => {
it('returns the host', async () => {
expect(server.host).toEqual('0.0.0.0');
});

describe('with PERCY_SERVER_HOST set', () => {
beforeEach(() => {
process.env.PERCY_SERVER_HOST = 'localhost';
});

afterEach(() => {
delete process.env.PERCY_SERVER_HOST;
});

it('returns correct host', async () => {
expect(server.host).toEqual('localhost');
});
});
});

describe('#port', () => {
it('returns the provided default port when not listening', () => {
expect(server.port).toEqual(8000);
Expand All @@ -33,12 +53,39 @@ describe('Unit / Server', () => {

describe('#address()', () => {
it('returns the localhost address for the server', () => {
// converts default 0.0.0.0 to localhost
expect(server.address()).toEqual('http://localhost:8000');
});

it('does not include the port without a default when not listening', () => {
expect(Server.createServer().address()).toEqual('http://localhost');
});

describe('with PERCY_SERVER_HOST set', () => {
afterEach(() => {
delete process.env.PERCY_SERVER_HOST;
});

describe('when PERCY_SERVER_HOST=localhost', () => {
beforeEach(() => {
process.env.PERCY_SERVER_HOST = 'localhost';
});

it('it uses localhost correctly', () => {
expect(Server.createServer().address()).toEqual('http://localhost');
});
});

describe('when PERCY_SERVER_HOST=120.22.12.1', () => {
beforeEach(() => {
process.env.PERCY_SERVER_HOST = '120.22.12.1';
});

it('it uses 120.22.12.1 correctly', () => {
expect(Server.createServer().address()).toEqual('http://120.22.12.1');
});
});
});
});

describe('#listen([port])', () => {
Expand All @@ -60,6 +107,26 @@ describe('Unit / Server', () => {
Server.createServer().listen(server.port)
).toBeRejected();
});

describe('with PERCY_SERVER_HOST set', () => {
beforeEach(() => {
process.env.PERCY_SERVER_HOST = 'localhost';
});

afterEach(() => {
delete process.env.PERCY_SERVER_HOST;
});

it('listens on correct host', async () => {
expect(server.host).toEqual('localhost');
await server.listen();
server.route('get', '/test/:path', (req, res) => res.text(req.params.path));
await expectAsync(request('/test/foo', 'GET')).toBeResolvedTo('foo');

// as we have a single network interface locally its not easy to test a negative test
// where with a separate network interface we are unable to access server
});
});
});

describe('#close()', () => {
Expand Down

0 comments on commit f92b871

Please sign in to comment.