@dcl/http-server
v2.2.1
Published
http server component
Readme
@dcl/http-server
forked from https://github.com/well-known-components/http-server
Server options
createServerComponent(components, options) accepts a Partial<IHttpServerOptions>
to tune the underlying Node server:
import { createServerComponent } from '@dcl/http-server'
const server = await createServerComponent(
{ config, logs },
{
// Reject request bodies larger than 1 MiB.
maxBodySize: 1024 * 1024,
// Abort a request whose headers + body don't arrive within 30s.
requestTimeout: 30_000,
maxHeadersCount: 100,
maxRequestsPerSocket: 1000,
cors: { origin: '*' }
}
)| Option | Maps to | Description |
| --- | --- | --- |
| maxBodySize | (enforced by this component) | Maximum size, in bytes, of an incoming request body. A request that declares a larger Content-Length is rejected with 413 Payload Too Large before the body is read; a body that omits or under-declares its length (e.g. chunked transfer-encoding) is capped with the same 413 while streaming. Unset means no limit. |
| cors | (CORS middleware) | CORS configuration applied as middleware. |
| keepAliveTimeout | server.keepAliveTimeout | Idle keep-alive socket timeout in ms (default 70000). |
| headersTimeout | server.headersTimeout | Time to receive the complete headers in ms (default 75000). |
| requestTimeout | server.requestTimeout | Time to receive the entire request in ms. 0 disables it. |
| maxHeadersCount | server.maxHeadersCount | Maximum number of request headers. 0 means unlimited. |
| maxRequestsPerSocket | server.maxRequestsPerSocket | Max requests served per keep-alive socket. Unset/0 means unlimited. |
A maxBodySize rejection produces a 413 that a handler can also surface itself: when
a handler reads an over-limit body inside the middleware chain (e.g. await ctx.request.json()),
the read rejects and the error middleware maps it to a 413 response.
maxBodySize must be a positive integer when provided — createServerComponent throws on
0, negative or fractional values (omit the option for "no limit"). When cors is also
configured, the up-front Content-Length rejection still carries the actual-response CORS
headers, so a cross-origin client can read the 413.
Reading the request body under a maxBodySize
The streaming limit is enforced as ctx.request.body is consumed, so it only turns into a 413
if your handler actually reads the body and propagates stream errors. Reading it with
await ctx.request.json() / .text() / .arrayBuffer() or for await (… of ctx.request.body)
does this for you — the read rejects with the 413 and the error middleware maps it to a response.
If you instead pipe ctx.request.body into another stream (e.g. a multipart parser like
busboy), remember that Readable.prototype.pipe does not forward source errors to the
destination. Attach an error handler to the adapted source, or a body-stream error — a client
abort or the maxBodySize limiter emitting its 413 — surfaces as an unhandled error (and can
crash the process):
import { Readable } from 'stream'
const body = Readable.fromWeb(ctx.request.body as any)
// Forward source errors (client abort, or the maxBodySize 413) so the parser rejects cleanly.
body.on('error', (err) => parser.destroy(err))
body.pipe(parser)Requests that declare a Content-Length over the limit — the usual case for multipart/form-data
uploads from browsers and fetch — are rejected up-front regardless of how the body is consumed.
Per-route body-size limits
maxBodySize on createServerComponent is server-wide. For a tighter limit on specific routes, use
createBodySizeLimitMiddleware(bytes) and mount it on those routes:
import { createBodySizeLimitMiddleware } from '@dcl/http-server'
// allow at most 4 KB on this endpoint
router.post('/v1/notes', createBodySizeLimitMiddleware(4096), notesHandler)It enforces the same dual check as the server-wide option: a request declaring a larger
Content-Length is rejected up front with 413 Payload Too Large, and a body that omits or
under-declares its length (e.g. chunked) is capped while streaming — the body read by downstream
handlers errors with a 413 once the limit is crossed. Either 413 sets Connection: close, so an
oversized or stalled client can't tie up the socket. bytes must be a positive integer or the
factory throws. It composes with the server-wide maxBodySize (the global cap still applies first).
Returning a native Response from a handler
Handlers return the structural IResponse (Node Readable/Buffer/string/JSON
bodies). A native Response (as returned by fetch, with a web ReadableStream
body) is not an IResponse — returning one via as any lets
response-transforming middleware (e.g. CORS) silently drop its status and body.
When you have a native Response (typically proxying an upstream fetch), adapt it
at the boundary with fromNativeResponse:
import { fromNativeResponse } from '@dcl/http-server'
router.get('/proxy/:id', async (ctx) => {
const upstream = await fetch(`${CONTENT_SERVER}/${ctx.params.id}`)
return fromNativeResponse(upstream)
})The body is streamed (not buffered), so proxying large responses stays
memory-safe. The passed Response is consumed by the returned stream — don't also
read it with .text()/.json().
Plain handlers are unaffected — keep returning the structural shape directly:
return { status: 200, body: { hello: 'world' } }