30 lines
1.3 KiB
TypeScript
30 lines
1.3 KiB
TypeScript
import { Effect, Stream } from "effect"
|
|
import { HttpClientResponse } from "effect/unstable/http"
|
|
|
|
export const collectBoundedResponseBody = (
|
|
response: HttpClientResponse.HttpClientResponse,
|
|
maximumBytes: number,
|
|
tooLarge: () => Error,
|
|
) =>
|
|
Effect.gen(function* () {
|
|
const contentLength = response.headers["content-length"]
|
|
const parsedSize = contentLength ? Number.parseInt(contentLength, 10) : undefined
|
|
const declaredSize =
|
|
parsedSize !== undefined && Number.isSafeInteger(parsedSize) && parsedSize >= 0 ? parsedSize : undefined
|
|
if (declaredSize !== undefined && declaredSize > maximumBytes) return yield* Effect.fail(tooLarge())
|
|
let body = Buffer.allocUnsafe(Math.min(maximumBytes, declaredSize || 64 * 1024))
|
|
let size = 0
|
|
yield* Stream.runForEach(response.stream, (chunk) => {
|
|
if (chunk.byteLength === 0) return Effect.void
|
|
if (size + chunk.byteLength > maximumBytes) return Effect.fail(tooLarge())
|
|
if (size + chunk.byteLength > body.byteLength) {
|
|
const grown = Buffer.allocUnsafe(Math.min(maximumBytes, Math.max(size + chunk.byteLength, body.byteLength * 2)))
|
|
body.copy(grown, 0, 0, size)
|
|
body = grown
|
|
}
|
|
body.set(chunk, size)
|
|
size += chunk.byteLength
|
|
return Effect.void
|
|
})
|
|
return body.subarray(0, size)
|
|
})
|