From bd9e91ecf7af877e4ecf08a16eda7b4e59707541 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Tue, 24 Oct 2023 16:35:36 +0000 Subject: [PATCH] perf(http): reduce data transfer when using HTTP caching (#52347) This commit reduces the property size in the http transfer cache to reduce the page payload. Before ```html ``` Now ```html ``` PR Close #52347 --- packages/common/http/src/transfer_cache.ts | 66 +++++++++++++------ .../common/http/test/transfer_cache_spec.ts | 28 ++++---- .../hydration/bundle.golden_symbols.json | 18 +++++ 3 files changed, 78 insertions(+), 34 deletions(-) diff --git a/packages/common/http/src/transfer_cache.ts b/packages/common/http/src/transfer_cache.ts index 78f31ab849704..9b1fd2d41d4d5 100644 --- a/packages/common/http/src/transfer_cache.ts +++ b/packages/common/http/src/transfer_cache.ts @@ -35,13 +35,31 @@ export type HttpTransferCacheOptions = { includePostRequests?: boolean }; +/** + * Keys within cached response data structure. + */ + +export const BODY = 'b'; +export const HEADERS = 'h'; +export const STATUS = 's'; +export const STATUS_TEXT = 'st'; +export const URL = 'u'; +export const RESPONSE_TYPE = 'rt'; + + interface TransferHttpResponse { - body: any; - headers: Record; - status?: number; - statusText?: string; - url?: string; - responseType?: HttpRequest['responseType']; + /** body */ + [BODY]: any; + /** headers */ + [HEADERS]: Record; + /** status */ + [STATUS]?: number; + /** statusText */ + [STATUS_TEXT]?: string; + /** url */ + [URL]?: string; + /** responseType */ + [RESPONSE_TYPE]?: HttpRequest['responseType']; } interface CacheOptions extends HttpTransferCacheOptions { @@ -82,22 +100,30 @@ export function transferCacheInterceptorFn( } if (response) { + const { + [BODY]: undecodedBody, + [RESPONSE_TYPE]: responseType, + [HEADERS]: httpHeaders, + [STATUS]: status, + [STATUS_TEXT]: statusText, + [URL]: url + } = response; // Request found in cache. Respond using it. - let body: ArrayBuffer|Blob|string|undefined = response.body; + let body: ArrayBuffer|Blob|string|undefined = undecodedBody; - switch (response.responseType) { + switch (responseType) { case 'arraybuffer': - body = new TextEncoder().encode(response.body).buffer; + body = new TextEncoder().encode(undecodedBody).buffer; break; case 'blob': - body = new Blob([response.body]); + body = new Blob([undecodedBody]); break; } // We want to warn users accessing a header provided from the cache // That HttpTransferCache alters the headers // The warning will be logged a single time by HttpHeaders instance - let headers = new HttpHeaders(response.headers); + let headers = new HttpHeaders(httpHeaders); if (typeof ngDevMode === 'undefined' || ngDevMode) { // Append extra logic in dev mode to produce a warning when a header // that was not transferred to the client is accessed in the code via `get` @@ -110,9 +136,9 @@ export function transferCacheInterceptorFn( new HttpResponse({ body, headers, - status: response.status, - statusText: response.statusText, - url: response.url, + status, + statusText, + url, }), ); } @@ -123,12 +149,12 @@ export function transferCacheInterceptorFn( tap((event: HttpEvent) => { if (event instanceof HttpResponse) { transferState.set(storeKey, { - body: event.body, - headers: getFilteredHeaders(event.headers, headersToInclude), - status: event.status, - statusText: event.statusText, - url: event.url || '', - responseType: req.responseType, + [BODY]: event.body, + [HEADERS]: getFilteredHeaders(event.headers, headersToInclude), + [STATUS]: event.status, + [STATUS_TEXT]: event.statusText, + [URL]: event.url || '', + [RESPONSE_TYPE]: req.responseType, }); } }), diff --git a/packages/common/http/test/transfer_cache_spec.ts b/packages/common/http/test/transfer_cache_spec.ts index b3666658f391d..a34c82571f37d 100644 --- a/packages/common/http/test/transfer_cache_spec.ts +++ b/packages/common/http/test/transfer_cache_spec.ts @@ -14,7 +14,7 @@ import {withBody} from '@angular/private/testing'; import {BehaviorSubject} from 'rxjs'; import {HttpClient, HttpResponse, provideHttpClient} from '../public_api'; -import {withHttpTransferCache} from '../src/transfer_cache'; +import {BODY, HEADERS, RESPONSE_TYPE, STATUS, STATUS_TEXT, URL, withHttpTransferCache} from '../src/transfer_cache'; import {HttpTestingController, provideHttpClientTesting} from '../testing'; interface RequestParams { @@ -84,7 +84,7 @@ describe('TransferCache', () => { makeRequestAndExpectOne('/test', 'foo'); const key = makeStateKey('432906284'); const transferState = TestBed.inject(TransferState); - expect(transferState.get(key, null)).toEqual(jasmine.objectContaining({body: 'foo'})); + expect(transferState.get(key, null)).toEqual(jasmine.objectContaining({[BODY]: 'foo'})); }); it('should stop storing HTTP calls in `TransferState` after application becomes stable', @@ -101,20 +101,20 @@ describe('TransferCache', () => { const transferState = TestBed.inject(TransferState); expect(JSON.parse(transferState.toJson()) as Record).toEqual({ '3706062792': { - 'body': 'foo', - 'headers': {}, - 'status': 200, - 'statusText': 'OK', - 'url': '/test-1', - 'responseType': 'json' + [BODY]: 'foo', + [HEADERS]: {}, + [STATUS]: 200, + [STATUS_TEXT]: 'OK', + [URL]: '/test-1', + [RESPONSE_TYPE]: 'json' }, '3706062823': { - 'body': 'buzz', - 'headers': {}, - 'status': 200, - 'statusText': 'OK', - 'url': '/test-2', - 'responseType': 'json' + [BODY]: 'buzz', + [HEADERS]: {}, + [STATUS]: 200, + [STATUS_TEXT]: 'OK', + [URL]: '/test-2', + [RESPONSE_TYPE]: 'json' } }); })); diff --git a/packages/core/test/bundling/hydration/bundle.golden_symbols.json b/packages/core/test/bundling/hydration/bundle.golden_symbols.json index a8cb4270236c2..f8bcfefdd73fe 100644 --- a/packages/core/test/bundling/hydration/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hydration/bundle.golden_symbols.json @@ -32,6 +32,9 @@ { "name": "BLOOM_MASK" }, + { + "name": "BODY" + }, { "name": "BROWSER_MODULE_PROVIDERS" }, @@ -188,6 +191,9 @@ { "name": "HAS_TRANSPLANTED_VIEWS" }, + { + "name": "HEADERS" + }, { "name": "HTTP_ROOT_INTERCEPTOR_FNS" }, @@ -398,6 +404,9 @@ { "name": "REMOVE_STYLES_ON_COMPONENT_DESTROY" }, + { + "name": "RESPONSE_TYPE" + }, { "name": "RefCountOperator" }, @@ -425,6 +434,12 @@ { "name": "SKIP_HYDRATION_ATTR_NAME_LOWER_CASE" }, + { + "name": "STATUS" + }, + { + "name": "STATUS_TEXT" + }, { "name": "SafeSubscriber" }, @@ -500,6 +515,9 @@ { "name": "TransferState" }, + { + "name": "URL2" + }, { "name": "USE_VALUE" },