Skip to content

Allow selecting individual WebSocket messages for compression - #14127

Merged
mkurz merged 1 commit into
playframework:mainfrom
mkurz:websocket-compression-selector
Jul 24, 2026
Merged

Allow selecting individual WebSocket messages for compression#14127
mkurz merged 1 commit into
playframework:mainfrom
mkurz:websocket-compression-selector

Conversation

@mkurz

@mkurz mkurz commented Jul 24, 2026

Copy link
Copy Markdown
Member

This PR lets applications decide whether each outbound WebSocket text or binary message should be compressed.

WebSocket.Accepted now accepts a shouldCompress selector in both the Scala and Java APIs. The selector receives a CompressionContext containing:

  • The final outbound Play message
  • Its uncompressed payload length in bytes
  • Whether it exceeds play.server.websocket.compression.threshold

Returning true or false overrides the configured threshold for that message.

WebSocket.Accepted(
  flow,
  shouldCompress = context =>
    context.message match {
      case TextMessage(text) if text.contains("secret") => false
      case _ => context.isAboveCompressionThreshold
    }
)

This allows applications to avoid compressing sensitive or already-compressed content while continuing to compress other messages on the same WebSocket connection.

Behavior

The selector:

  • Is evaluated once for each outbound text or binary message
  • Is called only when permessage-deflate was negotiated
  • Can override the configured compression threshold
  • Cannot enable compression when it is disabled globally or for the accepted WebSocket
  • Must not block; an exception fails the WebSocket stream

The default selector preserves the existing threshold behavior.

Implementation

The selector is propagated through message-flow transformations and the Java-to-Scala API bridge.

Both server backends support it:

  • Netty uses its WebSocket extension filter for the final outbound frame.
  • Pekko HTTP passes the decision to its per-message compression API.

Tests

Shared integration tests for both backends verify:

  • Selecting compressed and uncompressed messages on one connection
  • Overriding the threshold in both directions
  • Text and binary messages
  • UTF-8 byte-length calculation
  • Control frames bypassing the selector
  • The selector not being evaluated when compression is disabled or not negotiated
  • Java API propagation
  • Actual compressed wire payloads, not only handshake headers

Scala and Java WebSocket documentation and the Play release highlights are updated.

References

@mkurz
mkurz merged commit fb05b6a into playframework:main Jul 24, 2026
45 checks passed
@mkurz
mkurz deleted the websocket-compression-selector branch July 24, 2026 10:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant