@ledsun blog

無味の味は佳境に入らざればすなわち知れず

Async::WebSocket gemの素振り その6

Async::WebSocket gemの素振り その5 - @ledsun blog::Async::WebSocket::Response.forが気になりました。

::Async::WebSocket::Response.forソースコードです。

def self.for(request, headers = nil, **options, &body)
    if request.version =~ /http\/1/i
        return UpgradeResponse.new(request, headers, **options, &body)
    elsif request.version =~ /http\/2/i
        return ConnectResponse.new(request, headers, **options, &body)
    end
    
    raise UnsupportedVersionError, "Unsupported HTTP version: #{request.version}!"
end

実態は::Async::WebSocket::UpgradeResponseインスタンスを返すだけです。 現在の実験環境はHTTP1です。 ::Async::WebSocket::UpgradeResponseまで展開してみます。

require 'async/http/body/hijack'
require 'async/websocket/connection'
require 'protocol/websocket/framer'
require 'protocol/websocket/extensions'
require "protocol/rack/request"
require "protocol/rack/adapter"

class App
  def self.response(request, &block)
    headers = ::Protocol::HTTP::Headers.new
    accept_nounce = request.headers[::Protocol::WebSocket::Headers::SEC_WEBSOCKET_KEY]&.first
    headers.add ::Protocol::WebSocket::Headers::SEC_WEBSOCKET_ACCEPT,
                ::Protocol::WebSocket::Headers::Nounce.accept_digest(accept_nounce)

    body = Async::HTTP::Body::Hijack.wrap(request, &block)

    ::Protocol::HTTP::Response.new(request.version, 101, headers, body, ::Protocol::WebSocket::Headers::PROTOCOL)
  end

  def self.call(env)
    req = ::Protocol::Rack::Request[env]
    res = self.response(req) do |stream|
      framer = ::Protocol::WebSocket::Framer.new(stream)
      conn = ::Async::WebSocket::Connection.call framer,
                                                 nil,
                                                 ::Protocol::WebSocket::Extensions::Server.default

      # Echo back!
      while message = conn.read
        conn.write message
      end

      res
    end

    Protocol::Rack::Adapter.make_response(env, res)
  end
end

run App

リクエストのSec-WebSocket-Keyヘッダーの値のダイジェストをレスポンスのSec-WebSocket-Acceptヘッダーで返します。 RubyでWebSocketサーバー - @ledsun blogでやったことを思い出しました。

WebSocketメッセージを処理するブロックはAsync::HTTP::Body::Hijack.wrapで動くようです。