require 'rubygems' require 'bundler/setup' require 'reel' require 'celluloid/autostart' class RoundtripServer include Celluloid include Celluloid::Notifications def initialize async.run end def run now = Time.now.to_f sleep now.ceil - now + 0.001 every(1) do publish 'read_message' end end end class Writer include Celluloid include Celluloid::Notifications include Celluloid::Internals::Logger def initialize(websocket) info "Writing to socket" @socket = websocket subscribe('write_message', :new_message) end def new_message(topic, new_time) @socket << new_time.inspect rescue Reel::SocketError info "WS client disconnected" terminate end end class Reader include Celluloid include Celluloid::Notifications include Celluloid::Internals::Logger def initialize(websocket) info "Reading socket" @socket = websocket subscribe('read_message', :new_message) end def new_message(topic) msg = @socket.read publish 'write_message', msg rescue Reel::SocketError, EOFError info "WS client disconnected" terminate end end class WebServer < Reel::Server::HTTP include Celluloid::Internals::Logger def initialize(host = "0.0.0.0", port = 9000) info "Roundtrip example starting on #{host}:#{port}" super(host, port, &method(:on_connection)) end def on_connection(connection) while request = connection.request if request.websocket? info "Received a WebSocket connection" # We're going to hand off this connection to another actor (Writer/Reader) # However, initially Reel::Connections are "attached" to the # Reel::Server::HTTP actor, meaning that the server manages the connection # lifecycle (e.g. error handling) for us. # # If we want to hand this connection off to another actor, we first # need to detach it from the Reel::Server (in this case, Reel::Server::HTTP) connection.detach route_websocket request.websocket return else route_request connection, request end end end def route_request(connection, request) if request.url == "/" return render_index(connection) end info "404 Not Found: #{request.path}" connection.respond :not_found, "Not found" end def route_websocket(socket) if socket.url == "/ws" Writer.new(socket) Reader.new(socket) else info "Received invalid WebSocket request for: #{socket.url}" socket.close end end def render_index(connection) info "200 OK: /" connection.respond :ok, <<-HTML