diff --git a/packages/common/http/src/fetch.ts b/packages/common/http/src/fetch.ts index 81632ccc79c0..e4c263529c7a 100644 --- a/packages/common/http/src/fetch.ts +++ b/packages/common/http/src/fetch.ts @@ -191,6 +191,7 @@ export class FetchBackend implements HttpBackend { Number.isFinite(contentLengthValue) && contentLengthValue > this.maxResponseSize ) { + await response.body.cancel(); throwBodyTooLargeError(this.maxResponseSize); } diff --git a/packages/common/http/test/fetch_spec.ts b/packages/common/http/test/fetch_spec.ts index 72a427791de0..32e4484959f3 100644 --- a/packages/common/http/test/fetch_spec.ts +++ b/packages/common/http/test/fetch_spec.ts @@ -708,6 +708,19 @@ describe('FetchBackend', () => { expect(infiniteStreamFactory.cancelCount).toBe(1); }); + it('cancels an unread response stream when the declared size exceeds the limit', async () => { + infiniteStreamFactory.declaredContentLength = 2049; + + const req = new HttpRequest('GET', '/test', {responseType: 'text'}); + const events = await trackEvents(backend.handle(req)); + const error = events[1] as HttpErrorResponse; + + expect(events.length).toBe(2); + expect(error instanceof HttpErrorResponse).toBeTrue(); + expect(error.error.code).toBe(RuntimeErrorCode.FETCH_RESPONSE_BODY_TOO_LARGE); + expect(infiniteStreamFactory.cancelCount).toBe(1); + }); + it('allows disabling the size limit via dependency injection', async () => { TestBed.resetTestingModule(); TestBed.configureTestingModule({ @@ -854,6 +867,7 @@ class MockFetchRequest { class InfiniteStreamFetchFactory extends FetchFactory { public cancelCount = 0; + public declaredContentLength?: number; override fetch = async (_input: RequestInfo | URL, _init?: RequestInit): Promise => { const stream = new ReadableStream({ @@ -868,7 +882,12 @@ class InfiniteStreamFetchFactory extends FetchFactory { return new Response(stream, { status: HttpStatusCode.Ok, statusText: 'OK', - headers: {'Content-Type': 'text/plain'}, + headers: { + 'Content-Type': 'text/plain', + ...(this.declaredContentLength === undefined + ? {} + : {'Content-Length': `${this.declaredContentLength}`}), + }, }); }; } @@ -885,7 +904,7 @@ class FiniteChunkFetchFactory extends FetchFactory { return new Response(stream, { status: HttpStatusCode.Ok, statusText: 'OK', - headers: {'Content-Type': 'text/plain'}, + headers: {'Content-Type': 'text/plain', 'Content-Length': '2'}, }); }; }