Skip to content
This repository was archived by the owner on Nov 22, 2024. It is now read-only.

Commit 26f2aa5

Browse files
committed
feat(@nguniversal/common): add support for blob and arraybuffer in TransferHttpCacheModule
This commit add support to handle responses of types `arraybuffer` and `blob` in `TransferHttpCacheModule`. Closes #2734 (cherry picked from commit 868db00)
1 parent b511f53 commit 26f2aa5

File tree

3 files changed

+72
-9
lines changed

3 files changed

+72
-9
lines changed

modules/common/clover/src/transfer-http-cache/transfer-http-cache.interceptor.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@ import { StateKey, TransferState, makeStateKey } from '@angular/platform-browser
2020
import { Observable, of } from 'rxjs';
2121
import { filter, take, tap } from 'rxjs/operators';
2222

23+
type ResponseType = HttpRequest<unknown>['responseType'];
24+
2325
interface TransferHttpResponse {
2426
body?: any | null;
2527
headers?: Record<string, string[]>;
2628
status?: number;
2729
statusText?: string;
2830
url?: string;
31+
responseType?: ResponseType;
2932
}
3033

3134
@Injectable()
@@ -36,14 +39,16 @@ export class TransferHttpCacheInterceptor implements HttpInterceptor {
3639
method: string,
3740
url: string,
3841
params: HttpParams,
42+
responseType?: ResponseType,
3943
): StateKey<TransferHttpResponse> {
4044
// make the params encoded same as a url so it's easy to identify
4145
const encodedParams = params
4246
.keys()
4347
.sort()
4448
.map((k) => `${k}=${params.getAll(k)}`)
4549
.join('&');
46-
const key = (method === 'GET' ? 'G.' : 'H.') + url + '?' + encodedParams;
50+
51+
const key = (method === 'GET' ? 'G.' : 'H.') + responseType + '.' + url + '?' + encodedParams;
4752

4853
return makeStateKey(key);
4954
}
@@ -66,15 +71,38 @@ export class TransferHttpCacheInterceptor implements HttpInterceptor {
6671
return next.handle(req);
6772
}
6873

69-
const storeKey = this.makeCacheKey(req.method, req.url, req.params);
74+
const storeKey = this.makeCacheKey(req.method, req.url, req.params, req.responseType);
7075

7176
if (this.transferState.hasKey(storeKey)) {
7277
// Request found in cache. Respond using it.
7378
const response = this.transferState.get(storeKey, {});
79+
let body: ArrayBuffer | Blob | string | undefined = response.body;
80+
81+
switch (response.responseType) {
82+
case 'arraybuffer':
83+
{
84+
// If we're in Node...
85+
if (typeof Buffer !== 'undefined') {
86+
const buf = Buffer.from(response.body);
87+
body = new ArrayBuffer(buf.length);
88+
const view = new Uint8Array(body);
89+
for (let i = 0; i < buf.length; ++i) {
90+
view[i] = buf[i];
91+
}
92+
} else if (typeof TextEncoder !== 'undefined') {
93+
// Modern browsers implement TextEncode.
94+
body = new TextEncoder().encode(response.body).buffer;
95+
}
96+
}
97+
break;
98+
case 'blob':
99+
body = new Blob([response.body]);
100+
break;
101+
}
74102

75103
return of(
76104
new HttpResponse<any>({
77-
body: response.body,
105+
body,
78106
headers: new HttpHeaders(response.headers),
79107
status: response.status,
80108
statusText: response.statusText,
@@ -95,6 +123,7 @@ export class TransferHttpCacheInterceptor implements HttpInterceptor {
95123
status: event.status,
96124
statusText: event.statusText,
97125
url: event.url ?? '',
126+
responseType: req.responseType,
98127
});
99128
}
100129
}),

