ruby.wasmからのWebSocket - @ledsun blog では、faye-websocket-rubyを使いました。 このWebSocketサーバーを GitHub - socketry/async-websocket: Asynchronous WebSocket client and server, supporting HTTP/1 and HTTP/2 for Ruby. に変えてみましょう。
環境構築
bundle init bundle add falcon async-websocket
WebSocketサーバーの作成
https://github.com/socketry/async-websocket/blob/534cad73595292b82b88de3b66d10773106c254d/examples/rack/config.ru にあるサンプルを参考にします。
#!/usr/bin/env -S falcon serve --bind http://127.0.0.1:7070 --count 1 -c # frozen_string_literal: true require 'async/websocket/adapters/rack' app = lambda do |env| response = Async::WebSocket::Adapters::Rack.open(env) do |connection| while message = connection.read connection.write message end end or [404, {}, []] end run app
ruby.wasmのWebSocketクライアントを含んだindex.htmlを返せるようにします。
require 'async/websocket/adapters/rack' app = lambda do |env| response = Async::WebSocket::Adapters::Rack.open(env) do |connection| while message = connection.read connection.write message end end or [200, {'Content-Type' => 'text/html'}, [File.read('index.html')]] end run app
index.htmlは前回使った物をそのまま使います。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <script src="https://cdn.jsdelivr.net/npm/@ruby/[email protected]/dist/browser.script.iife.js"></script> <script type="text/ruby"> require "js" ws = JS.global[:WebSocket].new("ws://localhost:9292") ws[:onopen] = -> (event) { ws.call(:send, ["Hello, world! from Ruby"]) } ws[:onmessage] = ->(event) { p event[:data] } </script> </html>
動作確認
bundle exec falcon serve --bind "http://localhost:9292"
コマンドで、WebSocketサーバーを起動します。
ledsun@MSI:~/d/async_websocket►bundle exec falcon serve --bind "http://localhost:9292" 0.0s info: Falcon::Command::Serve [oid=0xf50] [ec=0xf78] [pid=135904] [2024-08-28 16:22:36 +0900] | Falcon v0.47.10 taking flight! Using Async::Container::Forked {:count=>8, :restart=>true}. | - Binding to: #<Falcon::Endpoint http://localhost:9292/ {}> | - To terminate: Ctrl-C or kill 135904 | - To reload configuration: kill -HUP 135904 0.03s info: Falcon::Service::Server [oid=0xfc8] [ec=0xf78] [pid=135904] [2024-08-28 16:22:36 +0900] | Starting server on #<Falcon::Endpoint http://localhost:9292/ {}>
ブラウザで http://localhost:9292/ を開きます。 開発コンソールでWebSocketの通信を確認します。
成功しています。 async-websocketの素振りをした上に、Falconデビューを果たしました。
おまけ
ブラウザを閉じるとWebSocketサーバーで次のようなエラーが表示されます。
4m warn: Async::Task: Reading HTTP/1.1 requests for Async::HTTP::Protocol::HTTP1::Server. [oid=0x1180] [ec=0x11a8] [pid=136177] [2024-08-28 16:50:08 +0900] | Task may have ended with unhandled exception. | Protocol::WebSocket::ClosedError: Protocol::WebSocket::ClosedError | → /home/ledsun/.rbenv/versions/3.4-dev/lib/ruby/gems/3.4.0+0/gems/protocol-websocket-0.15.0/lib/protocol/websocket/connection.rb:165 in 'Protocol::WebSocket::Connection#receive_close' | /home/ledsun/.rbenv/versions/3.4-dev/lib/ruby/gems/3.4.0+0/gems/protocol-websocket-0.15.0/lib/protocol/websocket/close_frame.rb:64 in 'Protocol::WebSocket::CloseFrame#apply' | /home/ledsun/.rbenv/versions/3.4-dev/lib/ruby/gems/3.4.0+0/gems/protocol-websocket-0.15.0/lib/protocol/websocket/connection.rb:111 in 'Protocol::WebSocket::Connection#read_frame' | /home/ledsun/.rbenv/versions/3.4-dev/lib/ruby/gems/3.4.0+0/gems/protocol-websocket-0.15.0/lib/protocol/websocket/connection.rb:272 in 'Protocol::WebSocket::Connection#read' | config.ru:8 in 'block (4 levels) in <top (required)>' | /home/ledsun/.rbenv/versions/3.4-dev/lib/ruby/gems/3.4.0+0/gems/async-websocket-0.28.0/lib/async/websocket/adapters/http.rb:41 in 'block in Async::WebSocket::Adapters::HTTP.open' | /home/ledsun/.rbenv/versions/3.4-dev/lib/ruby/gems/3.4.0+0/gems/async-http-0.71.0/lib/async/http/body/hijack.rb:39 in 'Async::HTTP::Body::Hijack#call' | /home/ledsun/.rbenv/versions/3.4-dev/lib/ruby/gems/3.4.0+0/gems/protocol-rack-0.6.0/lib/protocol/rack/body/streaming.rb:56 in 'Method#call' | /home/ledsun/.rbenv/versions/3.4-dev/lib/ruby/gems/3.4.0+0/gems/protocol-rack-0.6.0/lib/protocol/rack/body/streaming.rb:56 in 'Protocol::Rack::Body::Streaming#call' | /home/ledsun/.rbenv/versions/3.4-dev/lib/ruby/gems/3.4.0+0/gems/async-http-0.71.0/lib/async/http/protocol/http1/server.rb:72 in 'block in Async::HTTP::Protocol::HTTP1::Server#each' | /home/ledsun/.rbenv/versions/3.4-dev/lib/ruby/gems/3.4.0+0/gems/async-2.16.1/lib/async/task.rb:318 in 'Async::Task#defer_stop' | /home/ledsun/.rbenv/versions/3.4-dev/lib/ruby/gems/3.4.0+0/gems/async-http-0.71.0/lib/async/http/protocol/http1/server.rb:57 in 'Async::HTTP::Protocol::HTTP1::Server#each' | /home/ledsun/.rbenv/versions/3.4-dev/lib/ruby/gems/3.4.0+0/gems/async-http-0.71.0/lib/async/http/server.rb:50 in 'Async::HTTP::Server#accept' | /home/ledsun/.rbenv/versions/3.4-dev/lib/ruby/gems/3.4.0+0/gems/io-endpoint-0.13.1/lib/io/endpoint/wrapper.rb:182 in 'block (2 levels) in IO::Endpoint::Wrapper#accept' | /home/ledsun/.rbenv/versions/3.4-dev/lib/ruby/gems/3.4.0+0/gems/async-2.16.1/lib/async/task.rb:197 in 'block in Async::Task#run' | /home/ledsun/.rbenv/versions/3.4-dev/lib/ruby/gems/3.4.0+0/gems/async-2.16.1/lib/async/task.rb:422 in 'block in Async::Task#schedule'
WebSocketコネクションのcloseイベントをハンドリングしないと行けなさそうです。