Skip to content

fix(gateway): fix protocol handling for safari#4865

Draft
NathanFlurry wants to merge 1 commit into04-29-fix_rivetkit_standardize_startengine_runtime_mode_based_on_configfrom
04-30-fix_gateway_fix_protocol_handling_for_safari
Draft

fix(gateway): fix protocol handling for safari#4865
NathanFlurry wants to merge 1 commit into04-29-fix_rivetkit_standardize_startengine_runtime_mode_based_on_configfrom
04-30-fix_gateway_fix_protocol_handling_for_safari

Conversation

@NathanFlurry
Copy link
Copy Markdown
Member

Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Copy link
Copy Markdown
Member Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@claude
Copy link
Copy Markdown

claude Bot commented May 1, 2026

PR Review: fix(gateway): fix protocol handling for safari

Summary

This PR fixes a Sec-WebSocket-Protocol negotiation bug that caused WebSocket connections to fail in Safari. The root cause was the proxy unconditionally echoing "rivet" as the selected protocol regardless of what the client offered. The fix introduces select_websocket_response_protocol() which properly reads the client's offered protocols and picks the most appropriate one, and relaxes path-based routing to not error on a missing protocol header.


Findings

1. Incomplete data-carrying protocol filtering in the fallback (Low severity)

select_websocket_response_protocol filters rivet_conn_params.* and rivet_token.* in the fallback branch, but the client also sends other data-carrying protocols:

  • rivet_encoding.* (e.g. rivet_encoding.cbor)
  • rivet_target.*
  • rivet_actor.*
  • rivet_test_ack_hook.*

If "rivet" is absent from the offered list (which is the scenario this fix targets), the fallback could echo back rivet_encoding.cbor or rivet_actor.<uuid> as the selected protocol, which would confuse clients that parse the response header.

In practice, the client always sends "rivet" first so the fallback likely never fires for normal Rivet clients. But for browsers that reorder protocols (precisely the Safari case), this edge case is live. A simpler and more robust filter would be:

// Exclude all data-carrying protocol tokens; only echo plain negotiation identifiers
!protocol.starts_with("rivet_")

2. Duplicate SEC_WEBSOCKET_PROTOCOL constant (Minor)

guard/src/routing/mod.rs already defines SEC_WEBSOCKET_PROTOCOL. The PR adds a separate private definition in guard-core/src/proxy_service.rs. These are in different crates so there's no conflict, but it's worth considering whether guard-core should re-export or reference a shared location if these crates are expected to share protocol constants.

3. No tests added (Minor)

Given that the bug is browser-specific and involves protocol ordering behavior, a unit test for select_websocket_response_protocol would be valuable for regressions:

  • Safari-style ordering (data-carrying protocols appear before "rivet")
  • Missing "rivet" in the offered list
  • Empty protocol header → None
  • All protocols are data-carrying → None

What's correct

  • The pegboard_gateway change is correct: path-based routing extracts the token from the URL path, so a missing Sec-WebSocket-Protocol header is legitimately optional there. Returning Ok(None) rather than an error is the right call.
  • The fix is applied symmetrically to both WebSocket response paths in proxy_service.rs (the error-proxy path and the normal path).
  • Excluding rivet_token.* and rivet_conn_params.* from the echoed response header is correct — those values should never be reflected back to the client.
  • Using named constants instead of raw string literals is good practice.

Summary

The fix is sound and directly addresses the Safari compatibility issue. The main concern worth a follow-up is the incomplete filtering of data-carrying protocol prefixes (rivet_encoding.*, rivet_target.*, rivet_actor.*) in the fallback branch — extending the filter to !protocol.starts_with("rivet_") would close that gap more completely.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 1, 2026

Preview packages published to npm

Install with:

npm install rivetkit@pr-4865

All packages published as 0.0.0-pr.4865.b488635 with tag pr-4865.

Engine binary is shipped via @rivetkit/engine-cli on linux-x64-musl, linux-arm64-musl, darwin-x64, and darwin-arm64. Windows users should use the release installer or set RIVET_ENGINE_BINARY.

Docker images:

docker pull rivetdev/engine:slim-b488635
docker pull rivetdev/engine:full-b488635
Individual packages
npm install rivetkit@pr-4865
npm install @rivetkit/react@pr-4865
npm install @rivetkit/rivetkit-napi@pr-4865
npm install @rivetkit/workflow-engine@pr-4865

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