@whatwg-node/server
Advanced tools
Comparing version 0.10.0-alpha-20250205010145-5ab5edf75d82f8174c999dc970ad9a042ea5547d to 0.10.0-alpha-20250205010234-c9a7e9a3e2e8b8de7e4519f3119e6b874c5b996f
@@ -49,4 +49,5 @@ "use strict"; | ||
}, | ||
onResponse({ request, response, setResponse, fetchAPI }) { | ||
if (response.body) { | ||
onResponse({ request, response, setResponse, fetchAPI, serverContext }) { | ||
// Hack for avoiding to create whatwg-node to create a readable stream until it's needed | ||
if (response['bodyInit'] || response.body) { | ||
const encodings = encodingMap.get(request); | ||
@@ -57,8 +58,38 @@ if (encodings) { | ||
const compressionStream = new fetchAPI.CompressionStream(supportedEncoding); | ||
// To calculate final content-length | ||
const contentLength = response.headers.get('content-length'); | ||
if (contentLength) { | ||
const bufOfRes = response._buffer; | ||
if (bufOfRes) { | ||
const writer = compressionStream.writable.getWriter(); | ||
const write$ = writer.write(bufOfRes); | ||
serverContext.waitUntil?.(write$); | ||
const close$ = writer.close(); | ||
serverContext.waitUntil?.(close$); | ||
const uint8Arrays$ = (0, utils_js_1.isReadable)(compressionStream.readable['readable']) | ||
? collectReadableValues(compressionStream.readable['readable']) | ||
: (0, utils_js_1.isAsyncIterable)(compressionStream.readable) | ||
? collectAsyncIterableValues(compressionStream.readable) | ||
: collectReadableStreamValues(compressionStream.readable); | ||
return uint8Arrays$.then(uint8Arrays => { | ||
const chunks = uint8Arrays.flatMap(uint8Array => [...uint8Array]); | ||
const uint8Array = new Uint8Array(chunks); | ||
const newHeaders = new fetchAPI.Headers(response.headers); | ||
newHeaders.set('content-encoding', supportedEncoding); | ||
newHeaders.set('content-length', uint8Array.byteLength.toString()); | ||
const compressedResponse = new fetchAPI.Response(uint8Array, { | ||
...response, | ||
headers: newHeaders, | ||
}); | ||
utils_js_1.decompressedResponseMap.set(compressedResponse, response); | ||
setResponse(compressedResponse); | ||
const close$ = compressionStream.writable.close(); | ||
serverContext.waitUntil?.(close$); | ||
}); | ||
} | ||
} | ||
const newHeaders = new fetchAPI.Headers(response.headers); | ||
newHeaders.set('content-encoding', supportedEncoding); | ||
newHeaders.delete('content-length'); | ||
const compressedBody = response.body.pipeThrough(compressionStream, { | ||
signal: request.signal, | ||
}); | ||
const compressedBody = response.body.pipeThrough(compressionStream); | ||
const compressedResponse = new fetchAPI.Response(compressedBody, { | ||
@@ -77,1 +108,31 @@ status: response.status, | ||
} | ||
function collectReadableValues(readable) { | ||
const values = []; | ||
readable.on('data', value => values.push(value)); | ||
return new Promise((resolve, reject) => { | ||
readable.once('end', () => resolve(values)); | ||
readable.once('error', reject); | ||
}); | ||
} | ||
async function collectAsyncIterableValues(asyncIterable) { | ||
const values = []; | ||
for await (const value of asyncIterable) { | ||
values.push(value); | ||
} | ||
return values; | ||
} | ||
async function collectReadableStreamValues(readableStream) { | ||
const reader = readableStream.getReader(); | ||
const values = []; | ||
while (true) { | ||
const { done, value } = await reader.read(); | ||
if (done) { | ||
reader.releaseLock(); | ||
break; | ||
} | ||
else if (value) { | ||
values.push(value); | ||
} | ||
} | ||
return values; | ||
} |
@@ -1,2 +0,2 @@ | ||
import { decompressedResponseMap, getSupportedEncodings } from '../utils.js'; | ||
import { decompressedResponseMap, getSupportedEncodings, isAsyncIterable, isReadable, } from '../utils.js'; | ||
export function useContentEncoding() { | ||
@@ -46,4 +46,5 @@ const encodingMap = new WeakMap(); | ||
}, | ||
onResponse({ request, response, setResponse, fetchAPI }) { | ||
if (response.body) { | ||
onResponse({ request, response, setResponse, fetchAPI, serverContext }) { | ||
// Hack for avoiding to create whatwg-node to create a readable stream until it's needed | ||
if (response['bodyInit'] || response.body) { | ||
const encodings = encodingMap.get(request); | ||
@@ -54,8 +55,38 @@ if (encodings) { | ||
const compressionStream = new fetchAPI.CompressionStream(supportedEncoding); | ||
// To calculate final content-length | ||
const contentLength = response.headers.get('content-length'); | ||
if (contentLength) { | ||
const bufOfRes = response._buffer; | ||
if (bufOfRes) { | ||
const writer = compressionStream.writable.getWriter(); | ||
const write$ = writer.write(bufOfRes); | ||
serverContext.waitUntil?.(write$); | ||
const close$ = writer.close(); | ||
serverContext.waitUntil?.(close$); | ||
const uint8Arrays$ = isReadable(compressionStream.readable['readable']) | ||
? collectReadableValues(compressionStream.readable['readable']) | ||
: isAsyncIterable(compressionStream.readable) | ||
? collectAsyncIterableValues(compressionStream.readable) | ||
: collectReadableStreamValues(compressionStream.readable); | ||
return uint8Arrays$.then(uint8Arrays => { | ||
const chunks = uint8Arrays.flatMap(uint8Array => [...uint8Array]); | ||
const uint8Array = new Uint8Array(chunks); | ||
const newHeaders = new fetchAPI.Headers(response.headers); | ||
newHeaders.set('content-encoding', supportedEncoding); | ||
newHeaders.set('content-length', uint8Array.byteLength.toString()); | ||
const compressedResponse = new fetchAPI.Response(uint8Array, { | ||
...response, | ||
headers: newHeaders, | ||
}); | ||
decompressedResponseMap.set(compressedResponse, response); | ||
setResponse(compressedResponse); | ||
const close$ = compressionStream.writable.close(); | ||
serverContext.waitUntil?.(close$); | ||
}); | ||
} | ||
} | ||
const newHeaders = new fetchAPI.Headers(response.headers); | ||
newHeaders.set('content-encoding', supportedEncoding); | ||
newHeaders.delete('content-length'); | ||
const compressedBody = response.body.pipeThrough(compressionStream, { | ||
signal: request.signal, | ||
}); | ||
const compressedBody = response.body.pipeThrough(compressionStream); | ||
const compressedResponse = new fetchAPI.Response(compressedBody, { | ||
@@ -74,1 +105,31 @@ status: response.status, | ||
} | ||
function collectReadableValues(readable) { | ||
const values = []; | ||
readable.on('data', value => values.push(value)); | ||
return new Promise((resolve, reject) => { | ||
readable.once('end', () => resolve(values)); | ||
readable.once('error', reject); | ||
}); | ||
} | ||
async function collectAsyncIterableValues(asyncIterable) { | ||
const values = []; | ||
for await (const value of asyncIterable) { | ||
values.push(value); | ||
} | ||
return values; | ||
} | ||
async function collectReadableStreamValues(readableStream) { | ||
const reader = readableStream.getReader(); | ||
const values = []; | ||
while (true) { | ||
const { done, value } = await reader.read(); | ||
if (done) { | ||
reader.releaseLock(); | ||
break; | ||
} | ||
else if (value) { | ||
values.push(value); | ||
} | ||
} | ||
return values; | ||
} |
{ | ||
"name": "@whatwg-node/server", | ||
"version": "0.10.0-alpha-20250205010145-5ab5edf75d82f8174c999dc970ad9a042ea5547d", | ||
"version": "0.10.0-alpha-20250205010234-c9a7e9a3e2e8b8de7e4519f3119e6b874c5b996f", | ||
"description": "Fetch API compliant HTTP Server adapter", | ||
@@ -5,0 +5,0 @@ "sideEffects": false, |
169289
3295