NodeHttpServer unstable
Node.js implementation of the Effect HttpServer.
This module adapts a supplied Node http.Server or node:http2 server into
Effect's platform-independent HTTP server service. It starts the server with
Node listen options, converts request events into HttpServerRequest
values, writes HttpServerResponse bodies through Node's ServerResponse,
and handles upgrade events by exposing the upgraded socket through
HttpServerRequest.upgrade. It also exports request and upgrade handler
constructors plus layers for the server alone, HTTP support services, the
combined server, configurable options, and tests.
HTTP/2
Pass Http2.createServer() for cleartext HTTP/2 (h2c), or
Http2.createSecureServer({ key, cert, allowHTTP1: true }) to negotiate
HTTP/2 over TLS and fall back to HTTP/1.1 for clients that do not support
it. The protocol is detected per request, so the application sees the same
HttpServerRequest either way:
- pseudo-headers are removed from
headers, andhostis taken from:authority originalUrlandHttpServerRequest.toURLuse the:schemethe client sent, which a client can set as freely asx-forwarded-proto- connection-specific response headers that HTTP/2 forbids, such as
connection,keep-aliveandtransfer-encoding, are dropped silently - a response that fails after its headers were sent resets the stream with
INTERNAL_ERROR, so clients do not mistake a truncated body for a complete one
WebSockets and other upgrades only work over HTTP/1.1, which needs
allowHTTP1 on a TLS server. Trailers are only available through the Node
objects from NodeHttpServerRequest. Tune HTTP/2 limits, such as
maxSessionMemory and settings.maxConcurrentStreams, through the Node
server options.
Constructors
Creates a scoped HttpServer from a Node http.Server or node:http2
server, starts listening with the supplied options, registers request and
upgrade handling, and closes the server during scope finalization with
optional graceful-shutdown control.
Details
Node's own timeouts are set on the server you create, for example
createServer({ requestTimeout: 0, headersTimeout: 60_000 }):
requestTimeout(default 300 seconds) bounds receiving the whole request, headers and body. On expiry Node answers408, or closes the connection if the response has started. It is checked everyconnectionsCheckingInterval(default 30 seconds).0disables it.headersTimeout(default 60 seconds) bounds receiving the headers. Left unset, it is lowered to a smallerrequestTimeout, sorequestTimeout: 0alone disables both. When both are nonzero it may not exceedrequestTimeout.keepAliveTimeout(default 5 seconds) closes connections idle between requests.server.timeout, socket inactivity, is off by default, so a quiet response stream is never cut once the request is received.
Use Stream and Effect timeouts in handlers or middleware for per-route
limits on reading the body or producing the response.
Gotchas
A request counts as received once Node has parsed the whole body, so a
handler that streams a response without reading a large body is still under
requestTimeout and is cut when it expires.
Signature
declare const make: (...args: [evaluate: LazyArg<Server<typeof IncomingMessage, typeof ServerResponse> | Http2Server<typeof IncomingMessage, typeof ServerResponse, typeof Http2ServerRequest, typeof Http2ServerResponse> | Http2SecureServer<typeof IncomingMessage, typeof ServerResponse, typeof Http2ServerRequest, typeof Http2ServerResponse>>, options: Options]) => Effect<{ readonly address: SocketAddress; readonly serve: { <E, R>(effect: Effect<HttpServerResponse, E, R>): Effect<void, never, Scope | Exclude<R, HttpServerRequest>>; <E, R, App extends Effect<HttpServerResponse, any, any>>(effect: Effect<HttpServerResponse, E, R>, middleware: Applied<App, E, R>): Effect<void, never, Scope | Exclude<Services<App>, HttpServerRequest>>; };}, ServeError, Scope>Handlers
makeHandler
unstableCreates a Node request event handler for an Effect HTTP application,
injecting a HttpServerRequest and interrupting the request fiber if the
client closes the response before it finishes. It accepts both HTTP/1.1 and
HTTP/2 compatibility requests.
Gotchas
httpEffect runs uninterruptibly by default, so a client disconnect or
closing scope stops it only once it has produced a response. Wrap it in
Effect.interruptible, or serve interruptible HttpRouter routes, to let
them interrupt it earlier.
Signature
declare function makeHandler<R, E, App extends Effect<HttpServerResponse, any, any> = Effect<HttpServerResponse, E, R>>(httpEffect: Effect<HttpServerResponse, E, R>, options: { readonly middleware?: Applied<App, E, R>; readonly scope: Scope;}): Effect<(nodeRequest: IncomingMessage | Http2ServerRequest, nodeResponse: ServerResponse<IncomingMessage> | Http2ServerResponse<Http2ServerRequest>) => void, never, Exclude<Services<App>, Scope | HttpServerRequest>>makeUpgradeHandler
unstableCreates a Node upgrade event handler for an Effect HTTP application,
exposing the upgraded connection as the request's upgrade effect and
interrupting the request fiber when the socket closes early.
Details
Upgrade: websocket requests complete the WebSocket handshake. Any other
protocol answers 101 Switching Protocols with the requested protocol and
the headers given to upgradeWith, and exposes the raw connection, with
bytes the client sent after the request body first on the reader. Either
response is sent when the socket's reader is acquired. A handler that never
asks for upgrade answers with its response as usual.
The request body can be read before the upgrade. The 101 waits for the
body to end, discarding any unread rest up to the maximum body size. Node 25
and older do not parse the body of an upgrade request, so it is read from
the connection with Content-Length; chunked bodies get
411 Length Required there. Expect: 100-continue is not answered on
upgrade requests.
Gotchas
httpEffect runs uninterruptibly by default, so an early socket close or
closing scope stops it only once it has produced a response. Wrap it in
Effect.interruptible to let them interrupt it earlier.
Signature
declare function makeUpgradeHandler<R, E, App extends Effect<HttpServerResponse, any, any> = Effect<HttpServerResponse, E, R>>(lazyWss: Effect<WebSocketServer>, httpEffect: Effect<HttpServerResponse, E, R>, options: { readonly middleware?: Applied<App, E, R>; readonly scope: Scope;}): Effect<(nodeRequest: IncomingMessage, socket: Duplex, head: Buffer) => void, never, Exclude<Services<App>, Scope | HttpServerRequest>>Layers
Provides a Node HttpServer together with the Node HTTP platform, ETag, and
core platform services required to serve requests.
Details
Node's timeouts are set on the server returned by evaluate; see make.
Signature
declare function layer(evaluate: LazyArg<Server<typeof IncomingMessage, typeof ServerResponse> | Http2Server<typeof IncomingMessage, typeof ServerResponse, typeof Http2ServerRequest, typeof Http2ServerResponse> | Http2SecureServer<typeof IncomingMessage, typeof ServerResponse, typeof Http2ServerRequest, typeof Http2ServerResponse>>, options: Options): Layer<HttpPlatform | Generator | NodeServices | HttpServer, ServeError>layerConfig
unstableProvides a Node HttpServer together with the Node HTTP platform, ETag,
and core Node platform services, reading the listen and shutdown options from
a Config value.
Signature
declare function layerConfig(evaluate: LazyArg<Server<typeof IncomingMessage, typeof ServerResponse> | Http2Server<typeof IncomingMessage, typeof ServerResponse, typeof Http2ServerRequest, typeof Http2ServerResponse> | Http2SecureServer<typeof IncomingMessage, typeof ServerResponse, typeof Http2ServerRequest, typeof Http2ServerResponse>>, options: { readonly backlog?: Config<number | undefined>; readonly disablePreemptiveShutdown?: Config<boolean | undefined>; readonly exclusive?: Config<boolean | undefined>; readonly gracefulShutdownTimeout?: Config<Input | undefined>; readonly handle?: Config<BoundSocket | undefined>; readonly host?: Config<string | undefined>; readonly ipv6Only?: Config<boolean | undefined>; readonly path?: Config<string | undefined>; readonly port?: Config<number | undefined>; readonly readableAll?: Config<boolean | undefined>; readonly reusePort?: Config<boolean | undefined>; readonly signal?: { readonly aborted: Config<boolean>; addEventListener: Config<{ <K extends "abort">(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; }>; dispatchEvent: Config<(event: Event) => boolean>; readonly onabort: Config<(this: AbortSignal, ev: Event) => any | null>; readonly reason: Config<any>; removeEventListener: Config<{ <K extends "abort">(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void; (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; }>; throwIfAborted: Config<() => void>; } | Config<AbortSignal | undefined>; readonly websocket?: Config<Omit<ServerOptions, "port" | "host" | "noServer" | "server" | "path"> | undefined>; readonly writableAll?: Config<boolean | undefined>;} | Config<Options>): Layer<HttpPlatform | Generator | NodeServices | HttpServer, ConfigError | ServeError>layerHttpServices
unstableProvides the Node HTTP support services used by NodeHttpServer, including
the HTTP platform, ETag generator, and core Node platform services.
Signature
declare const layerHttpServices: Layer.Layer<NodeServices.NodeServices | HttpPlatform.HttpPlatform | Etag.Generator>layerServer
unstableProvides an HttpServer by creating and managing a scoped Node
http.Server or node:http2 server with the supplied listen and shutdown
options.
Signature
declare const layerServer: (evaluate: LazyArg<Http.Server | Http2.Http2Server | Http2.Http2SecureServer>, options: Options) => Layer.Layer<HttpServer.HttpServer, ServeError>Options
Options accepted by the Node HttpServer constructors and layers.
Signature
interface Options extends ListenOptions { readonly disablePreemptiveShutdown?: boolean; readonly gracefulShutdownTimeout?: Input; readonly websocket?: Omit<ServerOptions, "port" | "host" | "noServer" | "server" | "path">;}Testing
Provides a test HTTP server listening on an ephemeral port together with a
Fetch-backed HttpClient configured for server integration tests.
Signature
declare const layerTest: Layer.Layer<HttpServer.HttpServer | FileSystem.FileSystem | Path.Path | HttpPlatform.HttpPlatform | Etag.Generator | HttpClient, ServeError, never>