Async::WebSocket gemの素振り その6 - @ledsun blogでAsync::HTTP::Body::Hijack.wrapを使うようになりました。
今回はAsync::HTTP::Body::Hijack.wrapを展開します。
require "protocol/http/body/readable" require "protocol/http/body/writable" require "protocol/http/body/stream" require 'async/websocket/connection' require 'protocol/websocket/framer' require 'protocol/websocket/extensions' require "protocol/rack/request" require "protocol/rack/adapter" class MyReadable < ::Protocol::HTTP::Body::Readable def initialize(block, input) @block = block @input = input @task = nil @stream = nil @output = nil end # We prefer streaming directly as it's the lowest overhead. def stream? true end def call(stream) @block.call(stream) end attr :input # Has the producer called #finish and has the reader consumed the nil token? def empty? @output&.empty? end def ready? @output&.ready? end # Read the next available chunk. def read @output = ::Protocol::HTTP::Body::Writable.new @stream = ::Protocol::HTTP::Body::Stream.new(@input, @output) @block.call(@stream) @output.read end def inspect "\#<#{self.class} #{@block.inspect}>" end def to_s "<Hijack #{@block.class}>" end end class App def self.call(env) req = ::Protocol::Rack::Request[env] # Set a handler for received messages handler = lambda 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 end body = MyReadable.new(handler, req.body) # Prepare response headers response_headers = ::Protocol::HTTP::Headers.new accept_nounce = req.headers[::Protocol::WebSocket::Headers::SEC_WEBSOCKET_KEY]&.first response_headers.add ::Protocol::WebSocket::Headers::SEC_WEBSOCKET_ACCEPT, ::Protocol::WebSocket::Headers::Nounce.accept_digest(accept_nounce) # Return response res = ::Protocol::HTTP::Response.new(req.version, 101, response_headers, body, ::Protocol::WebSocket::Headers::PROTOCOL) Protocol::Rack::Adapter.make_response(env, res) end end run App
Readableクラスが増えて長くなりました。
Async::WebSocketへの依存が1つ減りました。
残るは::Async::WebSocket::Connection.callです。
::Async::WebSocket::Connection.callを展開したら、もう少しシンプルにできそうです。