fix(http): emit error on XMLHttpRequest abort event (#40767)
Before this change, when Google Chrome cancels a XMLHttpRequest, an Observable of the response never finishes. This happens, for example, when you put your computer to sleep or just press Ctrl+S to save the browser page. After this commit, if request is canceled or aborted an appropriate Observable will be completed with an error. Fixes #22324 PR Close #40767
This commit is contained in:
parent
ddff6b63d7
commit
38972653fa
@ -312,6 +312,7 @@ export class HttpXhrBackend implements HttpBackend {
|
|||||||
xhr.addEventListener('load', onLoad);
|
xhr.addEventListener('load', onLoad);
|
||||||
xhr.addEventListener('error', onError);
|
xhr.addEventListener('error', onError);
|
||||||
xhr.addEventListener('timeout', onError);
|
xhr.addEventListener('timeout', onError);
|
||||||
|
xhr.addEventListener('abort', onError);
|
||||||
|
|
||||||
// Progress events are only enabled if requested.
|
// Progress events are only enabled if requested.
|
||||||
if (req.reportProgress) {
|
if (req.reportProgress) {
|
||||||
@ -333,6 +334,7 @@ export class HttpXhrBackend implements HttpBackend {
|
|||||||
return () => {
|
return () => {
|
||||||
// On a cancellation, remove all registered event listeners.
|
// On a cancellation, remove all registered event listeners.
|
||||||
xhr.removeEventListener('error', onError);
|
xhr.removeEventListener('error', onError);
|
||||||
|
xhr.removeEventListener('abort', onError);
|
||||||
xhr.removeEventListener('load', onLoad);
|
xhr.removeEventListener('load', onLoad);
|
||||||
xhr.removeEventListener('timeout', onError);
|
xhr.removeEventListener('timeout', onError);
|
||||||
if (req.reportProgress) {
|
if (req.reportProgress) {
|
||||||
|
@ -55,6 +55,7 @@ export class MockXMLHttpRequest {
|
|||||||
listeners: {
|
listeners: {
|
||||||
error?: (event: ErrorEvent) => void,
|
error?: (event: ErrorEvent) => void,
|
||||||
timeout?: (event: ErrorEvent) => void,
|
timeout?: (event: ErrorEvent) => void,
|
||||||
|
abort?: () => void,
|
||||||
load?: () => void,
|
load?: () => void,
|
||||||
progress?: (event: ProgressEvent) => void,
|
progress?: (event: ProgressEvent) => void,
|
||||||
uploadProgress?: (event: ProgressEvent) => void,
|
uploadProgress?: (event: ProgressEvent) => void,
|
||||||
@ -71,12 +72,13 @@ export class MockXMLHttpRequest {
|
|||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
|
|
||||||
addEventListener(event: 'error'|'timeout'|'load'|'progress'|'uploadProgress', handler: Function):
|
addEventListener(
|
||||||
void {
|
event: 'error'|'timeout'|'load'|'progress'|'uploadProgress'|'abort',
|
||||||
|
handler: Function): void {
|
||||||
this.listeners[event] = handler as any;
|
this.listeners[event] = handler as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeEventListener(event: 'error'|'timeout'|'load'|'progress'|'uploadProgress'): void {
|
removeEventListener(event: 'error'|'timeout'|'load'|'progress'|'uploadProgress'|'abort'): void {
|
||||||
delete this.listeners[event];
|
delete this.listeners[event];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,6 +139,12 @@ export class MockXMLHttpRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mockAbortEvent(): void {
|
||||||
|
if (this.listeners.abort) {
|
||||||
|
this.listeners.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
abort() {
|
abort() {
|
||||||
this.mockAborted = true;
|
this.mockAborted = true;
|
||||||
}
|
}
|
||||||
|
@ -173,6 +173,13 @@ const XSSI_PREFIX = ')]}\'\n';
|
|||||||
factory.mock.abort = abort;
|
factory.mock.abort = abort;
|
||||||
factory.mock.mockFlush(HttpStatusCode.Ok, 'OK', 'Done');
|
factory.mock.mockFlush(HttpStatusCode.Ok, 'OK', 'Done');
|
||||||
});
|
});
|
||||||
|
it('emits an error when browser cancels a request', done => {
|
||||||
|
backend.handle(TEST_POST).subscribe(undefined, (err: HttpErrorResponse) => {
|
||||||
|
expect(err instanceof HttpErrorResponse).toBe(true);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
factory.mock.mockAbortEvent();
|
||||||
|
});
|
||||||
describe('progress events', () => {
|
describe('progress events', () => {
|
||||||
it('are emitted for download progress', done => {
|
it('are emitted for download progress', done => {
|
||||||
backend.handle(TEST_POST.clone({reportProgress: true}))
|
backend.handle(TEST_POST.clone({reportProgress: true}))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user