modules/common/spec/transfer_http.spec.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,29 +32,33 @@ describe('TransferHttp', () => {
3232
'GET',
3333
'https://google.com/api',
3434
new HttpParams().append('foo', 'bar'),
35+
'text',
3536
);
36-
expect(key).toEqual('G.https://google.com/api?foo=bar');
37+
expect(key).toEqual('G.text.https://google.com/api?foo=bar');
3738
});
3839
it('should sort the keys by unicode points', () => {
3940
const interceptor = new TransferHttpCacheInterceptor(mockAppRef(), mockTransferState());
4041
const key = interceptor['makeCacheKey'](
4142
'GET',
4243
'https://google.com/api',
4344
new HttpParams().append('b', 'foo').append('a', 'bar'),
45+
'text',
4446
);
45-
expect(key).toEqual('G.https://google.com/api?a=bar&b=foo');
47+
expect(key).toEqual('G.text.https://google.com/api?a=bar&b=foo');
4648
});
4749
it('should make equal keys if order of params changes', () => {
4850
const interceptor = new TransferHttpCacheInterceptor(mockAppRef(), mockTransferState());
4951
const key1 = interceptor['makeCacheKey'](
5052
'GET',
5153
'https://google.com/api',
5254
new HttpParams().append('a', 'bar').append('b', 'foo'),
55+
'text',
5356
);
5457
const key2 = interceptor['makeCacheKey'](
5558
'GET',
5659
'https://google.com/api',
5760
new HttpParams().append('b', 'foo').append('a', 'bar'),
61+
'text',
5862
);
5963
expect(key1).toEqual(key2);
6064
});
@@ -64,8 +68,9 @@ describe('TransferHttp', () => {
6468
'GET',
6569
'https://google.com/api',
6670
new HttpParams().append('b', 'xyz').append('a', 'foo').append('a', 'bar'),
71+
'text',
6772
);
68-
expect(key).toEqual('G.https://google.com/api?a=foo,bar&b=xyz');
73+
expect(key).toEqual('G.text.https://google.com/api?a=foo,bar&b=xyz');
6974
});
7075
});
7176
});

modules/common/src/transfer_http.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,15 @@ import {
2626
import { Observable, of as observableOf } from 'rxjs';
2727
import { filter, take, tap } from 'rxjs/operators';
2828

29+
type ResponseType = HttpRequest<unknown>['responseType'];
30+
2931
export interface TransferHttpResponse {
3032
body?: any | null;
3133
headers?: Record<string, string[]>;
3234
status?: number;
3335
statusText?: string;
3436
url?: string;
37+
responseType?: ResponseType;
3538
}
3639

3740
function getHeadersMap(headers: HttpHeaders): Record<string, string[]> {
@@ -60,14 +63,16 @@ export class TransferHttpCacheInterceptor implements HttpInterceptor {
6063
method: string,
6164
url: string,
6265
params: HttpParams,
66+
responseType: ResponseType,
6367
): StateKey<TransferHttpResponse> {
6468
// make the params encoded same as a url so it's easy to identify
6569
const encodedParams = params
6670
.keys()
6771
.sort()
6872
.map((k) => `${k}=${params.getAll(k)}`)
6973
.join('&');
70-
const key = (method === 'GET' ? 'G.' : 'H.') + url + '?' + encodedParams;
74+
75+
const key = (method === 'GET' ? 'G.' : 'H.') + responseType + '.' + url + '?' + encodedParams;
7176

7277
return makeStateKey<TransferHttpResponse>(key);
7378
}
@@ -97,15 +102,38 @@ export class TransferHttpCacheInterceptor implements HttpInterceptor {
97102
return next.handle(req);
98103
}
99104

100-
const storeKey = this.makeCacheKey(req.method, req.url, req.params);
105+
const storeKey = this.makeCacheKey(req.method, req.url, req.params, req.responseType);
101106

102107
if (this.transferState.hasKey(storeKey)) {
103108
// Request found in cache. Respond using it.
104109
const response = this.transferState.get(storeKey, {});
110+
let body: ArrayBuffer | Blob | string | undefined = response.body;
111+
112+
switch (response.responseType) {
113+
case 'arraybuffer':
114+
{
115+
// If we're in Node...
116+
if (typeof Buffer !== 'undefined') {
117+
const buf = Buffer.from(response.body);
118+
body = new ArrayBuffer(buf.length);
119+
const view = new Uint8Array(body);
120+
for (let i = 0; i < buf.length; ++i) {
121+
view[i] = buf[i];
122+
}
123+
} else if (typeof TextEncoder !== 'undefined') {
124+
// Modern browsers implement TextEncode.
125+
body = new TextEncoder().encode(response.body).buffer;
126+
}
127+
}
128+
break;
129+
case 'blob':
130+
body = new Blob([response.body]);
131+
break;
132+
}
105133

106134
return observableOf(
107135
new HttpResponse<any>({
108-
body: response.body,
136+
body,
109137
headers: new HttpHeaders(response.headers),
110138
status: response.status,
111139
statusText: response.statusText,
@@ -125,6 +153,7 @@ export class TransferHttpCacheInterceptor implements HttpInterceptor {
125153
status: event.status,
126154
statusText: event.statusText,
127155
url: event.url || '',
156+
responseType: req.responseType,
128157
});
129158
}
130159
}),

0 commit comments

Comments
 (0)