http-body.ts 1.3 KB

123456789101112131415161718192021222324252627282930
  1. import { Effect, Stream } from "effect"
  2. import { HttpClientResponse } from "effect/unstable/http"
  3. export const collectBoundedResponseBody = (
  4. response: HttpClientResponse.HttpClientResponse,
  5. maximumBytes: number,
  6. tooLarge: () => Error,
  7. ) =>
  8. Effect.gen(function* () {
  9. const contentLength = response.headers["content-length"]
  10. const parsedSize = contentLength ? Number.parseInt(contentLength, 10) : undefined
  11. const declaredSize =
  12. parsedSize !== undefined && Number.isSafeInteger(parsedSize) && parsedSize >= 0 ? parsedSize : undefined
  13. if (declaredSize !== undefined && declaredSize > maximumBytes) return yield* Effect.fail(tooLarge())
  14. let body = Buffer.allocUnsafe(Math.min(maximumBytes, declaredSize || 64 * 1024))
  15. let size = 0
  16. yield* Stream.runForEach(response.stream, (chunk) => {
  17. if (chunk.byteLength === 0) return Effect.void
  18. if (size + chunk.byteLength > maximumBytes) return Effect.fail(tooLarge())
  19. if (size + chunk.byteLength > body.byteLength) {
  20. const grown = Buffer.allocUnsafe(Math.min(maximumBytes, Math.max(size + chunk.byteLength, body.byteLength * 2)))
  21. body.copy(grown, 0, 0, size)
  22. body = grown
  23. }
  24. body.set(chunk, size)
  25. size += chunk.byteLength
  26. return Effect.void
  27. })
  28. return body.subarray(0, size)
  29. })