Skip to content

Commit b692f45

Browse files
committed
fix(rest): tidy up host/port parsing and client url building
1 parent b9d0aab commit b692f45

File tree

2 files changed

+65
-17
lines changed

2 files changed

+65
-17
lines changed

packages/rest/src/rest.server.ts

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,20 @@ export class RestServer extends Context implements Server, HttpServerLike {
402402
'http'
403403
);
404404
}
405+
406+
/**
407+
* Parse the host:port string into an object for host and port
408+
* @param host The host string
409+
*/
410+
private _parseHostAndPort(host: string | undefined) {
411+
host = host || '';
412+
host = host.split(',')[0];
413+
const portPattern = /:([0-9]+)$/;
414+
const port = (host.match(portPattern) || [])[1] || '';
415+
host = host.replace(portPattern, '');
416+
return {host, port};
417+
}
418+
405419
/**
406420
* Get the URL of the request sent by the client
407421
* @param request Http request
@@ -413,14 +427,18 @@ export class RestServer extends Context implements Server, HttpServerLike {
413427
// [::1]
414428
// 127.0.0.1:3000
415429
// 127.0.0.1
416-
let host =
417-
(request.get('x-forwarded-host') || '').split(',')[0] ||
418-
request.headers.host!.replace(/:[0-9]+$/, '');
419-
let port =
420-
(request.get('x-forwarded-port') || '').split(',')[0] ||
421-
this.config.port ||
422-
(request.headers.host!.match(/:([0-9]+)$/) || [])[1] ||
423-
'';
430+
let {host, port} = this._parseHostAndPort(
431+
request.get('x-forwarded-host') || request.headers.host,
432+
);
433+
434+
const forwardedPort = (request.get('x-forwarded-port') || '').split(',')[0];
435+
port = forwardedPort || port;
436+
437+
if (!host) {
438+
// No host detected from http headers. Use the configured values
439+
host = this.config.host!;
440+
port = this.config.port == null ? '' : this.config.port.toString();
441+
}
424442

425443
// clear default ports
426444
port = protocol === 'https' && port === '443' ? '' : port;

packages/rest/test/integration/rest.server.integration.ts

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -391,15 +391,6 @@ paths:
391391
const app = new Application();
392392
app.component(RestComponent);
393393
const server = await app.getServer(RestServer);
394-
const greetSpec = {
395-
responses: {
396-
200: {
397-
schema: {type: 'string'},
398-
description: 'greeting of the day',
399-
},
400-
},
401-
};
402-
server.route(new Route('get', '/greet', greetSpec, function greet() {}));
403394

404395
const response = await createClientForHandler(server.requestHandler)
405396
.get('/swagger-ui')
@@ -416,6 +407,45 @@ paths:
416407
expect(response.get('Location')).match(expectedUrl);
417408
});
418409

410+
it('honors "x-forwarded-host" headers', async () => {
411+
const app = new Application();
412+
app.component(RestComponent);
413+
const server = await app.getServer(RestServer);
414+
415+
const response = await createClientForHandler(server.requestHandler)
416+
.get('/swagger-ui')
417+
.set('x-forwarded-proto', 'http')
418+
.set('x-forwarded-host', 'example.com:8080,my.example.com:9080');
419+
await server.get(RestBindings.PORT);
420+
const expectedUrl = new RegExp(
421+
[
422+
'https://loopback.io/api-explorer',
423+
'\\?url=http://example.com:8080/openapi.json',
424+
].join(''),
425+
);
426+
expect(response.get('Location')).match(expectedUrl);
427+
});
428+
429+
it('skips port if it is the default for http or https', async () => {
430+
const app = new Application();
431+
app.component(RestComponent);
432+
const server = await app.getServer(RestServer);
433+
434+
const response = await createClientForHandler(server.requestHandler)
435+
.get('/swagger-ui')
436+
.set('x-forwarded-proto', 'https')
437+
.set('x-forwarded-host', 'example.com')
438+
.set('x-forwarded-port', '443');
439+
await server.get(RestBindings.PORT);
440+
const expectedUrl = new RegExp(
441+
[
442+
'https://loopback.io/api-explorer',
443+
'\\?url=https://example.com/openapi.json',
444+
].join(''),
445+
);
446+
expect(response.get('Location')).match(expectedUrl);
447+
});
448+
419449
it('exposes "GET /swagger-ui" endpoint with apiExplorer.url', async () => {
420450
const server = await givenAServer({
421451
rest: {

0 commit comments

Comments
 (0)