module Faye class WebSocket autoload :Draft75Parser, File.expand_path('..', __FILE__) + '/web_socket/draft75_parser' autoload :Draft76Parser, File.expand_path('..', __FILE__) + '/web_socket/draft76_parser' autoload :Protocol8Parser, File.expand_path('..', __FILE__) + '/web_socket/protocol8_parser' include Publisher CONNECTING = 0 OPEN = 1 CLOSING = 2 CLOSED = 3 attr_reader :request, :url, :ready_state attr_accessor :onopen, :onmessage, :onerror, :onclose extend Forwardable def_delegators :@parser, :version def self.parser(request) if request.env['HTTP_SEC_WEBSOCKET_VERSION'] Protocol8Parser elsif request.env['HTTP_SEC_WEBSOCKET_KEY1'] Draft76Parser else Draft75Parser end end def initialize(request) @request = request @callback = @request.env['async.callback'] @stream = Stream.new @callback.call [200, RackAdapter::TYPE_JSON, @stream] @url = determine_url @ready_state = CONNECTING @buffered_amount = 0 @parser = WebSocket.parser(@request).new(self) @stream.write(@parser.handshake_response) @ready_state = OPEN event = Event.new event.init_event('open', false, false) dispatch_event(event) @request.env[Thin::Request::WEBSOCKET_RECEIVE_CALLBACK] = @parser.method(:parse) end def receive(data) event = Event.new event.init_event('message', false, false) event.data = Faye.encode(data) dispatch_event(event) end def send(data, type = nil, error_type = nil) frame = @parser.frame(Faye.encode(data), type, error_type) @stream.write(frame) if frame end def close(*args) end def add_event_listener(type, listener, use_capture) add_subscriber(type, listener) end def remove_event_listener(type, listener, use_capture) remove_subscriber(type, listener) end def dispatch_event(event) event.target = event.current_target = self event.event_phase = Event::AT_TARGET publish_event(event.type, event) callback = __send__("on#{ event.type }") callback.call(event) if callback end private def determine_url env = @request.env secure = if env.has_key?('HTTP_X_FORWARDED_PROTO') env['HTTP_X_FORWARDED_PROTO'] == 'https' else env['HTTP_ORIGIN'] =~ /^https:/i end scheme = secure ? 'wss:' : 'ws:' "#{ scheme }//#{ env['HTTP_HOST'] }#{ env['REQUEST_URI'] }" end end class WebSocket::Stream include EventMachine::Deferrable def each(&callback) @data_callback = callback end def write(data) return unless @data_callback @data_callback.call(data) end end class WebSocket::Event attr_reader :type, :bubbles, :cancelable attr_accessor :target, :current_target, :event_phase, :data CAPTURING_PHASE = 1 AT_TARGET = 2 BUBBLING_PHASE = 3 def init_event(event_type, can_bubble, cancelable) @type = event_type @bubbles = can_bubble @cancelable = cancelable end def stop_propagation end def prevent_default end end end