fix(platform-server): Resolve absolute URL from baseUrl (#39334)
This commit fixes a bug when `useAbsoluteUrl` is set to true and `ServerPlatformLocation` infers the base url from the supplied `url`. User should explicitly set the `baseUrl` when they turn on `useAbsoluteUrl`. Breaking change: If you use `useAbsoluteUrl` to setup `platform-server`, you now need to also specify `baseUrl`. We are intentionally making this a breaking change in a minor release, because if `useAbsoluteUrl` is set to `true` then the behavior of the application could be unpredictable, resulting in issues that are hard to discover but could be affecting production environments. PR Close #39334
This commit is contained in:
parent
d33eaa64a2
commit
7768aeb62f
|
@ -3,6 +3,7 @@ export declare const BEFORE_APP_SERIALIZED: InjectionToken<(() => void | Promise
|
||||||
export declare const INITIAL_CONFIG: InjectionToken<PlatformConfig>;
|
export declare const INITIAL_CONFIG: InjectionToken<PlatformConfig>;
|
||||||
|
|
||||||
export declare interface PlatformConfig {
|
export declare interface PlatformConfig {
|
||||||
|
baseUrl?: string;
|
||||||
document?: string;
|
document?: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
useAbsoluteUrl?: boolean;
|
useAbsoluteUrl?: boolean;
|
||||||
|
|
|
@ -12,7 +12,6 @@ import {Subject} from 'rxjs';
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
import {INITIAL_CONFIG, PlatformConfig} from './tokens';
|
import {INITIAL_CONFIG, PlatformConfig} from './tokens';
|
||||||
|
|
||||||
|
|
||||||
function parseUrl(urlStr: string) {
|
function parseUrl(urlStr: string) {
|
||||||
const parsedUrl = url.parse(urlStr);
|
const parsedUrl = url.parse(urlStr);
|
||||||
return {
|
return {
|
||||||
|
@ -43,16 +42,28 @@ export class ServerPlatformLocation implements PlatformLocation {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DOCUMENT) private _doc: any, @Optional() @Inject(INITIAL_CONFIG) _config: any) {
|
@Inject(DOCUMENT) private _doc: any, @Optional() @Inject(INITIAL_CONFIG) _config: any) {
|
||||||
const config = _config as PlatformConfig | null;
|
const config = _config as PlatformConfig | null;
|
||||||
if (!!config && !!config.url) {
|
if (!config) {
|
||||||
const parsedUrl = parseUrl(config.url);
|
return;
|
||||||
this.hostname = parsedUrl.hostname;
|
}
|
||||||
this.protocol = parsedUrl.protocol;
|
if (config.url) {
|
||||||
this.port = parsedUrl.port;
|
const url = parseUrl(config.url);
|
||||||
this.pathname = parsedUrl.pathname;
|
this.protocol = url.protocol;
|
||||||
this.search = parsedUrl.search;
|
this.hostname = url.hostname;
|
||||||
this.hash = parsedUrl.hash;
|
this.port = url.port;
|
||||||
|
this.pathname = url.pathname;
|
||||||
|
this.search = url.search;
|
||||||
|
this.hash = url.hash;
|
||||||
this.href = _doc.location.href;
|
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 {
|
getBaseHrefFromDOM(): string {
|
||||||
|
|
|
@ -20,19 +20,26 @@ export interface PlatformConfig {
|
||||||
*/
|
*/
|
||||||
document?: string;
|
document?: string;
|
||||||
/**
|
/**
|
||||||
* The URL for the current application state. This is
|
* The URL for the current application state. This is used for initializing
|
||||||
* used for initializing the platform's location and
|
* the platform's location. `protocol`, `hostname`, and `port` will be
|
||||||
* for setting absolute URL resolution for HTTP requests.
|
* overridden if `baseUrl` is set.
|
||||||
* @default none
|
* @default none
|
||||||
*/
|
*/
|
||||||
url?: string;
|
url?: string;
|
||||||
/**
|
/**
|
||||||
* Whether to append the absolute URL to any relative HTTP
|
* Whether to append the absolute URL to any relative HTTP requests. If set to
|
||||||
* requests. If set to true, this logic executes prior to
|
* true, this logic executes prior to any HTTP interceptors that may run later
|
||||||
* any HTTP interceptors that may run later on in the request.
|
* on in the request. `baseUrl` must be supplied if this flag is enabled.
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
useAbsoluteUrl?: boolean;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -794,10 +794,62 @@ describe('platform-server integration', () => {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('relative requests', () => {
|
describe('relative requests', () => {
|
||||||
|
it('will throw if "useAbsoluteUrl" is true but "baseUrl" is not provided', async () => {
|
||||||
|
const platform = platformDynamicServer([{
|
||||||
|
provide: INITIAL_CONFIG,
|
||||||
|
useValue: {
|
||||||
|
document: '<app></app>',
|
||||||
|
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: '<app></app>',
|
||||||
|
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: '<app></app>',
|
||||||
|
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 () => {
|
it('correctly maps to absolute URL request with base config', async () => {
|
||||||
const platform = platformDynamicServer([{
|
const platform = platformDynamicServer([{
|
||||||
provide: INITIAL_CONFIG,
|
provide: INITIAL_CONFIG,
|
||||||
useValue: {document: '<app></app>', url: 'http://localhost', useAbsoluteUrl: true}
|
useValue: {
|
||||||
|
document: '<app></app>',
|
||||||
|
url: 'http://localhost',
|
||||||
|
baseUrl: 'http://localhost',
|
||||||
|
useAbsoluteUrl: true,
|
||||||
|
}
|
||||||
}]);
|
}]);
|
||||||
const ref = await platform.bootstrapModule(HttpClientExampleModule);
|
const ref = await platform.bootstrapModule(HttpClientExampleModule);
|
||||||
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
|
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 () => {
|
it('correctly maps to absolute URL request with port', async () => {
|
||||||
const platform = platformDynamicServer([{
|
const platform = platformDynamicServer([{
|
||||||
provide: INITIAL_CONFIG,
|
provide: INITIAL_CONFIG,
|
||||||
useValue: {document: '<app></app>', url: 'http://localhost:5000', useAbsoluteUrl: true}
|
useValue: {
|
||||||
|
document: '<app></app>',
|
||||||
|
url: 'http://localhost:5000',
|
||||||
|
baseUrl: 'http://localhost',
|
||||||
|
useAbsoluteUrl: true,
|
||||||
|
}
|
||||||
}]);
|
}]);
|
||||||
const ref = await platform.bootstrapModule(HttpClientExampleModule);
|
const ref = await platform.bootstrapModule(HttpClientExampleModule);
|
||||||
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
|
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 () => {
|
it('correctly maps to absolute URL request with two slashes', async () => {
|
||||||
const platform = platformDynamicServer([{
|
const platform = platformDynamicServer([{
|
||||||
provide: INITIAL_CONFIG,
|
provide: INITIAL_CONFIG,
|
||||||
useValue: {document: '<app></app>', url: 'http://localhost/', useAbsoluteUrl: true}
|
useValue: {
|
||||||
|
document: '<app></app>',
|
||||||
|
url: 'http://localhost/',
|
||||||
|
baseUrl: 'http://localhost',
|
||||||
|
useAbsoluteUrl: true,
|
||||||
|
}
|
||||||
}]);
|
}]);
|
||||||
const ref = await platform.bootstrapModule(HttpClientExampleModule);
|
const ref = await platform.bootstrapModule(HttpClientExampleModule);
|
||||||
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
|
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 () => {
|
it('correctly maps to absolute URL request with no slashes', async () => {
|
||||||
const platform = platformDynamicServer([{
|
const platform = platformDynamicServer([{
|
||||||
provide: INITIAL_CONFIG,
|
provide: INITIAL_CONFIG,
|
||||||
useValue: {document: '<app></app>', url: 'http://localhost', useAbsoluteUrl: true}
|
useValue: {
|
||||||
|
document: '<app></app>',
|
||||||
|
url: 'http://localhost',
|
||||||
|
baseUrl: 'http://localhost',
|
||||||
|
useAbsoluteUrl: true,
|
||||||
|
}
|
||||||
}]);
|
}]);
|
||||||
const ref = await platform.bootstrapModule(HttpClientExampleModule);
|
const ref = await platform.bootstrapModule(HttpClientExampleModule);
|
||||||
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
|
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 () => {
|
it('correctly maps to absolute URL request with longer url and no slashes', async () => {
|
||||||
const platform = platformDynamicServer([{
|
const platform = platformDynamicServer([{
|
||||||
provide: INITIAL_CONFIG,
|
provide: INITIAL_CONFIG,
|
||||||
useValue:
|
useValue: {
|
||||||
{document: '<app></app>', url: 'http://localhost/path/page', useAbsoluteUrl: true}
|
document: '<app></app>',
|
||||||
|
url: 'http://localhost/path/page',
|
||||||
|
baseUrl: 'http://localhost',
|
||||||
|
useAbsoluteUrl: true,
|
||||||
|
}
|
||||||
}]);
|
}]);
|
||||||
const ref = await platform.bootstrapModule(HttpClientExampleModule);
|
const ref = await platform.bootstrapModule(HttpClientExampleModule);
|
||||||
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
|
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 () => {
|
it('correctly maps to absolute URL request with longer url and slashes', async () => {
|
||||||
const platform = platformDynamicServer([{
|
const platform = platformDynamicServer([{
|
||||||
provide: INITIAL_CONFIG,
|
provide: INITIAL_CONFIG,
|
||||||
useValue:
|
useValue: {
|
||||||
{document: '<app></app>', url: 'http://localhost/path/page', useAbsoluteUrl: true}
|
document: '<app></app>',
|
||||||
|
url: 'http://localhost/path/page',
|
||||||
|
baseUrl: 'http://localhost',
|
||||||
|
useAbsoluteUrl: true,
|
||||||
|
}
|
||||||
}]);
|
}]);
|
||||||
const ref = await platform.bootstrapModule(HttpClientExampleModule);
|
const ref = await platform.bootstrapModule(HttpClientExampleModule);
|
||||||
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
|
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
|
||||||
|
@ -922,7 +997,8 @@ describe('platform-server integration', () => {
|
||||||
useValue: {
|
useValue: {
|
||||||
document: '<base href="http://other"><app></app>',
|
document: '<base href="http://other"><app></app>',
|
||||||
url: 'http://localhost/path/page',
|
url: 'http://localhost/path/page',
|
||||||
useAbsoluteUrl: true
|
baseUrl: 'http://localhost',
|
||||||
|
useAbsoluteUrl: true,
|
||||||
}
|
}
|
||||||
}]);
|
}]);
|
||||||
const ref = await platform.bootstrapModule(HttpClientExampleModule);
|
const ref = await platform.bootstrapModule(HttpClientExampleModule);
|
||||||
|
|
Loading…
Reference in New Issue