diff --git a/goldens/public-api/platform-server/platform-server.d.ts b/goldens/public-api/platform-server/platform-server.d.ts index 7db0b77e97..490599c709 100644 --- a/goldens/public-api/platform-server/platform-server.d.ts +++ b/goldens/public-api/platform-server/platform-server.d.ts @@ -3,6 +3,7 @@ export declare const BEFORE_APP_SERIALIZED: InjectionToken<(() => void | Promise export declare const INITIAL_CONFIG: InjectionToken; export declare interface PlatformConfig { + baseUrl?: string; document?: string; url?: string; useAbsoluteUrl?: boolean; diff --git a/packages/platform-server/src/location.ts b/packages/platform-server/src/location.ts index 60f42c793b..054e4f85ba 100644 --- a/packages/platform-server/src/location.ts +++ b/packages/platform-server/src/location.ts @@ -12,7 +12,6 @@ import {Subject} from 'rxjs'; import * as url from 'url'; import {INITIAL_CONFIG, PlatformConfig} from './tokens'; - function parseUrl(urlStr: string) { const parsedUrl = url.parse(urlStr); return { @@ -43,16 +42,28 @@ export class ServerPlatformLocation implements PlatformLocation { constructor( @Inject(DOCUMENT) private _doc: any, @Optional() @Inject(INITIAL_CONFIG) _config: any) { const config = _config as PlatformConfig | null; - if (!!config && !!config.url) { - const parsedUrl = parseUrl(config.url); - this.hostname = parsedUrl.hostname; - this.protocol = parsedUrl.protocol; - this.port = parsedUrl.port; - this.pathname = parsedUrl.pathname; - this.search = parsedUrl.search; - this.hash = parsedUrl.hash; + if (!config) { + return; + } + if (config.url) { + const url = parseUrl(config.url); + this.protocol = url.protocol; + this.hostname = url.hostname; + this.port = url.port; + this.pathname = url.pathname; + this.search = url.search; + this.hash = url.hash; this.href = _doc.location.href; } + if (config.useAbsoluteUrl) { + if (!config.baseUrl) { + throw new Error(`"PlatformConfig.baseUrl" must be set if "useAbsoluteUrl" is true`); + } + const url = parseUrl(config.baseUrl); + this.protocol = url.protocol; + this.hostname = url.hostname; + this.port = url.port; + } } getBaseHrefFromDOM(): string { diff --git a/packages/platform-server/src/tokens.ts b/packages/platform-server/src/tokens.ts index b5942985d1..7a937bc564 100644 --- a/packages/platform-server/src/tokens.ts +++ b/packages/platform-server/src/tokens.ts @@ -20,19 +20,26 @@ export interface PlatformConfig { */ document?: string; /** - * The URL for the current application state. This is - * used for initializing the platform's location and - * for setting absolute URL resolution for HTTP requests. + * The URL for the current application state. This is used for initializing + * the platform's location. `protocol`, `hostname`, and `port` will be + * overridden if `baseUrl` is set. * @default none */ url?: string; /** - * Whether to append the absolute URL to any relative HTTP - * requests. If set to true, this logic executes prior to - * any HTTP interceptors that may run later on in the request. + * Whether to append the absolute URL to any relative HTTP requests. If set to + * true, this logic executes prior to any HTTP interceptors that may run later + * on in the request. `baseUrl` must be supplied if this flag is enabled. * @default false */ useAbsoluteUrl?: boolean; + /** + * The base URL for resolving absolute URL for HTTP requests. It must be set + * if `useAbsoluteUrl` is true, and must consist of protocol, hostname, + * and optional port. This option has no effect if `useAbsoluteUrl` is not + * enabled. + */ + baseUrl?: string; } /** diff --git a/packages/platform-server/test/integration_spec.ts b/packages/platform-server/test/integration_spec.ts index d345ab4afb..34740d10ac 100644 --- a/packages/platform-server/test/integration_spec.ts +++ b/packages/platform-server/test/integration_spec.ts @@ -794,10 +794,62 @@ describe('platform-server integration', () => { })); describe('relative requests', () => { + it('will throw if "useAbsoluteUrl" is true but "baseUrl" is not provided', async () => { + const platform = platformDynamicServer([{ + provide: INITIAL_CONFIG, + useValue: { + document: '', + url: 'http://localhost', + useAbsoluteUrl: true, + }, + }]); + const appRef = await platform.bootstrapModule(HttpClientExampleModule); + expect(() => appRef.injector.get(PlatformLocation)) + .toThrowError(/"PlatformConfig\.baseUrl" must be set if "useAbsoluteUrl" is true/); + }); + + it('will resolve absolute url using "baseUrl"', async () => { + const platform = platformDynamicServer([{ + provide: INITIAL_CONFIG, + useValue: { + document: '', + url: 'http://localhost', + useAbsoluteUrl: true, + baseUrl: 'https://angular.io:8080', + }, + }]); + const appRef = await platform.bootstrapModule(HttpClientExampleModule); + const location = appRef.injector.get(PlatformLocation); + expect(location.protocol).toBe('https:'); + expect(location.hostname).toBe('angular.io'); + expect(location.port).toBe('8080'); + }); + + it('"baseUrl" has no effect if "useAbsoluteUrl" is not enabled', async () => { + const platform = platformDynamicServer([{ + provide: INITIAL_CONFIG, + useValue: { + document: '', + url: 'http://localhost', + baseUrl: 'https://angular.io:8080', + }, + }]); + const appRef = await platform.bootstrapModule(HttpClientExampleModule); + const location = appRef.injector.get(PlatformLocation); + expect(location.protocol).toBe('http:'); + expect(location.hostname).toBe('localhost'); + expect(location.port).toBe(''); + }); + it('correctly maps to absolute URL request with base config', async () => { const platform = platformDynamicServer([{ provide: INITIAL_CONFIG, - useValue: {document: '', url: 'http://localhost', useAbsoluteUrl: true} + useValue: { + document: '', + url: 'http://localhost', + baseUrl: 'http://localhost', + useAbsoluteUrl: true, + } }]); const ref = await platform.bootstrapModule(HttpClientExampleModule); const mock = ref.injector.get(HttpTestingController) as HttpTestingController; @@ -831,7 +883,12 @@ describe('platform-server integration', () => { it('correctly maps to absolute URL request with port', async () => { const platform = platformDynamicServer([{ provide: INITIAL_CONFIG, - useValue: {document: '', url: 'http://localhost:5000', useAbsoluteUrl: true} + useValue: { + document: '', + url: 'http://localhost:5000', + baseUrl: 'http://localhost', + useAbsoluteUrl: true, + } }]); const ref = await platform.bootstrapModule(HttpClientExampleModule); const mock = ref.injector.get(HttpTestingController) as HttpTestingController; @@ -848,7 +905,12 @@ describe('platform-server integration', () => { it('correctly maps to absolute URL request with two slashes', async () => { const platform = platformDynamicServer([{ provide: INITIAL_CONFIG, - useValue: {document: '', url: 'http://localhost/', useAbsoluteUrl: true} + useValue: { + document: '', + url: 'http://localhost/', + baseUrl: 'http://localhost', + useAbsoluteUrl: true, + } }]); const ref = await platform.bootstrapModule(HttpClientExampleModule); const mock = ref.injector.get(HttpTestingController) as HttpTestingController; @@ -865,7 +927,12 @@ describe('platform-server integration', () => { it('correctly maps to absolute URL request with no slashes', async () => { const platform = platformDynamicServer([{ provide: INITIAL_CONFIG, - useValue: {document: '', url: 'http://localhost', useAbsoluteUrl: true} + useValue: { + document: '', + url: 'http://localhost', + baseUrl: 'http://localhost', + useAbsoluteUrl: true, + } }]); const ref = await platform.bootstrapModule(HttpClientExampleModule); const mock = ref.injector.get(HttpTestingController) as HttpTestingController; @@ -882,8 +949,12 @@ describe('platform-server integration', () => { it('correctly maps to absolute URL request with longer url and no slashes', async () => { const platform = platformDynamicServer([{ provide: INITIAL_CONFIG, - useValue: - {document: '', url: 'http://localhost/path/page', useAbsoluteUrl: true} + useValue: { + document: '', + url: 'http://localhost/path/page', + baseUrl: 'http://localhost', + useAbsoluteUrl: true, + } }]); const ref = await platform.bootstrapModule(HttpClientExampleModule); const mock = ref.injector.get(HttpTestingController) as HttpTestingController; @@ -900,8 +971,12 @@ describe('platform-server integration', () => { it('correctly maps to absolute URL request with longer url and slashes', async () => { const platform = platformDynamicServer([{ provide: INITIAL_CONFIG, - useValue: - {document: '', url: 'http://localhost/path/page', useAbsoluteUrl: true} + useValue: { + document: '', + url: 'http://localhost/path/page', + baseUrl: 'http://localhost', + useAbsoluteUrl: true, + } }]); const ref = await platform.bootstrapModule(HttpClientExampleModule); const mock = ref.injector.get(HttpTestingController) as HttpTestingController; @@ -922,7 +997,8 @@ describe('platform-server integration', () => { useValue: { document: '', url: 'http://localhost/path/page', - useAbsoluteUrl: true + baseUrl: 'http://localhost', + useAbsoluteUrl: true, } }]); const ref = await platform.bootstrapModule(HttpClientExampleModule);