mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
fix(http): complete the request on timeout (#40771)
When using the [timeout attribute](https://xhr.spec.whatwg.org/#the-timeout-attribute) and an XHR request times out, browsers trigger the `timeout` event (and execute the XHR's `ontimeout` callback). Additionally, Safari 9 handles timed-out requests in the same way, even if no `timeout` has been explicitly set on the XHR. In the above cases, `HttpClient` would fail to capture the XHR's completing (with an error), so the corresponding `Observable` would never complete. PR Close #40771
This commit is contained in:
parent
ce80d5ee19
commit
a2a5b4add5
3 changed files with 23 additions and 2 deletions
|
|
@ -310,6 +310,7 @@ export class HttpXhrBackend implements HttpBackend {
|
|||
// By default, register for load and error events.
|
||||
xhr.addEventListener('load', onLoad);
|
||||
xhr.addEventListener('error', onError);
|
||||
xhr.addEventListener('timeout', onError);
|
||||
|
||||
// Progress events are only enabled if requested.
|
||||
if (req.reportProgress) {
|
||||
|
|
@ -332,6 +333,7 @@ export class HttpXhrBackend implements HttpBackend {
|
|||
// On a cancellation, remove all registered event listeners.
|
||||
xhr.removeEventListener('error', onError);
|
||||
xhr.removeEventListener('load', onLoad);
|
||||
xhr.removeEventListener('timeout', onError);
|
||||
if (req.reportProgress) {
|
||||
xhr.removeEventListener('progress', onDownProgress);
|
||||
if (reqBody !== null && xhr.upload) {
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ export class MockXMLHttpRequest {
|
|||
|
||||
listeners: {
|
||||
error?: (event: ErrorEvent) => void,
|
||||
timeout?: (event: ErrorEvent) => void,
|
||||
load?: () => void,
|
||||
progress?: (event: ProgressEvent) => void,
|
||||
uploadProgress?: (event: ProgressEvent) => void,
|
||||
|
|
@ -70,11 +71,12 @@ export class MockXMLHttpRequest {
|
|||
this.body = body;
|
||||
}
|
||||
|
||||
addEventListener(event: 'error'|'load'|'progress'|'uploadProgress', handler: Function): void {
|
||||
addEventListener(event: 'error'|'timeout'|'load'|'progress'|'uploadProgress', handler: Function):
|
||||
void {
|
||||
this.listeners[event] = handler as any;
|
||||
}
|
||||
|
||||
removeEventListener(event: 'error'|'load'|'progress'|'uploadProgress'): void {
|
||||
removeEventListener(event: 'error'|'timeout'|'load'|'progress'|'uploadProgress'): void {
|
||||
delete this.listeners[event];
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +131,12 @@ export class MockXMLHttpRequest {
|
|||
}
|
||||
}
|
||||
|
||||
mockTimeoutEvent(error: any): void {
|
||||
if (this.listeners.timeout) {
|
||||
this.listeners.timeout(error);
|
||||
}
|
||||
}
|
||||
|
||||
abort() {
|
||||
this.mockAborted = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,6 +147,17 @@ const XSSI_PREFIX = ')]}\'\n';
|
|||
});
|
||||
factory.mock.mockErrorEvent(new Error('blah'));
|
||||
});
|
||||
it('emits timeout if the request times out', done => {
|
||||
backend.handle(TEST_POST).subscribe({
|
||||
error: (error: HttpErrorResponse) => {
|
||||
expect(error instanceof HttpErrorResponse).toBeTrue();
|
||||
expect(error.error instanceof Error).toBeTrue();
|
||||
expect(error.url).toBe('/test');
|
||||
done();
|
||||
},
|
||||
});
|
||||
factory.mock.mockTimeoutEvent(new Error('timeout'));
|
||||
});
|
||||
it('avoids abort a request when fetch operation is completed', done => {
|
||||
const abort = jasmine.createSpy('abort');
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue