Skip to content

Commit 3897265

Browse files
ItNoNalxhub
authored andcommitted
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
1 parent ddff6b6 commit 3897265

File tree

3 files changed

+20
-3
lines changed

3 files changed

+20
-3
lines changed

packages/common/http/src/xhr.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ export class HttpXhrBackend implements HttpBackend {
312312
xhr.addEventListener('load', onLoad);
313313
xhr.addEventListener('error', onError);
314314
xhr.addEventListener('timeout', onError);
315+
xhr.addEventListener('abort', onError);
315316

316317
// Progress events are only enabled if requested.
317318
if (req.reportProgress) {
@@ -333,6 +334,7 @@ export class HttpXhrBackend implements HttpBackend {
333334
return () => {
334335
// On a cancellation, remove all registered event listeners.
335336
xhr.removeEventListener('error', onError);
337+
xhr.removeEventListener('abort', onError);
336338
xhr.removeEventListener('load', onLoad);
337339
xhr.removeEventListener('timeout', onError);
338340
if (req.reportProgress) {

packages/common/http/test/xhr_mock.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export class MockXMLHttpRequest {
5555
listeners: {
5656
error?: (event: ErrorEvent) => void,
5757
timeout?: (event: ErrorEvent) => void,
58+
abort?: () => void,
5859
load?: () => void,
5960
progress?: (event: ProgressEvent) => void,
6061
uploadProgress?: (event: ProgressEvent) => void,
@@ -71,12 +72,13 @@ export class MockXMLHttpRequest {
7172
this.body = body;
7273
}
7374

74-
addEventListener(event: 'error'|'timeout'|'load'|'progress'|'uploadProgress', handler: Function):
75-
void {
75+
addEventListener(
76+
event: 'error'|'timeout'|'load'|'progress'|'uploadProgress'|'abort',
77+
handler: Function): void {
7678
this.listeners[event] = handler as any;
7779
}
7880

79-
removeEventListener(event: 'error'|'timeout'|'load'|'progress'|'uploadProgress'): void {
81+
removeEventListener(event: 'error'|'timeout'|'load'|'progress'|'uploadProgress'|'abort'): void {
8082
delete this.listeners[event];
8183
}
8284

@@ -137,6 +139,12 @@ export class MockXMLHttpRequest {
137139
}
138140
}
139141

142+
mockAbortEvent(): void {
143+
if (this.listeners.abort) {
144+
this.listeners.abort();
145+
}
146+
}
147+
140148
abort() {
141149
this.mockAborted = true;
142150
}

packages/common/http/test/xhr_spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,13 @@ const XSSI_PREFIX = ')]}\'\n';
173173
factory.mock.abort = abort;
174174
factory.mock.mockFlush(HttpStatusCode.Ok, 'OK', 'Done');
175175
});
176+
it('emits an error when browser cancels a request', done => {
177+
backend.handle(TEST_POST).subscribe(undefined, (err: HttpErrorResponse) => {
178+
expect(err instanceof HttpErrorResponse).toBe(true);
179+
done();
180+
});
181+
factory.mock.mockAbortEvent();
182+
});
176183
describe('progress events', () => {
177184
it('are emitted for download progress', done => {
178185
backend.handle(TEST_POST.clone({reportProgress: true}))

0 commit comments

Comments
 (0)