mirror of
https://github.com/stablyai/orca.git
synced 2026-09-23 00:02:29 +00:00
The relay's control lane is a shared 1 MiB budget, and `sendResponse` admitted responses onto it with the fatal default: once the lane was full, admission closed the client. A ~900 KB `fs.listFiles` reply from a large remote workspace therefore took down the whole remote session -- every terminal on it -- rather than failing the one Quick Open request. The substitute `ResponseOverCapacity` frame already there only covered the `legacy-response` lane, because the fatal close beat it to the client. A JSON-RPC response is the droppable class of control frame: it carries an id, so one caller can be told and can retry. `pty.replay` and `notifyControl` keep the fatal default -- they are never re-sent, and a silent drop there desyncs the client with nothing to retry. Both response enqueues now pass `controlOverflow: 'reject'`, so the substitute error is what the caller sees; in the corner where even ~150 bytes will not fit, the caller's own 30s request timeout settles it and the session survives. Old clients are unaffected: they already decode this error code and message generically (`ssh-channel-multiplexer.handleResponse` rejects the pending promise with both), and the frame shape is unchanged. What changes is that a listing which used to drop the connection now returns an error on it.
63 lines
3.1 KiB
TypeScript
63 lines
3.1 KiB
TypeScript
/** Largest window one `fs.readFileRange` response may carry. Derived from the
|
|
* relay writer's admission budget, NOT from the 16 MiB frame cap.
|
|
*
|
|
* A response frame over `DISPATCHER_CONTROL_QUEUE_MAX_BYTES` (1 MiB) is demoted
|
|
* to the `legacy-response` lane, which is refused outright once the producer
|
|
* queue passes `DEFAULT_PRODUCER_QUEUE_MAX_BYTES` (2 MiB) -- an opaque
|
|
* `ResponseOverCapacity` that depends on unrelated load. 256 KiB of raw bytes
|
|
* frames to ~350 KB, so a full-cap response takes the control lane instead.
|
|
*
|
|
* The control lane is a shared budget, not a per-frame one: two full-cap
|
|
* responses fit alongside each other, and the third is refused. That refusal
|
|
* costs the one request -- `sendResponse` admits responses with
|
|
* `controlOverflow: 'reject'` and substitutes a `ResponseOverCapacity` error
|
|
* rather than closing the connection -- but it still turns on unrelated load,
|
|
* so the two-deep headroom that keeps it rare is pinned by a test. Widening
|
|
* the cap spends that headroom, so bigger transfers belong on the ack-paced
|
|
* bulk lane (`fs.readFileStream`) rather than on a wider window here.
|
|
*
|
|
* Raising this value is a wire change even though it is a constant: an older
|
|
* host still advertising `rangedReadVersion: 1` refuses a longer window with an
|
|
* opaque error, so a wider cap needs a version bump to negotiate against.
|
|
*
|
|
* Requests above it are REJECTED, never clamped: a clamped read is
|
|
* indistinguishable from EOF, and callers page by advancing `position`. */
|
|
export const MAX_FILE_RANGE_READ_BYTES = 256 * 1024
|
|
|
|
/** A range request the host will refuse. Separate from a read failure so a
|
|
* caller can tell "I asked for the wrong thing" from "the file is unreadable". */
|
|
export class FileRangeReadRequestError extends Error {
|
|
constructor(message: string) {
|
|
super(message)
|
|
this.name = 'FileRangeReadRequestError'
|
|
}
|
|
}
|
|
|
|
/** The single source of truth for what `fs.readFileRange` accepts. Both sides
|
|
* call it: the client so an invalid request never costs a round trip, the host
|
|
* because `position`/`length` land straight in an fd read and the relay has no
|
|
* request schema. Two independent copies would drift into a client that sends
|
|
* what the host rejects. */
|
|
export function validateFileRangeRequest(
|
|
position: unknown,
|
|
length: unknown
|
|
): { position: number; length: number } {
|
|
if (typeof position !== 'number' || !Number.isSafeInteger(position) || position < 0) {
|
|
throw new FileRangeReadRequestError(
|
|
'fs.readFileRange requires a non-negative safe-integer position'
|
|
)
|
|
}
|
|
if (typeof length !== 'number' || !Number.isSafeInteger(length) || length <= 0) {
|
|
throw new FileRangeReadRequestError('fs.readFileRange requires a positive integer length')
|
|
}
|
|
if (length > MAX_FILE_RANGE_READ_BYTES) {
|
|
throw new FileRangeReadRequestError(
|
|
`fs.readFileRange length ${length} exceeds the ${MAX_FILE_RANGE_READ_BYTES}-byte limit`
|
|
)
|
|
}
|
|
if (position > Number.MAX_SAFE_INTEGER - (length - 1)) {
|
|
throw new FileRangeReadRequestError('fs.readFileRange window exceeds safe-integer offsets')
|
|
}
|
|
return { position, length }
|
|
}
|