feat(platform-server): add option for absolute URL HTTP support (#37539)

In version 10.0.0-next.8, we introduced absolute URL support for
server-based HTTP requests, so long as the fully-resolved URL was
provided in the initial config. However, doing so represents a
breaking change for users who already have their own interceptors
to model this functionality, since our logic executes before all
interceptors fire on a request. See original PR #37071.

Therefore, we introduce a flag to make this change consistent with
v9 behavior, allowing users to opt in to this new behavior. This
commit also fixes two issues with the previous implementation:
1. if the server was initiated with a relative URL, the absolute
URL construction would fail because needed components were empty
2. if the user's absolute URL was on a port, the port would not
be included

PR Close #37539
This commit is contained in:
Adam
2020-06-11 11:42:31 -05:00
committed by Andrew Kushnir
parent d12cdb5019
commit d37049a2a2
4 changed files with 156 additions and 88 deletions

View File

@ -793,104 +793,149 @@ describe('platform-server integration', () => {
});
}));
it('can make relative HttpClient requests', async () => {
const platform = platformDynamicServer([
{provide: INITIAL_CONFIG, useValue: {document: '<app></app>', url: 'http://localhost'}}
]);
const ref = await platform.bootstrapModule(HttpClientExampleModule);
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get<string>('/testing').subscribe((body: string) => {
NgZone.assertInAngularZone();
expect(body).toEqual('success!');
describe('relative requests', () => {
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}
}]);
const ref = await platform.bootstrapModule(HttpClientExampleModule);
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get<string>('/testing').subscribe((body: string) => {
NgZone.assertInAngularZone();
expect(body).toEqual('success!');
});
mock.expectOne('http://localhost/testing').flush('success!');
});
mock.expectOne('http://localhost/testing').flush('success!');
});
});
it('can make relative HttpClient requests two slashes', async () => {
const platform = platformDynamicServer([
{provide: INITIAL_CONFIG, useValue: {document: '<app></app>', url: 'http://localhost/'}}
]);
const ref = await platform.bootstrapModule(HttpClientExampleModule);
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get<string>('/testing').subscribe((body: string) => {
NgZone.assertInAngularZone();
expect(body).toEqual('success!');
it('uses default URL behavior when not enabled', async () => {
const platform = platformDynamicServer([{
provide: INITIAL_CONFIG,
useValue: {document: '<app></app>', url: 'http://localhost', useAbsoluteUrl: false}
}]);
const ref = await platform.bootstrapModule(HttpClientExampleModule);
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get<string>('/testing').subscribe(() => {}, (body: string) => {
NgZone.assertInAngularZone();
expect(body).toEqual('error');
});
mock.expectOne('/testing').flush('error');
});
mock.expectOne('http://localhost/testing').flush('success!');
});
});
it('can make relative HttpClient requests no slashes', async () => {
const platform = platformDynamicServer([
{provide: INITIAL_CONFIG, useValue: {document: '<app></app>', url: 'http://localhost'}}
]);
const ref = await platform.bootstrapModule(HttpClientExampleModule);
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get<string>('testing').subscribe((body: string) => {
NgZone.assertInAngularZone();
expect(body).toEqual('success!');
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}
}]);
const ref = await platform.bootstrapModule(HttpClientExampleModule);
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get<string>('/testing').subscribe((body: string) => {
NgZone.assertInAngularZone();
expect(body).toEqual('success!');
});
mock.expectOne('http://localhost:5000/testing').flush('success!');
});
mock.expectOne('http://localhost/testing').flush('success!');
});
});
it('can make relative HttpClient requests no slashes longer url', async () => {
const platform = platformDynamicServer([{
provide: INITIAL_CONFIG,
useValue: {document: '<app></app>', url: 'http://localhost/path/page'}
}]);
const ref = await platform.bootstrapModule(HttpClientExampleModule);
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get<string>('testing').subscribe((body: string) => {
NgZone.assertInAngularZone();
expect(body).toEqual('success!');
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}
}]);
const ref = await platform.bootstrapModule(HttpClientExampleModule);
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get<string>('/testing').subscribe((body: string) => {
NgZone.assertInAngularZone();
expect(body).toEqual('success!');
});
mock.expectOne('http://localhost/testing').flush('success!');
});
mock.expectOne('http://localhost/path/testing').flush('success!');
});
});
it('can make relative HttpClient requests slashes longer url', async () => {
const platform = platformDynamicServer([{
provide: INITIAL_CONFIG,
useValue: {document: '<app></app>', url: 'http://localhost/path/page'}
}]);
const ref = await platform.bootstrapModule(HttpClientExampleModule);
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get<string>('/testing').subscribe((body: string) => {
NgZone.assertInAngularZone();
expect(body).toEqual('success!');
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}
}]);
const ref = await platform.bootstrapModule(HttpClientExampleModule);
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get<string>('testing').subscribe((body: string) => {
NgZone.assertInAngularZone();
expect(body).toEqual('success!');
});
mock.expectOne('http://localhost/testing').flush('success!');
});
mock.expectOne('http://localhost/testing').flush('success!');
});
});
it('can make relative HttpClient requests slashes longer url with base href', async () => {
const platform = platformDynamicServer([{
provide: INITIAL_CONFIG,
useValue:
{document: '<base href="http://other"><app></app>', url: 'http://localhost/path/page'}
}]);
const ref = await platform.bootstrapModule(HttpClientExampleModule);
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get<string>('/testing').subscribe((body: string) => {
NgZone.assertInAngularZone();
expect(body).toEqual('success!');
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}
}]);
const ref = await platform.bootstrapModule(HttpClientExampleModule);
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get<string>('testing').subscribe((body: string) => {
NgZone.assertInAngularZone();
expect(body).toEqual('success!');
});
mock.expectOne('http://localhost/path/testing').flush('success!');
});
mock.expectOne('http://other/testing').flush('success!');
});
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}
}]);
const ref = await platform.bootstrapModule(HttpClientExampleModule);
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get<string>('/testing').subscribe((body: string) => {
NgZone.assertInAngularZone();
expect(body).toEqual('success!');
});
mock.expectOne('http://localhost/testing').flush('success!');
});
});
it('correctly maps to absolute URL request with longer url, slashes, and base href',
async () => {
const platform = platformDynamicServer([{
provide: INITIAL_CONFIG,
useValue: {
document: '<base href="http://other"><app></app>',
url: 'http://localhost/path/page',
useAbsoluteUrl: true
}
}]);
const ref = await platform.bootstrapModule(HttpClientExampleModule);
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
const http = ref.injector.get(HttpClient);
ref.injector.get(NgZone).run(() => {
http.get<string>('/testing').subscribe((body: string) => {
NgZone.assertInAngularZone();
expect(body).toEqual('success!');
});
mock.expectOne('http://other/testing').flush('success!');
});
});
});
it('requests are macrotasks', async(() => {