Document that server.publish() compresses by default; pin the compress defaults

Closed
RO
1mo ago
+240-12

Problem

  • server.publish(topic, data) with the compress argument omitted deflates the message for every subscriber that negotiated permessage-deflate. ws.send(), ws.publish() and their text/binary variants send uncompressed unless compress is true.
  • The true default comes from compress_value.unwrap_or(JSValue::TRUE) in src/runtime/server/server_body.rs:1680; the ServerWebSocket methods go through parse_compress_arg in src/runtime/server/ServerWebSocket.rs:298, which yields false when the argument is missing. server.publish() has behaved this way since it was added (8753c483ff, 2022).
  • Neither default is stated anywhere. The .d.ts and the docs describe compression as something you pass true to get, so code calling server.publish() on small messages compresses without the author knowing: a 23 byte payload goes out as 29 bytes with RSV1 set, and uWS deflates it once per subscriber (packages/bun-uws/src/App.h:244 and :470 call ws->send(..., compress) per subscriber).

Fix

  • This PR documents the existing behavior rather than changing it. Flipping the default would silently stop compressing for existing callers; #24593, for example, is a user relying on server.publish() compressing ~109 KB broadcasts with no third argument. If the default should change instead, it is the one line in server_body.rs above plus inverting the new test.
  • packages/bun-types/serve.d.ts: Server.publish JSDoc says compress defaults to true and points at the ServerWebSocket methods that default to false; its explicit-true example becomes an explicit-false one. The six ServerWebSocket send/publish methods say compress defaults to false.
  • docs/runtime/http/websockets.mdx, docs/guides/websocket/compression.mdx, docs/runtime/http/server.mdx: the compression sections state both defaults and show server.publish(topic, data, false). The reference block in websockets.mdx also gains the compress parameter and number return type that ws.publish() already has.
  • test/js/bun/websocket/websocket-server.test.ts, describe("compress argument defaults"): a raw TCP client negotiates permessage-deflate and records the RSV1 bit of each text frame the server writes (inflating the compressed ones). It checks server.publish() with the argument omitted / false / true, ws.publish() omitted / true, and ws.send() omitted / true, plus a second subscriber without the extension and a second test with perMessageDeflate off, where every frame is plain regardless of the argument.
  • Verified: the new tests pass with bun bd test and with the released 1.4.0; changing the server.publish() expectation to compressed: false makes the test fail, so it pins the default. bun test test/integration/bun-types/bun-types.test.ts passes.

Background

  • permessage-deflate (RFC 7692) is a WebSocket extension negotiated during the upgrade. Bun's server offers it when websocket.perMessageDeflate is set; whether a given message is actually deflated is decided per send/publish call via the compress argument, and a compressed message is marked by the RSV1 bit in the first byte of its frame. The argument has no effect on a connection that did not negotiate the extension.
  • server.publish() and ws.publish() both fan out through the uWS topic tree, which calls the per-socket send() once per subscriber with the same compress flag, so a compressed publish costs one deflate per subscriber rather than one per message.