Description
Bug
RSocketServer accepts a fragmentation.maxOutboundFragmentSize config, but it only applies to
the requester side (server-initiated requests). The responder side (server responses to client
requests) always uses fragmentSize=0, meaning responses are never fragmented.
This causes a crash when the server tries to send a response larger than 16MB (the RSocket frame
length field is 24-bit, max 16,777,215 bytes), because writeUInt24BE overflows:
RangeError: The value of "value" is out of range. It must be >= 0 and <= 255.
at writeUInt24BE (rsocket-core/src/Codecs.ts:80)
at serializeFrameWithLength (rsocket-core/src/Codecs.ts:161)
Root Cause
Two issues in packages/rsocket-core/src/RSocketServer.ts:
-
Constructor doesn't store fragmentation: The fragmentation field from ServerConfig is
never assigned to this, so this.fragmentation is always undefined.
-
DefaultStreamRequestHandler hardcodes fragmentSize=0: Even if this.fragmentation were
stored, the stream handler is created with fragmentSize=0 instead of using the configured value.
Fix
constructor(config: ServerConfig) {
this.acceptor = config.acceptor;
this.transport = config.transport;
this.lease = config.lease;
+ this.fragmentation = config.fragmentation;
this.serverSideKeepAlive = config.serverSideKeepAlive;
-const streamsHandler = new DefaultStreamRequestHandler(responder, 0);
+const streamsHandler = new DefaultStreamRequestHandler(
+ responder,
+ this.fragmentation?.maxOutboundFragmentSize ?? 0,
+);
Impact
Without this fix, any RSocket server using rsocket-core v1 cannot send responses larger than ~16MB.
The fragmentation infrastructure (Fragmenter.ts, RequestResponseResponderStream.onNext) already
exists and works correctly — it just never receives a non-zero fragmentSize.
Reproduction
const server = new RSocketServer({
transport: new TcpServerTransport({ listenOptions: { port: 9090 } }),
fragmentation: { maxOutboundFragmentSize: (1 << 24) - 1 - 9 }, // This is ignored!
acceptor: {
async accept() {
return {
requestResponse(payload, responder) {
// This crashes if payload response > 16MB
responder.onNext({ data: Buffer.alloc(20_000_000) }, true);
return { cancel() {}, onExtension() {} };
},
};
},
},
});
Files Changed
packages/rsocket-core/src/RSocketServer.ts (2 lines)
Description
Bug
RSocketServeraccepts afragmentation.maxOutboundFragmentSizeconfig, but it only applies tothe requester side (server-initiated requests). The responder side (server responses to client
requests) always uses
fragmentSize=0, meaning responses are never fragmented.This causes a crash when the server tries to send a response larger than 16MB (the RSocket frame
length field is 24-bit, max 16,777,215 bytes), because
writeUInt24BEoverflows:Root Cause
Two issues in
packages/rsocket-core/src/RSocketServer.ts:Constructor doesn't store
fragmentation: Thefragmentationfield fromServerConfigisnever assigned to
this, sothis.fragmentationis alwaysundefined.DefaultStreamRequestHandlerhardcodesfragmentSize=0: Even ifthis.fragmentationwerestored, the stream handler is created with
fragmentSize=0instead of using the configured value.Fix
constructor(config: ServerConfig) { this.acceptor = config.acceptor; this.transport = config.transport; this.lease = config.lease; + this.fragmentation = config.fragmentation; this.serverSideKeepAlive = config.serverSideKeepAlive;Impact
Without this fix, any RSocket server using rsocket-core v1 cannot send responses larger than ~16MB.
The fragmentation infrastructure (
Fragmenter.ts,RequestResponseResponderStream.onNext) alreadyexists and works correctly — it just never receives a non-zero
fragmentSize.Reproduction
Files Changed
packages/rsocket-core/src/RSocketServer.ts(2 lines)