feat(compression): Improve compression implementation (buffering and threshold), add Deno brotli support, add compression tests and dynamic route API
This commit is contained in:
@@ -68,7 +68,8 @@ export function normalizeCompressionConfig(
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Check if response should be compressed
|
||||
* Check if response should be compressed (preliminary check)
|
||||
* Note: Final threshold check happens in compressResponse after buffering
|
||||
*/
|
||||
export function shouldCompressResponse(
|
||||
response: Response,
|
||||
@@ -97,15 +98,6 @@ export function shouldCompressResponse(
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check size threshold
|
||||
const contentLength = response.headers.get('Content-Length');
|
||||
if (contentLength) {
|
||||
const size = parseInt(contentLength, 10);
|
||||
if (size < (config.threshold ?? DEFAULT_COMPRESSION_CONFIG.threshold)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check excluded paths
|
||||
if (config.exclude?.length) {
|
||||
const url = new URL(request.url);
|
||||
@@ -166,28 +158,44 @@ export function selectCompressionAlgorithm(
|
||||
|
||||
/**
|
||||
* Compress a Response object
|
||||
* Uses buffered compression for reliability (streaming can have flushing issues)
|
||||
*/
|
||||
export async function compressResponse(
|
||||
response: Response,
|
||||
algorithm: TCompressionAlgorithm,
|
||||
level?: number
|
||||
level?: number,
|
||||
threshold?: number
|
||||
): Promise<Response> {
|
||||
if (algorithm === 'identity' || !response.body) {
|
||||
return response;
|
||||
}
|
||||
|
||||
// Read the entire body first (required for proper compression)
|
||||
const originalBody = new Uint8Array(await response.arrayBuffer());
|
||||
|
||||
// Check threshold - if body is too small, return uncompressed
|
||||
const effectiveThreshold = threshold ?? DEFAULT_COMPRESSION_CONFIG.threshold;
|
||||
if (originalBody.byteLength < effectiveThreshold) {
|
||||
// Return original response with the body we read
|
||||
return new Response(originalBody as unknown as BodyInit, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: response.headers,
|
||||
});
|
||||
}
|
||||
|
||||
const provider = getCompressionProvider();
|
||||
|
||||
// Compress the body
|
||||
const compressedBody = await provider.compress(originalBody, algorithm, level);
|
||||
|
||||
// Clone headers and modify
|
||||
const headers = new Headers(response.headers);
|
||||
headers.set('Content-Encoding', algorithm);
|
||||
headers.set('Vary', appendVaryHeader(headers.get('Vary'), 'Accept-Encoding'));
|
||||
headers.delete('Content-Length'); // Size changes after compression
|
||||
headers.set('Content-Length', compressedBody.byteLength.toString());
|
||||
|
||||
// Compress the body stream
|
||||
const compressedBody = provider.compressStream(response.body, algorithm, level);
|
||||
|
||||
return new Response(compressedBody, {
|
||||
return new Response(compressedBody as unknown as BodyInit, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers,
|
||||
|
||||
Reference in New Issue
Block a user