Skip to content
Merged
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
20 changes: 18 additions & 2 deletions v2/ruby/lib/terminalwire/v2/server/rack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ module Server
class Rack
WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"

# WebSocket subprotocols this server speaks, best first. The handshake echoes
# the first one the client also offered (RFC 6455 negotiation) — the v1 handler
# did this (`protocols: ['ws']`) and some edges/proxies (e.g. Fly) drop a
# WebSocket whose Sec-WebSocket-Protocol the server never echoes back.
SUBPROTOCOLS = %w[terminalwire.v2 ws].freeze

# @param cli_class [Class] a Thor CLI that includes Terminalwire::V2::Server::Thor
# @param verbose [Boolean] send full backtraces to the client (dev only)
# @param report [#call, nil] optional callable invoked with unexpected errors
Expand All @@ -56,7 +62,7 @@ def call(env)
# adapter in here — this is the only path that needs the async stack.
# :nocov: Falcon transport wiring — exercised live by the conformance suite, not units.
require "async/websocket/adapters/rack"
Async::WebSocket::Adapters::Rack.open(env) { |connection| ReactorBridge.new(connection, @handler, request: request).run }
Async::WebSocket::Adapters::Rack.open(env, protocols: SUBPROTOCOLS) { |connection| ReactorBridge.new(connection, @handler, request: request).run }
# :nocov:
else
# Threaded server (Puma & friends): hand-roll the upgrade and stream.
Expand Down Expand Up @@ -119,7 +125,17 @@ def websocket?(env)

def upgrade_headers(env)
accept = [Digest::SHA1.digest("#{env['HTTP_SEC_WEBSOCKET_KEY']}#{WS_GUID}")].pack("m0")
{ "upgrade" => "websocket", "connection" => "Upgrade", "sec-websocket-accept" => accept }
headers = { "upgrade" => "websocket", "connection" => "Upgrade", "sec-websocket-accept" => accept }
if (proto = negotiated_subprotocol(env))
headers["sec-websocket-protocol"] = proto
end
headers
end

# The first subprotocol both we and the client support (RFC 6455), or nil.
def negotiated_subprotocol(env)
offered = env["HTTP_SEC_WEBSOCKET_PROTOCOL"].to_s.split(/,\s*/)
(SUBPROTOCOLS & offered).first
end

def upgrade_required
Expand Down
Loading