2 require 'faye/websocket'
5 # A Rack middleware to handle inbound websocket connection requests and hand
6 # them over to the faye websocket library.
9 DEFAULT_ENDPOINT = '/websocket'
11 # Stop EventMachine on signal, this should give it a chance to to unwind any
13 def die_gracefully_on_signal
14 Signal.trap("INT") { EM.stop }
15 Signal.trap("TERM") { EM.stop }
18 # Create a new RackSocket handler
19 # +app+ The next layer of the Rack stack.
22 # +:handler+ (Required) A class to handle new connections. #initialize will
23 # call handler.new to create the actual handler instance object. When a new
24 # websocket connection is established, #on_connect on the handler instance
25 # object will be called with the new connection.
27 # +:mount+ The HTTP request path that will be recognized for websocket
28 # connect requests, defaults to '/websocket'.
30 # +:websocket_only+ If true, the server will only handle websocket requests,
31 # and all other requests will result in an error. If false, unhandled
32 # non-websocket requests will be passed along on to 'app' in the usual Rack
34 def initialize(app = nil, options = nil)
35 @app = app if app.respond_to?(:call)
36 @options = [app, options].grep(Hash).first || {}
37 @endpoint = @options[:mount] || DEFAULT_ENDPOINT
38 @websocket_only = @options[:websocket_only] || false
40 # from https://gist.github.com/eatenbyagrue/1338545#file-eventmachine-rb
41 if defined?(PhusionPassenger)
42 PhusionPassenger.on_event(:starting_worker_process) do |forked|
43 # for passenger, we need to avoid orphaned threads
44 if forked && EM.reactor_running?
51 ActiveRecord::Base.connection.close
54 die_gracefully_on_signal
57 # faciliates debugging
58 Thread.abort_on_exception = true
59 # just spawn a thread and start it up
64 ActiveRecord::Base.connection.close
69 # Create actual handler instance object from handler class.
70 @handler = @options[:handler].new
73 # Handle websocket connection request, or pass on to the next middleware
74 # supplied in +app+ initialize (unless +:websocket_only+ option is true, in
75 # which case return an error response.)
76 # +env+ the Rack environment with information about the request.
78 request = Rack::Request.new(env)
79 if request.path_info == @endpoint and Faye::WebSocket.websocket?(env)
80 if @handler.overloaded?
81 return [503, {"Content-Type" => "text/plain"}, ["Too many connections, try again later."]]
84 ws = Faye::WebSocket.new(env, nil, :ping => 30)
86 # Notify handler about new connection
87 @handler.on_connect ws
89 # Return async Rack response
91 elsif not @websocket_only
94 [406, {"Content-Type" => "text/plain"}, ["Only websocket connections are permitted on this port."]]