Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/runtime/http/websockets.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ The `.send(message)` method of `ServerWebSocket` returns a `number` indicating t

### Timeouts and limits

By default, Bun closes a WebSocket connection that has been idle for 120 seconds. Configure this with the `idleTimeout` parameter.
By default, Bun closes a WebSocket connection that has been idle for 120 seconds. Configure this with the `idleTimeout` parameter. It takes a whole number of seconds from `0` to `960`. `0` disables the timeout entirely, and values from `1` to `7` are rounded up to `8`.

```ts
Bun.serve({
Expand Down
3 changes: 3 additions & 0 deletions packages/bun-types/serve.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,9 @@ declare module "bun" {
* Sets the number of seconds to wait before timing out a connection
* due to no messages or pings.
*
* The maximum value is `960`. `0` disables the timeout. Values from
* `1` to `7` are rounded up to `8`.
*
* @default 120
*/
idleTimeout?: number;
Expand Down
17 changes: 9 additions & 8 deletions src/runtime/server/WebSocketServerContext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,18 +331,19 @@ pub(crate) fn on_create(
)));
}

let mut idle_timeout: u16 = value.to_int64().max(0) as u16;
if idle_timeout > 960 {
let seconds = value.to_int64();
if !(0..=960).contains(&seconds) {
return Err(global_object.throw_invalid_arguments(format_args!(
"websocket expects idleTimeout to be 960 or less"
"websocket expects idleTimeout to be between 0 and 960"
)));
} else if idle_timeout > 0 {
// uws does not allow idleTimeout to be between (0, 8),
// since its timer is not that accurate, therefore round up.
idle_timeout = idle_timeout.max(8);
}

server.idle_timeout = idle_timeout;
// uws::App::ws terminates on an idleTimeout in 1..8 or above 960.
server.idle_timeout = if seconds > 0 {
(seconds as u16).max(8)
} else {
0
};
}
}
if let Some(value) = object.get(global_object, "backpressureLimit")? {
Expand Down
35 changes: 35 additions & 0 deletions test/js/bun/http/bun-serve-args.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,41 @@ describe("Bun.serve websocket options", () => {
server.stop();
});

// idleTimeout was narrowed to 16 bits before the range check, so 65537
// became 1 second and 2 ** 31 became 0 (no timeout at all).
test("websocket idleTimeout outside 0..960 throws", () => {
for (const idleTimeout of [65537, 2 ** 31, 961, -1]) {
expect(() =>
serve({
port: 0,
websocket: {
message(ws, message) {},
idleTimeout,
},
fetch() {
return new Response("ok");
},
}),
).toThrow("websocket expects idleTimeout to be between 0 and 960");
}
});

test("websocket idleTimeout accepts 0 and 960", () => {
for (const idleTimeout of [0, 960]) {
using server = serve({
port: 0,
websocket: {
message(ws, message) {},
idleTimeout,
},
fetch() {
return new Response("ok");
},
});
server.stop();
}
});

test("websocket with compression options", () => {
using server = serve({
port: 0,
Expand Down
Loading