### Install async-websocket Gem Source: https://github.com/socketry/async-websocket/blob/main/guides/getting-started/readme.md This command adds the async-websocket gem to your project's Gemfile using Bundler. ```bash $ bundle add async-websocket ``` -------------------------------- ### Start Rails Server Source: https://github.com/socketry/async-websocket/blob/main/guides/rails-integration/readme.md Starts the Rails development server. This command is used to run the application locally for testing purposes. ```bash rails s ``` -------------------------------- ### Establish WebSocket Client Connection and Communicate in Ruby Source: https://context7.com/socketry/async-websocket/llms.txt This example shows how to establish a WebSocket client connection using Async::WebSocket::Client. It covers sending text and binary messages, flushing the connection, reading incoming messages, and checking connection state. It depends on the 'async', 'async-http', and 'async-websocket' gems. ```ruby require "async" require "async/http/endpoint" require "async/websocket/client" Async do endpoint = Async::HTTP::Endpoint.parse("ws://localhost:8080") Async::WebSocket::Client.connect(endpoint) do |connection| # Check connection protocol (if sub-protocol was negotiated) puts "Protocol: #{connection.protocol}" # Send text message connection.send_text("Hello") # Send binary data connection.send_binary("\x00\x01\x02\x03") # Flush to ensure immediate delivery connection.flush # Read messages (blocks until message arrives or connection closes) while message = connection.read case message when Protocol::WebSocket::TextMessage puts "Text: #{message.to_s}" when Protocol::WebSocket::BinaryMessage puts "Binary: #{message.to_s.bytes}" end end # Connection state puts "Reusable: #{connection.reusable?}" # => false (WebSocket connections are not reusable) # Explicit close (automatic in block form) connection.close end end ``` -------------------------------- ### Rails Adapter: WebSocket Controller Integration (Ruby) Source: https://context7.com/socketry/async-websocket/llms.txt Shows how to integrate WebSockets into a Rails application using the `Async::WebSocket::Adapters::Rails` adapter. This example creates a controller action that handles WebSocket connections, sends structured messages, and processes incoming commands like 'ping' and 'subscribe'. ```ruby # app/controllers/websocket_controller.rb require "async/websocket/adapters/rails" class WebsocketController < ApplicationController # Disable CSRF for WebSocket endpoints skip_before_action :verify_authenticity_token, only: [:connect] def connect self.response = Async::WebSocket::Adapters::Rails.open(request) do |connection| # Send welcome message message = Protocol::WebSocket::TextMessage.generate({ type: "welcome", message: "Connected to Rails WebSocket" }) connection.write(message) connection.flush # Handle incoming messages while incoming = connection.read data = incoming.to_h case data[:action] when "ping" connection.write({action: "pong", timestamp: Time.now.to_i}) when "subscribe" connection.write({action: "subscribed", channel: data[:channel]}) else connection.write({action: "echo", data: data}) end connection.flush end end end end # config/routes.rb Rails.application.routes.draw do # Support both GET (HTTP/1.1) and CONNECT (HTTP/2) methods match "websocket/connect", to: "websocket#connect", via: [:get, :connect] end # Start server with Falcon for production use: # bundle remove puma # bundle add falcon # rails s # => Booting Falcon v0.47.7 ``` -------------------------------- ### Start Falcon Server with HTTPS Source: https://github.com/socketry/async-websocket/blob/main/guides/rails-integration/readme.md Starts the Falcon server, binding it to an HTTPS endpoint. This configuration is required for enabling HTTP/2 WebSockets, as it allows for protocol negotiation. ```bash falcon serve --bind "https://localhost:3000" ``` -------------------------------- ### Configure Rails Routes for HTTP/2 Source: https://github.com/socketry/async-websocket/blob/main/guides/rails-integration/readme.md Adjusts the Rails application's routes to accept both GET and CONNECT HTTP methods for the WebSocket endpoint. This is necessary for supporting HTTP/2 WebSockets. ```ruby Rails.application.routes.draw do # Previously it was this: # get "home/index" match "home/index", to: "home#index", via: [:get, :connect] end ``` -------------------------------- ### Rack Adapter: Basic Echo Server (Ruby) Source: https://context7.com/socketry/async-websocket/llms.txt Sets up a basic WebSocket echo server using the Rack adapter. Any message received by the server is immediately sent back to the client. This is useful for testing WebSocket connectivity. ```ruby # config.ru - Basic echo server require "async/websocket/adapters/rack" app = lambda do |env| Async::WebSocket::Adapters::Rack.open(env) do |connection| while message = connection.read # Echo the message back connection.write(message) connection.flush end end or [404, {}, ["WebSocket connections only"]] end run app ``` -------------------------------- ### WebSocket Server Implementation with Rack & Falcon in Ruby Source: https://github.com/socketry/async-websocket/blob/main/guides/getting-started/readme.md Sets up a WebSocket server using Rack and Falcon. It broadcasts incoming messages to all connected clients and handles client disconnections. The server listens on http://localhost:7070. ```ruby #!/usr/bin/env -S falcon serve --bind http://localhost:7070 --count 1 -c require 'async/websocket/adapters/rack' require 'set' $connections = Set.new run lambda {|env| Async::WebSocket::Adapters::Rack.open(env, protocols: ['ws']) do |connection| $connections << connection while message = connection.read $connections.each do |connection| connection.write(message) connection.flush end ensure $connections.delete(connection) end or [200, {}, ["Hello World"]] } ``` -------------------------------- ### Implement WebSocket Server with HTTP Adapter in Ruby Source: https://context7.com/socketry/async-websocket/llms.txt This snippet demonstrates how to set up a basic WebSocket server using the Async::WebSocket::Adapters::HTTP. It handles incoming connections, reads messages, and echoes them back to the client. It requires the 'async-websocket' and 'async-http' gems. ```ruby require "async/websocket/adapters/http" require "async/http/server" require "async/http/endpoint" endpoint = Async::HTTP::Endpoint.parse("http://localhost:8080") app = lambda do |request| Async::WebSocket::Adapters::HTTP.open(request, protocols: ["v1", "v2"]) do |connection| puts "Client connected with protocol: #{connection.protocol}" while message = connection.read connection.write(message) connection.flush end end or Protocol::HTTP::Response[404, {}, ["Not Found"]] end Async do server = Async::HTTP::Server.new(app, endpoint) server.run end ``` -------------------------------- ### Basic WebSocket Client Implementation in Ruby Source: https://github.com/socketry/async-websocket/blob/main/guides/getting-started/readme.md Implements a basic WebSocket client that connects to a specified URL, sends user messages from stdin, and prints received messages. It uses the async gem for concurrency and async-websocket for WebSocket communication. ```ruby #!/usr/bin/env ruby require 'async' require 'async/http/endpoint' require 'async/websocket/client' USER = ARGV.pop || "anonymous" URL = ARGV.pop || "http://localhost:7070" Async do |task| endpoint = Async::HTTP::Endpoint.parse(URL) Async::WebSocket::Client.connect(endpoint) do |connection| input_task = task.async do while line = $stdin.gets connection.write({user: USER, text: line}) connection.flush end end # Generate a text message by geneating a JSON payload from a hash: connection.write(Protocol::WebSocket::TextMessage.generate({ user: USER, status: "connected", })) while message = connection.read puts message.inspect end ensure input_task&.stop end end ``` -------------------------------- ### Open Reusable WebSocket Client Source: https://context7.com/socketry/async-websocket/llms.txt Creates a reusable WebSocket client instance for establishing multiple connections to the same endpoint. Useful for connection pooling. ```ruby require "async" require "async/http/endpoint" require "async/websocket/client" Async do endpoint = Async::HTTP::Endpoint.parse("wss://localhost:8080") Async::WebSocket::Client.open(endpoint) do |client| # Establish a connection using the client connection = client.connect(endpoint.authority, endpoint.path) begin connection.send_text("Hello from open client") connection.flush message = connection.read puts "Response: #{message}" ensure connection.close end end end ``` -------------------------------- ### Force HTTP/1.1 Connection for WebSocket Client in Ruby Source: https://github.com/socketry/async-websocket/blob/main/guides/getting-started/readme.md Demonstrates how to force a WebSocket client to connect using HTTP/1.1, even if the server advertises HTTP/2. This is useful when the server does not support HTTP/2 for WebSocket connections. ```ruby endpoint = Async::HTTP::Endpoint.parse("https://remote-server.com", alpn_protocols: Async::HTTP::Protocol::HTTP11.names) Async::WebSocket::Client.connect(endpoint) do ... ``` -------------------------------- ### WebSocket Client: Send and Receive Structured Messages (Ruby) Source: https://context7.com/socketry/async-websocket/llms.txt Demonstrates how to establish a WebSocket connection, send structured JSON messages using Protocol::WebSocket::TextMessage, and receive/parse responses. It highlights both explicit JSON generation and direct hash writing. ```ruby require "protocol/websocket" Async do endpoint = Async::HTTP::Endpoint.parse("ws://localhost:8080") Async::WebSocket::Client.connect(endpoint) do |connection| # Generate JSON message from hash message = Protocol::WebSocket::TextMessage.generate({ type: "subscribe", channel: "updates", user_id: 123 }) message.send(connection) connection.flush # Alternative: write hash directly (auto-converted to JSON) connection.write({event: "ping", timestamp: Time.now.to_i}) connection.flush # Read and parse response response = connection.read puts response.to_h # => {:type=>"subscribed", :channel=>"updates"} end end ``` -------------------------------- ### Connect to Binance Ticker Stream with Async::WebSocket (Ruby) Source: https://context7.com/socketry/async-websocket/llms.txt Connects to the Binance BTC/USDT ticker stream using Async::WebSocket. It forces HTTP/1.1 for compatibility, reads messages, parses them as JSON, and prints bid and ask prices. Includes error handling and a reconnection mechanism with exponential backoff. ```ruby require "async/websocket" require "async/http/endpoint" require "async/http/protocol/http11" URL = "wss://stream.binance.com:9443/ws/btcusdt@bookTicker" Async do |task| endpoint = Async::HTTP::Endpoint.parse( URL, alpn_protocols: Async::HTTP::Protocol::HTTP11.names ) reconnect_delay = 1 loop do begin Async::WebSocket::Client.connect(endpoint) do |connection| puts "Connected to stream" reconnect_delay = 1 # Reset delay on successful connection while message = connection.read data = message.parse puts "BTC/USDT - Bid: #{data['b']} Ask: #{data['a']}" # Output: BTC/USDT - Bid: 43250.00 Ask: 43251.00 end end rescue => error puts "Connection error: #{error.message}" puts "Reconnecting in #{reconnect_delay}s..." sleep reconnect_delay reconnect_delay = [reconnect_delay * 2, 60].min # Exponential backoff end end end ``` -------------------------------- ### Connect to WebSocket Server (Client) Source: https://context7.com/socketry/async-websocket/llms.txt Establishes a WebSocket client connection. Supports block form for automatic closing and manual lifecycle management. Can force HTTP/1.1 using ALPN protocols. ```ruby require "async" require "async/http/endpoint" require "async/websocket/client" # Basic client connection with block form (auto-closes connection) Async do endpoint = Async::HTTP::Endpoint.parse("wss://echo.websocket.org") Async::WebSocket::Client.connect(endpoint) do |connection| # Send a text message connection.send_text("Hello, WebSocket!") connection.flush # Read response message = connection.read puts "Received: #{message}" # => Received: # end end # Connection without block (manual lifecycle management) Async do endpoint = Async::HTTP::Endpoint.parse("ws://localhost:8080") connection = Async::WebSocket::Client.connect(endpoint) begin connection.send_text("ping") connection.flush response = connection.read puts response.to_s ensure connection.close end end # Force HTTP/1.1 connection (useful when server doesn't support HTTP/2 WebSockets) Async do endpoint = Async::HTTP::Endpoint.parse( "wss://api.example.com/ws", alpn_protocols: Async::HTTP::Protocol::HTTP11.names ) Async::WebSocket::Client.connect(endpoint) do |connection| while message = connection.read puts message.parse end end end ``` -------------------------------- ### Rack Adapter: Chat Server with Broadcast (Ruby) Source: https://context7.com/socketry/async-websocket/llms.txt Implements a simple chat server using the Rack adapter. New connections are added to a set, and incoming messages are broadcast to all currently connected clients. Connections are removed when they are closed. ```ruby # config.ru - Chat server with broadcast require "async/websocket/adapters/rack" require "set" $connections = Set.new app = lambda do |env| Async::WebSocket::Adapters::Rack.open(env, protocols: ["chat"]) do |connection| $connections << connection begin while message = connection.read # Broadcast to all connected clients $connections.each do |conn| conn.write(message) conn.flush end end ensure $connections.delete(connection) end end or [200, {"Content-Type" => "text/plain"}, ["Use WebSocket to connect"]] end run app # Run with Falcon: # falcon serve --bind http://localhost:7070 --count 1 ``` -------------------------------- ### Rack Adapter: Check for WebSocket Request (Ruby) Source: https://context7.com/socketry/async-websocket/llms.txt Demonstrates how to use `Async::WebSocket::Adapters::Rack.websocket?` to differentiate between regular HTTP requests and WebSocket upgrade requests within a single Rack application. It handles WebSocket connections by echoing messages and provides a JSON response for other requests. ```ruby require "async/websocket/adapters/rack" app = lambda do |env| if Async::WebSocket::Adapters::Rack.websocket?(env) Async::WebSocket::Adapters::Rack.open(env) do |connection| connection.write({status: "connected"}) connection.flush while message = connection.read connection.write({echo: message.to_s}) connection.flush end end else # Regular HTTP response [200, {"Content-Type" => "application/json"}, ['{"message": "Use WebSocket"}']] end end run app ``` -------------------------------- ### Test WebSocket Connection with websocat Source: https://github.com/socketry/async-websocket/blob/main/guides/rails-integration/readme.md Connects to the running Rails WebSocket server using the 'websocat' command-line client. It demonstrates how to verify that the WebSocket endpoint is functioning correctly and sending messages. ```bash websocat ws://localhost:3000/home/index ``` -------------------------------- ### WebSocket Connection Reading and Writing Source: https://context7.com/socketry/async-websocket/llms.txt Demonstrates sending and receiving text and binary messages concurrently using async tasks. Supports parsing JSON messages. ```ruby require "async" require "async/http/endpoint" require "async/websocket/client" Async do |task| endpoint = Async::HTTP::Endpoint.parse("ws://localhost:8080") Async::WebSocket::Client.connect(endpoint) do |connection| # Concurrent reading and writing using async tasks writer_task = task.async do 10.times do |i| # Send plain text connection.send_text("Message #{i}") connection.flush sleep 0.1 end end reader_task = task.async do while message = connection.read # Parse JSON messages if message.respond_to?(:parse) data = message.parse puts "Parsed: #{data}" else puts "Raw: #{message}" end end end writer_task.wait connection.close end end ``` -------------------------------- ### Handle WebSocket Errors in Ruby Client and Server Source: https://context7.com/socketry/async-websocket/llms.txt This snippet illustrates comprehensive error handling for WebSocket connections in Ruby, covering both client and server scenarios. It demonstrates catching specific exceptions like ConnectionError, UnsupportedVersionError, and ProtocolError, as well as generic errors. It requires 'async', 'async-http', 'async-websocket', and 'async-websocket/adapters/rack'. ```ruby require "async" require "async/http/endpoint" require "async/websocket/client" require "async/websocket/error" Async do endpoint = Async::HTTP::Endpoint.parse("wss://invalid.example.com/ws") begin Async::WebSocket::Client.connect(endpoint) do |connection| connection.send_text("test") connection.flush message = connection.read puts message end rescue Async::WebSocket::ConnectionError => error # Connection failed during handshake puts "Connection failed: #{error.message}" puts "HTTP Response: #{error.response.status}" if error.response rescue Async::WebSocket::UnsupportedVersionError => error # Server doesn't support WebSocket on this HTTP version puts "Unsupported HTTP version: #{error.message}" rescue Async::WebSocket::ProtocolError => error # WebSocket protocol violation puts "Protocol error: #{error.message}" rescue Async::WebSocket::Error => error # Generic WebSocket error puts "WebSocket error: #{error.message}" rescue => error # Network or other errors puts "Error: #{error.class} - #{error.message}" end end # Server-side error handling require "async/websocket/adapters/rack" app = lambda do |env| Async::WebSocket::Adapters::Rack.open(env) do |connection| begin while message = connection.read # Process message data = message.to_h raise ArgumentError, "Invalid data" unless data[:type] connection.write({status: "ok"}) connection.flush end rescue Protocol::WebSocket::ClosedError # Client closed connection normally puts "Client disconnected" rescue => error # Send error to client before closing connection.write({error: error.message}) connection.flush raise end end or [404, {}, []] end run app ``` -------------------------------- ### Async::WebSocket::Adapters::Rack Source: https://context7.com/socketry/async-websocket/llms.txt The Rack adapter enables WebSocket support in any Rack-compatible server. It handles WebSocket upgrade requests and provides a connection object for server-side message handling. ```APIDOC ## Async::WebSocket::Adapters::Rack The Rack adapter enables WebSocket support in any Rack-compatible server (Falcon, Puma with async support, etc.). It handles WebSocket upgrade requests and provides a connection object for server-side message handling. ### Example: Basic Echo Server ```ruby # config.ru - Basic echo server require "async/websocket/adapters/rack" app = lambda do |env| Async::WebSocket::Adapters::Rack.open(env) do |connection| while message = connection.read # Echo the message back connection.write(message) connection.flush end end or [404, {}, ["WebSocket connections only"]] end run app ``` ### Example: Chat Server with Broadcast ```ruby # config.ru - Chat server with broadcast require "async/websocket/adapters/rack" require "set" $connections = Set.new app = lambda do |env| Async::WebSocket::Adapters::Rack.open(env, protocols: ["chat"]) do |connection| $connections << connection begin while message = connection.read # Broadcast to all connected clients $connections.each do |conn| conn.write(message) conn.flush end end ensure $connections.delete(connection) end end or [200, {"Content-Type" => "text/plain"}, ["Use WebSocket to connect"]] end run app # Run with Falcon: # falcon serve --bind http://localhost:7070 --count 1 ``` ``` -------------------------------- ### Generate Rails Controller Source: https://github.com/socketry/async-websocket/blob/main/guides/rails-integration/readme.md Generates a new controller named 'home' with an 'index' action using the Rails CLI. This is a foundational step for creating the WebSocket endpoint. ```bash rails generate controller home index ``` -------------------------------- ### Rails WebSocket Controller Implementation Source: https://github.com/socketry/async-websocket/blob/main/guides/rails-integration/readme.md Implements a Rails controller action to handle WebSocket connections using async-websocket. It skips CSRF token verification for WebSocket clients and opens a WebSocket connection, sending a 'Hello World' message. ```ruby require 'async/websocket/adapters/rails' class HomeController < ApplicationController # WebSocket clients may not send CSRF tokens, so we need to disable this check. skip_before_action :verify_authenticity_token, only: [:index] def index self.response = Async::WebSocket::Adapters::Rails.open(request) do |connection| message = Protocol::WebSocket::TextMessage.generate({message: "Hello World"}) connection.write(message) end end end ``` -------------------------------- ### Async::WebSocket::Adapters::Rails Source: https://context7.com/socketry/async-websocket/llms.txt Integrates async-websocket with Ruby on Rails controllers. Allows handling WebSocket connections directly in Rails actions while maintaining compatibility with Rails routing and middleware. ```APIDOC ## Async::WebSocket::Adapters::Rails Integrates async-websocket with Ruby on Rails controllers. Allows handling WebSocket connections directly in Rails actions while maintaining compatibility with Rails routing and middleware. ### Method `Async::WebSocket::Adapters::Rails.open(request)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```ruby # app/controllers/websocket_controller.rb require "async/websocket/adapters/rails" class WebsocketController < ApplicationController # Disable CSRF for WebSocket endpoints skip_before_action :verify_authenticity_token, only: [:connect] def connect self.response = Async::WebSocket::Adapters::Rails.open(request) do |connection| # Send welcome message message = Protocol::WebSocket::TextMessage.generate({ type: "welcome", message: "Connected to Rails WebSocket" }) connection.write(message) connection.flush # Handle incoming messages while incoming = connection.read data = incoming.to_h case data[:action] when "ping" connection.write({action: "pong", timestamp: Time.now.to_i}) when "subscribe" connection.write({action: "subscribed", channel: data[:channel]}) else connection.write({action: "echo", data: data}) end connection.flush end end end end # config/routes.rb Rails.application.routes.draw do # Support both GET (HTTP/1.1) and CONNECT (HTTP/2) methods match "websocket/connect", to: "websocket#connect", via: [:get, :connect] end # Start server with Falcon for production use: # bundle remove puma # bundle add falcon # rails s # => Booting Falcon v0.47.7 ``` ### Response #### Success Response (200) - **type** (String) - Type of message (e.g., "welcome", "subscribed"). - **message** (String) - A descriptive message. - **action** (String) - The action performed (e.g., "pong", "echo"). - **timestamp** (Integer) - Unix timestamp for time-sensitive actions. - **channel** (String) - The channel the user subscribed to. #### Response Example ```json { "type": "welcome", "message": "Connected to Rails WebSocket" } ``` ```json { "action": "pong", "timestamp": 1678886400 } ``` ```json { "action": "subscribed", "channel": "updates" } ``` ```json { "action": "echo", "data": {"message": "Hello"} } ``` ``` -------------------------------- ### Add Falcon Gem Source: https://github.com/socketry/async-websocket/blob/main/guides/rails-integration/readme.md Adds the Falcon gem to the project's Gemfile using the bundle command. Falcon is recommended for handling a large number of concurrent WebSocket connections due to its fiber-per-request architecture. ```bash bundle add falcon ``` -------------------------------- ### Async::WebSocket::Adapters::Rack.websocket? Source: https://context7.com/socketry/async-websocket/llms.txt Checks if an incoming request is a WebSocket upgrade request. Useful for routing logic where you need to handle both WebSocket and regular HTTP requests in the same endpoint. ```APIDOC ## Async::WebSocket::Adapters::Rack.websocket? Checks if an incoming request is a WebSocket upgrade request. Useful for routing logic where you need to handle both WebSocket and regular HTTP requests in the same endpoint. ### Method `Async::WebSocket::Adapters::Rack.websocket?(env)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```ruby require "async/websocket/adapters/rack" app = lambda do |env| if Async::WebSocket::Adapters::Rack.websocket?(env) Async::WebSocket::Adapters::Rack.open(env) do |connection| connection.write({status: "connected"}) connection.flush while message = connection.read connection.write({echo: message.to_s}) connection.flush end end else # Regular HTTP response [200, {"Content-Type" => "application/json"}, ['{"message": "Use WebSocket"}']] end end run app ``` ### Response #### Success Response (200) - **status** (String) - Indicates connection status. - **echo** (String) - Echoes back the received message. #### Response Example ```json { "status": "connected" } ``` ```json { "echo": "Hello, WebSocket!" } ``` ``` -------------------------------- ### Test HTTP/2 WebSocket Connection with Ruby Client Source: https://github.com/socketry/async-websocket/blob/main/guides/rails-integration/readme.md Connects to an HTTPS WebSocket endpoint using a custom Ruby script. This script demonstrates how to establish an HTTP/2 WebSocket connection and read messages from the server. ```ruby require 'async/http/endpoint' require 'async/websocket/client' endpoint = Async::HTTP::Endpoint.parse("https://localhost:3000/home/index") Async::WebSocket::Client.connect(endpoint) do |connection| puts connection.framer.connection.class # Async::HTTP::Protocol::HTTP2::Client while message = connection.read puts message.inspect end end ``` -------------------------------- ### Remove Puma Gem Source: https://github.com/socketry/async-websocket/blob/main/guides/rails-integration/readme.md Removes the Puma gem from the project's Gemfile using the bundle command. This is a prerequisite for using Falcon as the application server. ```bash bundle remove puma ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.