Add brotli/gzip compression for dynamic SSR and API responses#2409
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8167c33878
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const port = process.env.PORT ?? (path ? undefined : "3000"); | ||
| const shutdownTimeout = parseInt(process.env.SHUTDOWN_TIMEOUT ?? "30", 10); | ||
|
|
||
| server.listen({ path, host, port }, () => { |
There was a problem hiding this comment.
Omit port when listening on SOCKET_PATH
When SOCKET_PATH is configured, this call still passes a port property in the listen options (process.env.PORT if set, otherwise undefined). In Node, an options object with port: undefined is treated as a TCP listen on port 0 rather than a Unix-domain socket, so socket-based deployments bind a random TCP port while logging the socket path and never create the expected socket. Build the listen options without host/port whenever path is set.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed empirically (a listen options object with host/port present binds a random TCP port even when path is set) and fixed in 11657b4: the socket path is now the only key in the listen options when SOCKET_PATH is configured. Verified end to end: the socket file is created and serves HTTP 200 over curl --unix-socket.
What
Dynamic responses (SSR HTML and all API JSON) currently ship with no content encoding: the landing page document is ~318KB on the wire and gzips to ~33KB. Static assets are already precompressed by adapter-node, but nothing compresses what the SvelteKit handler generates at runtime.
This adds a small custom server (
server.js) wrappingbuild/handler.jswith polka and@polka/compression, following the pattern recommended by the SvelteKit adapter-node docs. The Dockerfile and entrypoint now run it instead ofbuild/index.js.Details worth calling out:
application/jsonl(token streaming) andtext/event-stream(conversation updates) are explicitly excluded from compression. Both packages' default filters would match them, and compressing them buffers tokens inside the zlib transform instead of delivering them as produced.Vary: Accept-Encodingis merged into response headers at write time. SvelteKit sets its ownVary: Accepton routes that have both a page and a co-located+server.ts, which would otherwise overwrite a header set up front by middleware (verified against the built manifest).SHUTDOWN_TIMEOUT) andSOCKET_PATH/HOST/PORThandling are reimplemented, since a custom server does not inherit adapter-node's lifecycle behavior. The old-- --host --portCLI args in entrypoint.sh were dead code (the generated index.js only reads env vars).Measured
Against a local production build with a real database:
Verification
Vary: Accept, Accept-Encodingpresent on page+endpoint routes (e.g./conversation/[id])Accept-Encoding: gzip, brsentPart of a performance series measured from a live profile of hf.co/chat. Each PR is file-disjoint and merges independently, in any order: #2409 (compression), #2410 (models payload), #2411 (markdown pipeline), #2412 (conversation switching), #2413 (stream update batching), #2414 (send handler DB), #2415 (sidebar hydration).