A handler that throws synchronously escaped the mergeMap in the
socket.io adapter's bindMessageHandlers and errored the stream, and a
handler whose error filter rethrows rejects the same way in both
adapters: the stream dies, that event goes silent for the client and
RxJS reports an unhandled error.
Both adapters now defer the handler call and catchError the stream, so
a sync throw or a rejected handler only drops that one message. A
filter that rethrows is an app bug, so the failure gets logged through
the adapter logger instead of being swallowed silently.
Backport of 4247f6e5d0 (#17666) to v11.
WsAdapter serialized handler responses with a bare JSON.stringify inside
the subscriber callback, and IoAdapter called socket.emit/ack there too.
JSON.parse accepts arbitrarily deep input while JSON.stringify and
socket.io's recursive encoder overflow the stack on it, so a handler
echoing client data let a single message throw from the subscriber,
which RxJS rethrows asynchronously and which terminates the process.
Circular references and BigInts crashed the same way.
Catch and log serialization failures so the offending reply is dropped
and the connection and process keep running.
Backport of 7e961f8b52 to v11.
Backport of #17769. multer 2.4.0 changed the LIMIT_UNEXPECTED_FILE
message, so matching on error.message turned a file sent under an
unexpected field name into a 500 instead of a 400.
Backport of the shutdown and memory-bound fixes from 12.x.
"ServerTCP#close" only called "net.Server#close", which stops the server
from accepting new connections but leaves established ones untouched. A
connection opened before the shutdown therefore survived it: the process
was kept alive by the dangling socket long after "app.close()" resolved,
and messages arriving on that connection were still dispatched to message
handlers. Accepted sockets are now tracked and destroyed when the server
closes.
Two further paths let a single peer grow the process heap without limit.
A peer could declare a packet length, send part of the payload and then
go silent, and nothing ever reaped the connection. On the sending side
the return value of "socket.write" was discarded, so a peer that issued
requests without reading the responses made the process queue every
response in memory.
An idle timer now drops a peer that stops sending mid-packet. It is
refreshed on every read, so a slow but progressing transfer is never
interrupted and a peer sitting idle between packets is left alone.
Reading is suspended while a peer's outgoing buffer is backed up, and
responses queued for a peer that reads nothing are capped. Both limits
are configurable on the server and the client through the new
"incompleteMessageTimeout" and "maxSendBufferSize" options, and either
can be turned off by setting it to 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"JSON.stringify" throws a RangeError for deeply nested message patterns.
The TCP and RMQ servers stringified client-controlled patterns without
guarding against it, and the resulting unhandled promise rejection
terminated the process.
Patterns are now stringified safely (falling back to a sentinel that
matches no handler) and rejected message handling is routed to
"handleError" instead of being left unhandled.
Backport of aa97b5144d (master).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Requests using an absolute-form request target ("GET http://host/path")
were routed to the handler by Fastify while path-scoped middleware, which
matched against the raw request target, never ran.
- replace the inlined @fastify/middie fork with the upstream package
(9.3.4), which resolves absolute-form targets before matching and
already contains the decoded-path fix the fork was carrying
- resolve absolute-form targets in the adapter's own route check
- fold the deprecated top-level Fastify router options into
"routerOptions" so that @fastify/middie normalizes request paths the
same way the router does
Backport of dda252090e (master).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A lazily loaded `DynamicModule` whose dynamic metadata carries `imports`
never had those imports scanned, so their providers could not resolve.
`NestContainer#setModule` pre-registers a dynamic module's `imports` via
`addDynamicMetadata` -> `addDynamicModules` -> `addModule` before the
scanner recurses into them. Those modules therefore report
`inserted: false`, and the lazy skip condition treated them as already
loaded, leaving them registered but never scanned or instantiated.
Skip only modules that were actually instantiated, and bind global
providers on every lazy pass: a rescanned module reports
`inserted: false`, so `lazy && moduleInserted` skipped binding `@Global`
providers into it.
Closes#17462