mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
refactor(http): Improves base64 encoding/decoding with feature detection (#67002)
Use feature detection for `Uint8Array.prototype.toBase64` and `Uint8Array.fromBase64`, falling back to the existing implementation when native support is not available PR Close #67002
This commit is contained in:
parent
d9e40cb456
commit
aafeb1d2bd
3 changed files with 50 additions and 23 deletions
|
|
@ -29,6 +29,7 @@ import {HTTP_ROOT_INTERCEPTOR_FNS, HttpHandlerFn} from './interceptor';
|
|||
import {HttpRequest} from './request';
|
||||
import {HttpEvent, HttpResponse} from './response';
|
||||
import {HttpParams} from './params';
|
||||
import {fromBase64, toBase64} from './util';
|
||||
|
||||
/**
|
||||
* Options to configure how TransferCache should be used to cache requests made via HttpClient.
|
||||
|
|
@ -272,7 +273,7 @@ export function transferCacheInterceptorFn(
|
|||
transferState.set<TransferHttpResponse>(storeKey, {
|
||||
[BODY]:
|
||||
req.responseType === 'arraybuffer' || req.responseType === 'blob'
|
||||
? toBase64(event.body)
|
||||
? toBase64(event.body as ArrayBufferLike)
|
||||
: event.body,
|
||||
[HEADERS]: getFilteredHeaders(event.headers, headersToInclude),
|
||||
[STATUS]: event.status,
|
||||
|
|
@ -360,28 +361,6 @@ function generateHash(value: string): string {
|
|||
return hash.toString();
|
||||
}
|
||||
|
||||
function toBase64(buffer: unknown): string {
|
||||
//TODO: replace with when is Baseline widely available
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/toBase64
|
||||
const bytes = new Uint8Array(buffer as ArrayBufferLike);
|
||||
|
||||
const CHUNK_SIZE = 0x8000; // 32,768 bytes (~32 KB) per chunk, to avoid stack overflow
|
||||
|
||||
let binaryString = '';
|
||||
|
||||
for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
|
||||
const chunk = bytes.subarray(i, i + CHUNK_SIZE);
|
||||
binaryString += String.fromCharCode.apply(null, chunk as unknown as number[]);
|
||||
}
|
||||
return btoa(binaryString);
|
||||
}
|
||||
|
||||
function fromBase64(base64: string): ArrayBuffer {
|
||||
const binary = atob(base64);
|
||||
const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
||||
return bytes.buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DI providers needed to enable HTTP transfer cache.
|
||||
*
|
||||
|
|
|
|||
47
packages/common/http/src/util.ts
Normal file
47
packages/common/http/src/util.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
// TODO: Replace this fallback once widely available.
|
||||
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/toBase64
|
||||
|
||||
type Uint8ArrayWithToBase64 = Uint8Array & {toBase64(): string};
|
||||
type Uint8ArrayCtorWithFromBase64 = typeof Uint8Array & {fromBase64(base64: string): Uint8Array};
|
||||
|
||||
function hasToBase64(u8: Uint8Array): u8 is Uint8ArrayWithToBase64 {
|
||||
return typeof (u8 as Uint8ArrayWithToBase64).toBase64 === 'function';
|
||||
}
|
||||
|
||||
function hasFromBase64(ctor: typeof Uint8Array): ctor is Uint8ArrayCtorWithFromBase64 {
|
||||
return typeof (ctor as Uint8ArrayCtorWithFromBase64).fromBase64 === 'function';
|
||||
}
|
||||
|
||||
export function toBase64(buffer: ArrayBufferLike): string {
|
||||
const bytes = new Uint8Array(buffer);
|
||||
|
||||
if (hasToBase64(bytes)) {
|
||||
return bytes.toBase64();
|
||||
}
|
||||
|
||||
const CHUNK_SIZE = 0x8000; // 32,768 bytes (~32 KB) per chunk, to avoid stack overflow
|
||||
let binaryString = '';
|
||||
for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
|
||||
const chunk = bytes.subarray(i, i + CHUNK_SIZE);
|
||||
binaryString += String.fromCharCode.apply(null, chunk as unknown as number[]);
|
||||
}
|
||||
return btoa(binaryString);
|
||||
}
|
||||
|
||||
export function fromBase64(base64: string): ArrayBuffer {
|
||||
if (hasFromBase64(Uint8Array)) {
|
||||
return Uint8Array.fromBase64(base64).buffer as ArrayBuffer;
|
||||
}
|
||||
|
||||
const binary = atob(base64);
|
||||
const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));
|
||||
return bytes.buffer as ArrayBuffer;
|
||||
}
|
||||
|
|
@ -531,6 +531,7 @@
|
|||
"hasApplyArgsData",
|
||||
"hasAuthHeaders",
|
||||
"hasDeps",
|
||||
"hasFromBase64",
|
||||
"hasInSkipHydrationBlockFlag",
|
||||
"hasLift",
|
||||
"hasMatchingDehydratedView",
|
||||
|
|
|
|||
Loading…
Reference in a new issue