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 interface PlatformConfig {
|
||||
baseUrl?: string;
|
||||
document?: string;
|
||||
url?: string;
|
||||
useAbsoluteUrl?: boolean;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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: '<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 () => {
|
||||
const platform = platformDynamicServer([{
|
||||
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 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: '<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 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: '<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 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: '<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 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: '<app></app>', url: 'http://localhost/path/page', useAbsoluteUrl: true}
|
||||
useValue: {
|
||||
document: '<app></app>',
|
||||
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: '<app></app>', url: 'http://localhost/path/page', useAbsoluteUrl: true}
|
||||
useValue: {
|
||||
document: '<app></app>',
|
||||
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: '<base href="http://other"><app></app>',
|
||||
url: 'http://localhost/path/page',
|
||||
useAbsoluteUrl: true
|
||||
baseUrl: 'http://localhost',
|
||||
useAbsoluteUrl: true,
|
||||
}
|
||||
}]);
|
||||
const ref = await platform.bootstrapModule(HttpClientExampleModule);
|
||||
|
|
Loading…
Reference in New Issue