Skip to content

Commit 5d2e243

Browse files
JeanMechealxhub
authored andcommittedAug 27, 2024·
fix(http): Dynamicaly call the global fetch implementation (#57531)
Instead of using the reference that existing when `FetchBackend` is setup. fixes #57527 PR Close #57531
1 parent 5e9661d commit 5d2e243

File tree

2 files changed

+37
-2
lines changed

2 files changed

+37
-2
lines changed
 

‎packages/common/http/src/fetch.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ function getResponseUrl(response: Response): string | null {
5252
*/
5353
@Injectable()
5454
export class FetchBackend implements HttpBackend {
55-
// We need to bind the native fetch to its context or it will throw an "illegal invocation"
55+
// Note: binding fetch directly causes an "illegal invocation" error
56+
// We use an arrow function to always reference the current global implementation of fetch
57+
// In case it has been (monkey)patched after the FetchBackend has been created
5658
private readonly fetchImpl =
57-
inject(FetchFactory, {optional: true})?.fetch ?? fetch.bind(globalThis);
59+
inject(FetchFactory, {optional: true})?.fetch ?? ((...args) => globalThis.fetch(...args));
5860
private readonly ngZone = inject(NgZone);
5961

6062
handle(request: HttpRequest<any>): Observable<HttpEvent<any>> {

‎packages/common/http/test/fetch_spec.ts

+33
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ import {Observable, of, Subject} from 'rxjs';
1212
import {catchError, retry, scan, skip, take, toArray} from 'rxjs/operators';
1313

1414
import {
15+
HttpClient,
1516
HttpDownloadProgressEvent,
1617
HttpErrorResponse,
1718
HttpHeaderResponse,
1819
HttpParams,
1920
HttpStatusCode,
21+
provideHttpClient,
22+
withFetch,
2023
} from '../public_api';
2124
import {FetchBackend, FetchFactory} from '../src/fetch';
2225

@@ -416,6 +419,36 @@ describe('FetchBackend', async () => {
416419
fetchMock.mockFlush(0, 'CORS 0 status');
417420
});
418421
});
422+
423+
describe('dynamic global fetch', () => {
424+
beforeEach(() => {
425+
TestBed.resetTestingModule();
426+
TestBed.configureTestingModule({
427+
providers: [provideHttpClient(withFetch())],
428+
});
429+
});
430+
431+
it('should use the current implementation of the global fetch', async () => {
432+
const fakeFetch = jasmine
433+
.createSpy('', () => Promise.resolve(new Response(JSON.stringify({foo: 'bar'}))))
434+
.and.callThrough();
435+
globalThis.fetch = fakeFetch;
436+
437+
const client = TestBed.inject(HttpClient);
438+
expect(fakeFetch).not.toHaveBeenCalled();
439+
let response = await client.get<unknown>('').toPromise();
440+
expect(fakeFetch).toHaveBeenCalled();
441+
expect(response).toEqual({foo: 'bar'});
442+
443+
// We dynamicaly change the implementation of fetch
444+
const fakeFetch2 = jasmine
445+
.createSpy('', () => Promise.resolve(new Response(JSON.stringify({foo: 'baz'}))))
446+
.and.callThrough();
447+
globalThis.fetch = fakeFetch2;
448+
response = await client.get<unknown>('').toPromise();
449+
expect(response).toEqual({foo: 'baz'});
450+
});
451+
});
419452
});
420453

421454
export class MockFetchFactory extends FetchFactory {

0 commit comments

Comments
 (0)
Please sign in to comment.