Giter Club home page Giter Club logo

faye-websocket-ruby's Introduction

faye-websocket

This is a general-purpose WebSocket implementation extracted from the Faye project. It provides classes for easily building WebSocket servers and clients in Ruby. It does not provide a server itself, but rather makes it easy to handle WebSocket connections within an existing Rack application. It does not provide any abstraction other than the standard WebSocket API.

It also provides an abstraction for handling EventSource connections, which are one-way connections that allow the server to push data to the client. They are based on streaming HTTP responses and can be easier to access via proxies than WebSockets.

The following web servers are supported. Other servers that implement the rack.hijack API should also work.

Installation

$ gem install faye-websocket

Handling WebSocket connections in Rack

You can handle WebSockets on the server side by listening for requests using the Faye::WebSocket.websocket? method, and creating a new socket for the request. This socket object exposes the usual WebSocket methods for receiving and sending messages. For example this is how you'd implement an echo server:

# app.rb
require 'faye/websocket'

App = lambda do |env|
  if Faye::WebSocket.websocket?(env)
    ws = Faye::WebSocket.new(env)

    ws.on :message do |event|
      ws.send(event.data)
    end

    ws.on :close do |event|
      p [:close, event.code, event.reason]
      ws = nil
    end

    # Return async Rack response
    ws.rack_response

  else
    # Normal HTTP request
    [200, { 'Content-Type' => 'text/plain' }, ['Hello']]
  end
end

Note that under certain circumstances (notably a draft-76 client connecting through an HTTP proxy), the WebSocket handshake will not be complete after you call Faye::WebSocket.new because the server will not have received the entire handshake from the client yet. In this case, calls to ws.send will buffer the message in memory until the handshake is complete, at which point any buffered messages will be sent to the client.

If you need to detect when the WebSocket handshake is complete, you can use the onopen event.

If the connection's protocol version supports it, you can call ws.ping() to send a ping message and wait for the client's response. This method takes a message string, and an optional callback that fires when a matching pong message is received. It returns true if and only if a ping message was sent. If the client does not support ping/pong, this method sends no data and returns false.

ws.ping 'Mic check, one, two' do
  # fires when pong is received
end

Using the WebSocket client

The client supports both the plain-text ws protocol and the encrypted wss protocol, and has exactly the same interface as a socket you would use in a web browser. On the wire it identifies itself as hybi-13.

require 'faye/websocket'
require 'eventmachine'

EM.run {
  ws = Faye::WebSocket::Client.new('ws://www.example.com/')

  ws.on :open do |event|
    p [:open]
    ws.send('Hello, world!')
  end

  ws.on :message do |event|
    p [:message, event.data]
  end

  ws.on :close do |event|
    p [:close, event.code, event.reason]
    ws = nil
  end
}

The WebSocket client also lets you inspect the status and headers of the handshake response via its status and headers methods.

To connect via a proxy, set the proxy option to the HTTP origin of the proxy, including any authorization information and custom headers you require:

ws = Faye::WebSocket::Client.new('ws://www.example.com/', [], {
  :proxy => {
    :origin  => 'http://username:[email protected]',
    :headers => { 'User-Agent' => 'ruby' }
  }
})

Subprotocol negotiation

The WebSocket protocol allows peers to select and identify the application protocol to use over the connection. On the client side, you can set which protocols the client accepts by passing a list of protocol names when you construct the socket:

ws = Faye::WebSocket::Client.new('ws://www.example.com/', ['irc', 'amqp'])

On the server side, you can likewise pass in the list of protocols the server supports after the other constructor arguments:

ws = Faye::WebSocket.new(env, ['irc', 'amqp'])

If the client and server agree on a protocol, both the client- and server-side socket objects expose the selected protocol through the ws.protocol property.

Protocol extensions

faye-websocket is based on the websocket-extensions framework that allows extensions to be negotiated via the Sec-WebSocket-Extensions header. To add extensions to a connection, pass an array of extensions to the :extensions option. For example, to add permessage-deflate:

require 'permessage_deflate'

ws = Faye::WebSocket.new(env, [], :extensions => [PermessageDeflate])

Initialization options

Both the server- and client-side classes allow an options hash to be passed in at initialization time, for example:

ws = Faye::WebSocket.new(env, protocols, options)
ws = Faye::WebSocket::Client.new(url, protocols, options)

protocols as an array of subprotocols as described above, or nil. options is an optional hash containing any of these keys:

  • :extensions - an array of websocket-extensions compatible extensions, as described above
  • :headers - a hash containing key-value pairs representing HTTP headers to be sent during the handshake process
  • :max_length - the maximum allowed size of incoming message frames, in bytes. The default value is 2^26 - 1, or 1 byte short of 64 MiB.
  • :ping - an integer that sets how often the WebSocket should send ping frames, measured in seconds
  • :tls - a hash containing key-value pairs for specifying TLS parameters. These are passed along to EventMachine and you can find more details here

Secure sockets

Starting with version 0.11.0, Faye::WebSocket::Client will verify the server certificate for wss connections. This is not the default behaviour for EventMachine's TLS interface, and so our defaults for the :tls option are a little different.

First, :verify_peer is enabled by default. Our implementation checks that the chain of certificates sent by the server is trusted by your root certificates, and that the final certificate's hostname matches the hostname in the request URL.

By default, we use your system's root certificate store by invoking OpenSSL::X509::Store#set_default_paths. If you want to use a different set of root certificates, you can pass them via the :root_cert_file option, which takes a path or an array of paths to the certificates you want to use.

ws = Faye::WebSocket::Client.new('wss://example.com/', [], :tls => {
  :root_cert_file => ['path/to/certificate.pem']
})

If you want to switch off certificate verification altogether, then set :verify_peer to false.

ws = Faye::WebSocket::Client.new('wss://example.com/', [], :tls => {
  :verify_peer => false
})

WebSocket API

Both the server- and client-side WebSocket objects support the following API:

  • on(:open) { |event| } fires when the socket connection is established. Event has no attributes.
  • on(:message) { |event| } fires when the socket receives a message. Event has one attribute, data, which is either a String (for text frames) or an Array of unsigned integers, i.e. integers in the range 0..255 (for binary frames).
  • on(:error) { |event| } fires when there is a protocol error due to bad data sent by the other peer. This event is purely informational, you do not need to implement error recovery.
  • on(:close) { |event| } fires when either the client or the server closes the connection. Event has two optional attributes, code and reason, that expose the status code and message sent by the peer that closed the connection.
  • send(message) accepts either a String or an Array of byte-sized integers and sends a text or binary message over the connection to the other peer; binary data must be encoded as an Array.
  • ping(message, &callback) sends a ping frame with an optional message and fires the callback when a matching pong is received.
  • close(code, reason) closes the connection, sending the given status code and reason text, both of which are optional.
  • version is a string containing the version of the WebSocket protocol the connection is using.
  • protocol is a string (which may be empty) identifying the subprotocol the socket is using.

Handling EventSource connections in Rack

EventSource connections provide a very similar interface, although because they only allow the server to send data to the client, there is no onmessage API. EventSource allows the server to push text messages to the client, where each message has an optional event-type and ID.

# app.rb
require 'faye/websocket'

App = lambda do |env|
  if Faye::EventSource.eventsource?(env)
    es = Faye::EventSource.new(env)
    p [:open, es.url, es.last_event_id]

    # Periodically send messages
    loop = EM.add_periodic_timer(1) { es.send('Hello') }

    es.on :close do |event|
      EM.cancel_timer(loop)
      es = nil
    end

    # Return async Rack response
    es.rack_response

  else
    # Normal HTTP request
    [200, { 'Content-Type' => 'text/plain' }, ['Hello']]
  end
end

The send method takes two optional parameters, :event and :id. The default event-type is 'message' with no ID. For example, to send a notification event with ID 99:

es.send('Breaking News!', :event => 'notification', :id => '99')

The EventSource object exposes the following properties:

  • url is a string containing the URL the client used to create the EventSource.
  • last_event_id is a string containing the last event ID received by the client. You can use this when the client reconnects after a dropped connection to determine which messages need resending.

When you initialize an EventSource with Faye::EventSource.new, you can pass configuration options after the env parameter. Available options are:

  • :headers is a hash containing custom headers to be set on the EventSource response.
  • :retry is a number that tells the client how long (in seconds) it should wait after a dropped connection before attempting to reconnect.
  • :ping is a number that tells the server how often (in seconds) to send 'ping' packets to the client to keep the connection open, to defeat timeouts set by proxies. The client will ignore these messages.

For example, this creates a connection that allows access from any origin, pings every 15 seconds and is retryable every 10 seconds if the connection is broken:

es = Faye::EventSource.new(es,
  :headers => { 'Access-Control-Allow-Origin' => '*' },
  :ping    => 15,
  :retry   => 10
)

You can send a ping message at any time by calling es.ping. Unlike WebSocket the client does not send a response to this; it is merely to send some data over the wire to keep the connection alive.

Running your socket application

The following describes how to run a WebSocket application using all our supported web servers.

Running the app with Thin

If you use Thin to serve your application you need to include this line after loading faye/websocket:

Faye::WebSocket.load_adapter('thin')

Thin can be started via the command line if you've set up a config.ru file for your application:

$ thin start -R config.ru -p 9292

Or, you can use rackup. In development mode, this adds middlewares that don't work with async apps, so you must start it in production mode:

$ rackup config.ru -s thin -E production -p 9292

It can also be started using the Rack::Handler interface common to many Ruby servers. You can configure Thin further in a block passed to run:

require 'eventmachine'
require 'rack'
require 'thin'
require './app'

Faye::WebSocket.load_adapter('thin')

thin = Rack::Handler.get('thin')

thin.run(App, :Port => 9292) do |server|
  # You can set options on the server here, for example to set up SSL:
  server.ssl_options = {
    :private_key_file => 'path/to/ssl.key',
    :cert_chain_file  => 'path/to/ssl.crt'
  }
  server.ssl = true
end

Running the app with Passenger

faye-websocket requires either Passenger for Nginx or Passenger Standalone. Apache doesn't work well with WebSockets at this time. You do not need any special configuration to make faye-websocket work, it should work out of the box on Passenger provided you use at least Passenger 4.0.

However, you do need to insert the following code in config.ru for optimal WebSocket performance in Passenger. This is documented in the Passenger manual.

if defined?(PhusionPassenger)
  PhusionPassenger.advertised_concurrency_level = 0
end

Run your app on Passenger for Nginx by creating a virtual host entry which points to your app's "public" directory:

server {
  listen 9292;
  server_name yourdomain.local;
  root /path-to-your-app/public;
  passenger_enabled on;
}

Or run your app on Passenger Standalone:

$ passenger start -p 9292

More information can be found on the Passenger website.

Running the app with Puma

Puma has a command line interface for starting your application:

$ puma config.ru -p 9292

Or, you can use rackup. In development mode, this adds middlewares that don't work with async apps, so you must start it in production mode:

$ rackup config.ru -s puma -E production -p 9292

Running the app with Rainbows

If you're using version 4.4 or lower of Rainbows, you need to run it with the EventMachine backend and enable the adapter. Put this in your rainbows.conf file:

Rainbows! { use :EventMachine }

And make sure you load the adapter in your application:

Faye::WebSocket.load_adapter('rainbows')

Version 4.5 of Rainbows does not need this adapter.

You can run your config.ru file from the command line. Again, Rack::Lint will complain unless you put the application in production mode.

$ rainbows config.ru -c path/to/rainbows.conf -E production -p 9292

Running the app with Goliath

If you use Goliath to server your application you need to include this line after loading faye/websocket:

Faye::WebSocket.load_adapter('goliath')

Goliath can be made to run arbitrary Rack apps by delegating to them from a Goliath::API instance. A simple server looks like this:

require 'goliath'
require './app'
Faye::WebSocket.load_adapter('goliath')

class EchoServer < Goliath::API
  def response(env)
    App.call(env)
  end
end

Faye::WebSocket can also be used inline within a Goliath app:

require 'goliath'
require 'faye/websocket'
Faye::WebSocket.load_adapter('goliath')

class EchoServer < Goliath::API
  def response(env)
    ws = Faye::WebSocket.new(env)

    ws.on :message do |event|
      ws.send(event.data)
    end

    ws.rack_response
  end
end

faye-websocket-ruby's People

Contributors

benv avatar christiangeek avatar foobarwidget avatar jarthod avatar jcoglan avatar jonleighton avatar jordoh avatar mrloop avatar nakilon avatar nicolasleger avatar philnash avatar reemplazable avatar rwz avatar sgrove avatar shpakvel avatar taher-ghaleb avatar wpp avatar ylecuyer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

faye-websocket-ruby's Issues

Recommendations for Integration Testing Faye Websocket Client App

I'm currently building a websocket client for Slack on top of this gem, and while it works great, I wanted to create some integration tests for the high level functionality. To this end, I basically tried to make a Fake Slack Websocket Server:

class FakeSlack
  attr_accessor :socket, :messages

  def initialize
    @messages = []
  end

  def call(env)
    if Faye::WebSocket.websocket?(env)
      self.socket = Faye::WebSocket.new(env)

      socket.on :message do |event|
        @messages << event.data
        socket.send(event.data)
      end

      socket.rack_response
    else
      [200, {'Content-Type' => 'text/plain'}, ['Hello']]
    end
  end

  def has_message?(message)
    messages.include? message
  end
end

The idea here being I'd hold all messages in the "chat room" in an array, and the Fake Slack would accept messages from the actual client.

Before the test suit runs, I set up the server and connect my client to it like so:

  @slack = FakeSlack.new

  # Webmock route ws connection to FakeSlack
  stub_request(:any, "ws://localhost:9292").to_rack(@slack)

  Thread.new do
    MyApp.websocket.connect
  end

And this is the basic test:

  MyApp.websocket.send_message "Some Message"
  expect(@slack).to have_message "The Response."

This seems to more or less work from a test set up perspective, and I can see in the log that my client gets connected to the Server, and my client sends the message to the Server. But at that point it falls apart, because the server doesn't appear to be alive. It accepts the initial connection, but doesn't react after that. So the messages array is never populated, and I can't make assertions on it.

Do you have any recommendations for testing a client against a stand-in server?

Regression: failed: Compressed bit must be 0 if no negotiated deflate-frame extension

With 0.4.7 the socket gets closed with an error when the server tries to write data into it. This happens both on Chrome and Firefox. The server is running rainbows, I've tried with 4.3, 4.4 and 4.5 and the behavior is consistent across. The error message in Chrome is:

WebSocket connection to 'ws://[url omitted]' failed: Compressed bit must be 0 if no negotiated deflate-frame extension

Downgrading to 0.4.6 fixes the issue.

Invalid "Sec-WebSocket-Accept" response

The server is not responding with the Sec-WebSocket-Accept header on the response when connecting.

It looks like clients are getting the a status 101 and trying to find this accept header and then failing. The driver looks like its handling this correctly but its being called after this 101 response? I stopped digging in further.

I'm using the faye node websocket and that working fine with the clients.

This should be reproduceable with the faye client connecting to the faye(-websocket) ruby server.

Connection closes after 30 seconds, 1006

Hi jcoglan,

Thanks for everything that you do with the faye repos.

A slightly modified example program. I connect well, but then the connection always drops after 30s. I tried using the -t 180 on thin to force a timeout, but that doesn't seem to change the drop. Also, if I send 1 message, the timeout changes to 180s.

The code returned is always 1006, which seems to be incredibly undesirable.

Ideally, the connection would never close.

I launch the server with ruby config.ru or thin -t 180 -R config.ru start and the client with ruby client.rb

Thoughts? Thanks!

config.ru

require 'eventmachine'
require 'rack'
require 'thin'
require './app'

Faye::WebSocket.load_adapter('thin')

def stop
  EventMachine.stop
  # BaseStation.threads.each(&:join)
end

EM.run {

  # Start phones server
  Rack::Handler.get('thin').run(App, {
    :Port    => 9000, 
    :signals => false
  })

  # Handles ctrl-c
  Signal.trap("INT")  { stop }
  Signal.trap("TERM") { stop }
}

App.rb

# app.rb
require 'faye/websocket'

App = lambda do |env|
  if Faye::WebSocket.websocket?(env)
    ws = Faye::WebSocket.new(env)

    ws.on :open do |event|
      p [:open]
    end

    ws.on :message do |event|
      puts event.data
    end

    ws.on :close do |event|
      p [:close, event.code, event.reason]
      ws = nil
    end

    # Return async Rack response
    ws.rack_response

  else
    # Normal HTTP request
    [200, {'Content-Type' => 'text/plain'}, ['Hello']]
  end
end

client.rb

require 'bundler/setup'

require 'faye/websocket'
require 'dnssd'
require 'eventmachine'
require 'json'

Dir["./lib/*.rb", "./lib/**/*.rb"].each {|file| require file }

EM.run do

  url = 'localhost'
  port = 9000
  @ws = Faye::WebSocket::Client.new("ws://#{url}:#{port}/")


  @ws.on :open do |event|
    p [:open]

    # send('9876')
  end

  @ws.on :message do |event|
    begin
      parsed = JSON.parse event.data
    rescue => e
      puts ">>>> [Error! Failed to parse JSON]"
      puts ">>>> [#{e.message}]"
      puts ">>>> #{event.data}"
    end

    p [:message, parsed]

  end

  @ws.on :close do |event|
    p [:close, event.code, event.reason]
    ws = nil
    exit 1

    #This should go into a retry mode
  end

  def send(message, status = 10)
    payload = {
      orig: "client", 
      status: status,
      message: message
    }

    #Actions
    @ws.send(payload.to_json)
  end


end

Passenger 4.0.48 not Working

When attempting to use faye-rails, which implements faye-websocket, I've followed the instructions on their readme and the stack fails in websocket.rb on line 42 because there is no constant called passenger or file to require ("cannot load such file" error). This happens whether the passenger gem is included in the gemfile or not.

More info on the issue here:
jimsynz/faye-rails#58

JRuby support

faye-websocket-ruby seems to work on JRuby with its C extension support, but as I have just found out, Travis CI doesn't support C extensions because they are considered somewhat experimental / not a best practise.

So it would be good if, on JRuby, faye-websocket-ruby either:

  • Used a pure-Ruby implementation of what is done in C code
  • Used a pure-Java implementation of what is done in C code

What do you think? I can have a stab at implementing it at some point...

Close reason and code information lost

In the begin_close procedure, in lib/faye/websocket/api.rb, I noticed that the finalize_close procedure is being called before @close_params is being set. This means that the references to @close_params inside finalize_close won't ever have anything but their default values, and thus the 'reason' and 'code' arguments to begin_close are information that is being lost.

Support `rack.hijack` for Rainbows, Puma, etc.

fao @stakach, @evanphx

Let's move the WebSocket-specific chat off of faye/faye#198 and into this issue.

The rack_hijack branch contains code that works on Puma -- run the following to boot the server:

bundle install
rake compile
ruby examples/server.rb 7000 '' puma

As I say, the code works but I'd like code review and testing from those more familiar with the rack.hijack system.

`std::runtime_error: adding existing descriptor` from Faye::RackStream

I'm trying to upgrade http://firehose.io/ to Rainbows 4.5 and the latest version of faye-websocket. When I run a the suite of integration specs from https://github.com/polleverywhere/firehose/tree/faye-websocket-ruby/spec/integrations the thin server tests pass, but the Rainbows! 4.5 server fails with: libc++abi.dylib: terminating with uncaught exception of type std::runtime_error: adding existing descriptor

I traced the source of the error message to https://github.com/eventmachine/eventmachine/blob/master/ext/em.cpp?source=cc#L1341 which gets triggered by https://github.com/faye/faye-websocket-ruby/blob/master/lib/faye/rack_stream.rb#L26.

Steps to reproduce

  1. Clone [email protected]:polleverywhere/firehose.git
  2. Track the faye-websocket-ruby branch
  3. Install and run Redis (brew update ; brew install redis ; redis-server &)
  4. Run bundle ; LOG_LEVEL=debug bundle exec rspec spec/integrations/rainbows_spec.rb ;

The output looks something like:

gem[faye-websocket-ruby] โ†’ be rspec ./spec/integrations/rainbows_spec.rb
Run options: include {:focus=>true}

All examples were filtered out; ignoring {:focus=>true}
[2013-10-29 07:34:55.418 #10849] INFO : Starting 1.2.9 'Configurable Carl', in test
[2013-10-29 07:34:55.448 #10849] INFO : listening on addr=0.0.0.0:7474 fd=7
[2013-10-29 07:34:55.448 #10849] INFO : worker=0 spawning...
[2013-10-29 07:34:55.449 #10849] INFO : master process ready
[2013-10-29 07:34:55.451 #10850] INFO : worker=0 spawned pid=10850
[2013-10-29 07:34:55.462 #10850] INFO : Rainbows! EventMachine worker_connections=50
[2013-10-29 07:34:55.462 #10850] INFO : EventMachine: epoll=false kqueue=true
1
libc++abi.dylib: terminating with uncaught exception of type std::runtime_error: adding existing descriptor
[2013-10-29 07:34:56.667 #10849] ERROR : reaped #<Process::Status: pid 10850 SIGIOT (signal 6)> worker=0

I plowed through the EM docs to try to understand EM.attach, but the context doesn't really make sense to me. Any help would be greatly appreciated. Thanks!

Bad request 400?

Nginx (1.4.1) with passenger (4.0.2) (not standalone version!) and faye doesn't seem to work? All I get is 400 Bad request. Any ideas?

cannot load such file /../.. faye_websocket_mask (LoadError)

I am new to Ruby (sorry),
so when i try this(in examples) :: rackup config.ru -s thin -E production -p 9292

i got this error:

/Users/kanybek/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in require': cannot load such file -- /Users/kanybek/Downloads/faye-faye-websocket-ruby-1de7d2d/lib/faye/websocket/../../faye_websocket_mask (LoadError) from /Users/kanybek/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:inrequire'
from /Users/kanybek/Downloads/faye-faye-websocket-ruby-1de7d2d/lib/faye/websocket.rb:23:in <class:WebSocket>' from /Users/kanybek/Downloads/faye-faye-websocket-ruby-1de7d2d/lib/faye/websocket.rb:20:inmodule:Faye'
from /Users/kanybek/Downloads/faye-faye-websocket-ruby-1de7d2d/lib/faye/websocket.rb:19:in <top (required)>' from /Users/kanybek/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:inrequire'
from /Users/kanybek/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in require' from /Users/kanybek/Downloads/faye-faye-websocket-ruby-1de7d2d/examples/app.rb:1:in<top (required)>'
from /Users/kanybek/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in require' from /Users/kanybek/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:inrequire'
from /Users/kanybek/Downloads/faye-faye-websocket-ruby-1de7d2d/examples/config.ru:14:in block in <main>' from /Users/kanybek/.rvm/gems/ruby-1.9.3-p0/gems/rack-1.3.6/lib/rack/builder.rb:51:ininstance_eval'
from /Users/kanybek/.rvm/gems/ruby-1.9.3-p0/gems/rack-1.3.6/lib/rack/builder.rb:51:in initialize' from /Users/kanybek/Downloads/faye-faye-websocket-ruby-1de7d2d/examples/config.ru:1:innew'
from /Users/kanybek/Downloads/faye-faye-websocket-ruby-1de7d2d/examples/config.ru:1:in <main>' from /Users/kanybek/.rvm/gems/ruby-1.9.3-p0/gems/rack-1.3.6/lib/rack/builder.rb:40:ineval'
from /Users/kanybek/.rvm/gems/ruby-1.9.3-p0/gems/rack-1.3.6/lib/rack/builder.rb:40:in parse_file' from /Users/kanybek/.rvm/gems/ruby-1.9.3-p0/gems/rack-1.3.6/lib/rack/server.rb:200:inapp'
from /Users/kanybek/.rvm/gems/ruby-1.9.3-p0/gems/rack-1.3.6/lib/rack/server.rb:301:in wrapped_app' from /Users/kanybek/.rvm/gems/ruby-1.9.3-p0/gems/rack-1.3.6/lib/rack/server.rb:252:instart'
from /Users/kanybek/.rvm/gems/ruby-1.9.3-p0/gems/rack-1.3.6/lib/rack/server.rb:137:in start' from /Users/kanybek/.rvm/gems/ruby-1.9.3-p0/gems/rack-1.3.6/bin/rackup:4:in<top (required)>'
from /Users/kanybek/.rvm/gems/ruby-1.9.3-p0/bin/rackup:19:in load' from /Users/kanybek/.rvm/gems/ruby-1.9.3-p0/bin/rackup:19:in

'

WebSocket#protocol always the empty string

When passing protocol(s) via the WebSocket constructor in javascript, I still get an empty string when accessing ws.protocol in the :open callback.
I have verified that env['HTTP_SEC_WEBSOCKET_PROTOCOL'] contains the correct protocol.

I've tried digging a bit and it seems to me that the problem may be with websocket-driver-ruby, where Faye::WebSocket#protocol is delegated to. I can find the protocol attribute declared there, but see no place where it's actually set.

The value "upgrade" of the header Connection should be case-insensitive

Coming from this thread: websocket-rails/websocket-rails#55 (comment)

I think that value of the Connection header should be case-insensitive. In other words the value "upgrade" and "Upgrade" should be supported. The official docs states:

If the response lacks a |Connection| header field or the |Connection| header field doesn't contain a token that is an ASCII case-insensitive match for the value "Upgrade", the client MUST Fail the WebSocket Connection.

It looks like the offending code lies here: https://github.com/faye/faye-websocket-ruby/blob/master/lib/faye/websocket.rb#L96

I would assume changing this

env['HTTP_CONNECTION'].split(/\s*,\s*/).include?('Upgrade') and
env['HTTP_UPGRADE'].downcase == 'websocket'

to this

env['HTTP_CONNECTION'].downcase.split(/\s*,\s*/).include?('upgrade') and
env['HTTP_UPGRADE'].downcase == 'websocket'

would fix the issue, but it may not be that simple.

Thanks to DanKnox for the proposed solution

Always closing connection to an eventmachine websocket server behind an nginx

I tried to connect the faye-websocket client to an eventmachine websocket server which lives behind a nginx reverse proxy. The nginx is in version 1.3.15 so it already supports websockets. The connection gets established and immediately gets a "close" with code "1006". The problem does not occur without the nginx. I am not sure where the problem lies, but if i use the em-websocket-client (https://github.com/mwylde/em-websocket-client), everything works fine. My Browser JavaScript clients are connecting fine too.
So do you have any idea why this happens? Can you point me in some direction to investigate further?

thx
Andi

License missing from gemspec

RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.

via e.g.

spec.license = 'MIT'
# or
spec.licenses = ['MIT', 'GPL-2']

Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can imagine, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.

There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.

I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!

Appendix:

If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
Here's a list of the license names I've found and their frequencies

p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post about this project for more information.

`receive_data': undefined method `env' for nil:NilClass (NoMethodError)

First thanks for your amazing work.

I'm using Goliath and Faye, and I encountered a bug.

$ bundle exec ruby faye.rb -sv
[10335:INFO] 2014-10-16 11:31:28 :: Starting server on 0.0.0.0:9000 in development mode. Watch out for stones.
[10335:INFO] 2014-10-16 11:31:30 :: Status: 200, Content-Length: 35924, Response Time: 28.21ms
[10335:INFO] 2014-10-16 11:31:30 :: Status: 101, Content-Length: , Response Time: 8.70ms
/Users/lyset/.gem/ruby/2.1.0/gems/goliath-1.0.4/lib/goliath/connection.rb:66:in `receive_data': undefined method `env' for nil:NilClass (NoMethodError)
from /Users/lyset/.gem/ruby/2.1.0/gems/faye-websocket-0.7.5/lib/faye/adapters/goliath.rb:9:in `receive_data'
from /Users/lyset/.gem/ruby/2.1.0/gems/eventmachine-1.0.3/lib/eventmachine.rb:187:in `run_machine'
from /Users/lyset/.gem/ruby/2.1.0/gems/eventmachine-1.0.3/lib/eventmachine.rb:187:in `run'
from /Users/lyset/.gem/ruby/2.1.0/gems/em-synchrony-1.0.3/lib/em-synchrony.rb:38:in `synchrony'
from /Users/lyset/.gem/ruby/2.1.0/gems/goliath-1.0.4/lib/goliath/server.rb:73:in `start'
from /Users/lyset/.gem/ruby/2.1.0/gems/goliath-1.0.4/lib/goliath/runner.rb:304:in `run_server'
from /Users/lyset/.gem/ruby/2.1.0/gems/goliath-1.0.4/lib/goliath/runner.rb:224:in `run'
from /Users/lyset/.gem/ruby/2.1.0/gems/goliath-1.0.4/lib/goliath/application.rb:111:in `run!'
from /Users/lyset/.gem/ruby/2.1.0/gems/goliath-1.0.4/lib/goliath/application.rb:133:in `block in <module:Goliath>'

Here is a gist describing the bug : https://gist.github.com/antoinelyset/2f3af5489799f91d3bd6

Maybe the issue is linked to #38

$ ruby -v 
ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-darwin13.0]

Problems with headers

Hi,

I am trying to connect to a server via websocket in ruby without success. I have an example code in javascript that connects properly but doing the same in ruby using faye-websocket-ruby I can't manage it. I get all the time 1006 error without any msg.

The code in JS:

function connect() {
    var url = "wss://ws.xapi.pro/demo";
    console.log('Connecting to: ' + url);
    ws = new WebSocket(url);
    ws.onopen = function() {
      console.log('Connected');
      login();
    };
    ws.onmessage = function(evt) {
      console.log("Received: " + evt.data);    
      try {
        var response = JSON.parse(evt.data);
        if(response.status == true) {
          if(response.streamSessionId != undefined) {
            // We received login response
            getAllSymbols();
          } else {
            // We received getAllSymbols response
            parseGetAllSymbols(response.returnData);
          }
        } else {
          alert('Error: ' + response.errorDescr);
        }
      } catch (Exception) {
        alert('Fatal error while receiving data! :(');
      }
    }
    ws.onclose = function() {
      console.log('Connection closed');
    };
  }; 

and the headers:

Request URL:wss://ws.xapi.pro/demo
Request Method:GET
Status Code:101 Switching Protocols
Request Headers
Provisional headers are shown
Cache-Control:no-cache
Connection:Upgrade
Cookie:_ga=GA1.2.925455390.1409913814
Host:ws.xapi.pro
Origin:null
Pragma:no-cache
Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits, x-webkit-deflate-frame
Sec-WebSocket-Key:T1PfWEaQZSNeu7oEAl+sug==
Sec-WebSocket-Version:13
Upgrade:websocket
User-Agent:Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.94 Safari/537.36
Response Headers
Connection:Upgrade
Date:Fri, 05 Sep 2014 19:56:35 GMT
Sec-WebSocket-Accept:fXnH92pqQfL0hpksA+Q8NVEuuIg=
Server:Microsoft-IIS/8.0
Upgrade:Websocket
X-Powered-By:ARR/2.5
X-Powered-By:ASP.NET

and my code:

require 'faye/websocket'
require 'eventmachine'

EM.run {
  ws = Faye::WebSocket::Client.new("wss://ws.xapi.pro/demo", nil, { :headers => {
                                                                'Connection' => 'Upgrade',
                                                                'Host' => 'ws.xapi.pro',
                                                                'Sec-WebSocket-Key' => 'T1PfWEaQZSNeu7oEAl+sug==',
                                                                'Sec-WebSocket-Version' => '13',
                                                                'Upgrade' => 'websocket'
                                                                        }})

  ws.on :open do |event|
    p [:open]
    ws.send('Hello, world!')
  end

  ws.on :message do |event|
    p [:message, event.data]
  end

 ws.on :error do |event|
   p [:error, event.inspect]
 end

  ws.on :close do |event|
    p [:close, event.code]
    pp event.inspect
    ws = nil
  end
}

Any ideas what may be wrong?? I guess this is somehow related to headers but I don't know how to process. Any help appreciated

faye websocket dns resolving error, unhandled exception

Hi,

I'm using Faye with websockets in a ruby client.

This runs off a device with an internet connection that might not be available on startup.
DNS resolvement is not working then.

When the client tries to subscribe to the first channel, the actual connect fails because the host can't be resolved and i get the following stacktrace.

the event loop is stopped and program is exited

Is there any way that i can instruct faye to try to reconnect and not error out, or does the websocket part first need error handling around the EM.connect code, and provide an on_error callback?

Thanks for the feedback!
Dale

/1.9.3-p429/lib/ruby/gems/1.9.1/gems/eventmachine-1.0.3/lib/eventmachine.rb:664:in `connect_server': unable to resolve server address (EventMachine::ConnectionError)
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/eventmachine-1.0.3/lib/eventmachine.rb:664:in `bind_connect'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/eventmachine-1.0.3/lib/eventmachine.rb:640:in `connect'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-websocket-0.6.1/lib/faye/websocket/client.rb:24:in `initialize'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye/transport/web_socket.rb:52:in `new'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye/transport/web_socket.rb:52:in `connect'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye/transport/web_socket.rb:26:in `usable?'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye/transport/web_socket.rb:11:in `usable?'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye/transport/transport.rb:86:in `block in get'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye.rb:103:in `call'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye.rb:103:in `block in async_each'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye.rb:110:in `call'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye.rb:110:in `block in async_each'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye.rb:117:in `call'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye.rb:117:in `block in async_each'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye.rb:119:in `call'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye.rb:119:in `async_each'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye/transport/transport.rb:101:in `get'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye/protocol/client.rb:309:in `select_transport'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye/protocol/client.rb:91:in `handshake'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye/protocol/client.rb:132:in `connect'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/faye-0.8.9/lib/faye/protocol/client.rb:205:in `subscribe'
        from /home/pi/getin_print/client/faye_printer_client.rb:82:in `subscribe_to_print_client_channel'
        from print_client.rb:63:in `block in <main>'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/eventmachine-1.0.3/lib/eventmachine.rb:187:in `call'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/eventmachine-1.0.3/lib/eventmachine.rb:187:in `run_machine'
        from /1.9.3-p429/lib/ruby/gems/1.9.1/gems/eventmachine-1.0.3/lib/eventmachine.rb:187:in `run'
        from print_client.rb:49:in `<main>'

Running cluster mode puma and multi threats

Hi,

I created a small websocket server, that is storing the clients in a @clients array. I run this with puma configured in cluster mode with 2 workers and a max of 4 threads.

Some of my clients are not receving notifications, my guess is that this is related to the multi thread enviroment from puma, somehow the client connects are not visible to all workers and threats.

Do you have any experience with running websockets on puma?

send MessagePack string via websocket

I try your example. Just with msgpack serialization.

EM.run{

  ws = Faye::WebSocket::Client.new('ws://localhost:9292/')

  ws.on :open do |event|
    p [:open]
    ws.send('Hello, world!'.to_msgpack)
  end

  ws.on :message do |event|
    p [:message, event.data]
  end

  ws.on :close do |event|
    p [:close, event.code, event.reason]
    ws = nil
  end

}

It raise

/home/special-k/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/websocket-driver-0.3.0/lib/websocket/driver/hybi.rb:116:in frame': undefined methodsize' for nil:NilClass (NoMethodError)

How should I send it?

NoMethodError when running faye-websocket

Hi, I'm working on Ruby client for SockJS and I decided to use faye-websocket for handling websocket connections. Unfortunately when I run examples/config.rb, I'm getting:

/Users/botanicus/Dropbox/Projects/sockjs/ruby/vendor/faye-websocket/lib/faye/websocket/api.rb:35:in `block in close': undefined method `close_connection_after_writing' for nil:NilClass (NoMethodError)
    from /Users/botanicus/Dropbox/Projects/sockjs/ruby/vendor/faye-websocket/lib/faye/websocket/api.rb:49:in `call'
    from /Users/botanicus/Dropbox/Projects/sockjs/ruby/vendor/faye-websocket/lib/faye/websocket/api.rb:49:in `close'
    from /Users/botanicus/Dropbox/Projects/sockjs/ruby/vendor/faye-websocket/lib/faye/websocket.rb:119:in `fail'
    from /Users/botanicus/.rvm/gems/ruby-1.9.2-head@ruby-amqp/gems/thin-1.3.1/lib/thin/connection.rb:155:in `unbind'
    from /Users/botanicus/.rvm/gems/ruby-1.9.2-head@ruby-amqp/gems/eventmachine-0.12.10/lib/eventmachine.rb:1417:in `event_callback'
    from /Users/botanicus/.rvm/gems/ruby-1.9.2-head@ruby-amqp/gems/eventmachine-0.12.10/lib/eventmachine.rb:256:in `run_machine'
    from /Users/botanicus/.rvm/gems/ruby-1.9.2-head@ruby-amqp/gems/eventmachine-0.12.10/lib/eventmachine.rb:256:in `run'
    from ./config.rb:88:in `'

Apparently the problem is that in some cases @stream is nil.

Rake fails with 'cannot load such file -- rake/extensiontask'

Hi, after cloning faye-websocket-ruby and running bundle install, rake commands fail with the following exception:

faye-websocket-ruby tinco$ bundle exec rake -T
rake aborted!
LoadError: cannot load such file -- rake/extensiontask
/Users/tinco/Source/faye-websocket-ruby/Rakefile:12:in `require'
/Users/tinco/Source/faye-websocket-ruby/Rakefile:12:in `<top (required)>'
/Users/tinco/.rvm/gems/ruby-2.1.1/bin/ruby_executable_hooks:15:in `eval'
/Users/tinco/.rvm/gems/ruby-2.1.1/bin/ruby_executable_hooks:15:in `<main>'
(See full trace by running task with --trace)

Is there a dependency missing? All the specs pass if I run rspec without rake. Seems like the 'rake-compiler' gem would be missing but I'm not sure.

Using a url for a websocket client without a trailing / causes a invalid request error

Very simple, if you create a new websocket client with a line like:

ws = Faye::WebSocket::Client.new("ws://localhost:8080")

you will get a parse error !! Invalid request. This is because the initial data sent to the server will be similar to:

GET  HTTP/1.1
Host: localhost:8080
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: /VCbVjtuwoCUNU7nHVzVBA==
Sec-WebSocket-Version: 13

Note the missing address in the GET line.

If you change the line to

ws = Faye::WebSocket::Client.new("ws://localhost:8080/")

the will work correctly, yielding this http request:

GET / HTTP/1.1
Host: localhost:8080
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: tbQOPX3hpI3A6e/PWSQCxQ==
Sec-WebSocket-Version: 13

I think if there isn't a trailing slash, the uri in the GET request should default to /

'Connection' header value is not 'Upgrade'

Looks like this is still an ongoing issue. My example faye client script is the following:

require 'faye/websocket'
require 'eventmachine'
require 'json'

EM.run {
ws = Faye::WebSocket::Client.new('ws://streaming.example.com')

ws.on :message do |event|
#p [:message, event.data]
p JSON.parse(event.data)
end

ws.on :close do |event|
p [:close, event.code, event.reason]
ws = nil
end
}

The output when running it:

[:close, 1002, "Error during WebSocket handshake: 'Connection' header value is not 'Upgrade'"]

I'm running Nginx on the server and that proxies requests over to Play Framework 2.2.0-scala. Nginx is setup exactly as described here and I'm explicitly setting the Upgrade header with a capital 'U' (tried with a lower case u also), still no luck:

http://nginx.org/en/docs/http/websocket.html

faye-websocket (0.7.0)
websocket-driver (0.3.0)

Report code errors

Hi, I'm loving the library so far. However, often when I have a syntax error in my code on the server the only way that I can tell is that the server terminates the connection with the 1006 code and no explanation. I can't seem to find any sort of logging options in the documentation - is there any way to get a stack trace and/or error message with what line the error occurred on?

Encoding issue when using JRuby

I'm facing an encoding compatibility error when trying to use faye-websocket with Goliath server in JRuby 1.7.3 (1.9 mode)

Encoding::CompatibilityError: incompatible character encodings: ASCII-8BIT and UTF-8
                    + at org/jruby/RubyString.java:1113
  handshake_signature at /home/samuel/.gem/jruby/1.9.3/gems/faye-websocket-0.4.7-java/lib/faye/websocket/draft76_parser.rb:35
                parse at /home/samuel/.gem/jruby/1.9.3/gems/faye-websocket-0.4.7-java/lib/faye/websocket/draft76_parser.rb:46
                parse at /home/samuel/.gem/jruby/1.9.3/gems/faye-websocket-0.4.7-java/lib/faye/websocket.rb:172
             __send__ at org/jruby/RubyBasicObject.java:1683
              receive at /home/samuel/.gem/jruby/1.9.3/gems/faye-websocket-0.4.7-java/lib/faye/websocket.rb:202
         receive_data at /home/samuel/.gem/jruby/1.9.3/gems/faye-websocket-0.4.7-java/lib/faye/adapters/goliath.rb:10
       event_callback at /home/samuel/.gem/jruby/1.9.3/gems/eventmachine-1.0.3-java/lib/eventmachine.rb:1484
        eventCallback at /home/samuel/.gem/jruby/1.9.3/gems/eventmachine-1.0.3-java/lib/jeventmachine.rb:92
          run_machine at /home/samuel/.gem/jruby/1.9.3/gems/eventmachine-1.0.3-java/lib/jeventmachine.rb:111
                  run at /home/samuel/.gem/jruby/1.9.3/gems/eventmachine-1.0.3-java/lib/eventmachine.rb:187
            synchrony at /home/samuel/.gem/jruby/1.9.3/gems/em-synchrony-1.0.3/lib/em-synchrony.rb:38
                start at /home/samuel/.gem/jruby/1.9.3/gems/goliath-1.0.1/lib/goliath/server.rb:73
           run_server at /home/samuel/.gem/jruby/1.9.3/gems/goliath-1.0.1/lib/goliath/runner.rb:296
                  run at /home/samuel/.gem/jruby/1.9.3/gems/goliath-1.0.1/lib/goliath/runner.rb:221
                 run! at /home/samuel/.gem/jruby/1.9.3/gems/goliath-1.0.1/lib/goliath/application.rb:111
              Goliath at /home/samuel/.gem/jruby/1.9.3/gems/goliath-1.0.1/lib/goliath/application.rb:129

Exceptions lost in event handlers

Ruby 2.2.2 p95 windows
require 'faye/websocket'
require 'eventmachine'

ws.on :open do |event|
raise "oh noes"
ws.send('Hello, world!')
end

Using your primary client example code, this exception goes silent, we never see it. This makes debugging difficult.

Unable to connect to wss server using TLSv1 or greater

Moving faye/websocket-driver-ruby#14 to this repo where it belongs. /cc @laphlaw.

I am unable to connect to a wss (secure websocket) server.

Using ssldump to debug on the server side, I see my client (using your gem) attempt to connect using SSLv2:

1 1 0.0048 (0.0048) C>S SSLv2 compatible client hello
Version 3.3

Is there a way I can specify the TLS version I want to use? The server requires TLSv1 and above.

EventSource connections don't work with Rainbows

I'm building an app that uses faye-websocket with Rainbows, but I can't get it to work. I'm seeing the same issue when running the app in the examples folder:
$ rainbows -c spec/rainbows.conf -E production examples/config.ru -p 7000

The browser continuously prints ERROR: undefined and disconnects. The server outputs the following:
[:open, "http://localhost:7000/", ""] [:close, "http://localhost:7000/"]

I'm running faye-websocket 0.4.7 and rainbows 4.5.0. I have also tried with rainbows 4.4.3, but I get the same error.

thin adapter duplicate header 101 switching protocol

Hi, im struggle with small issue which does not allow me to use a faye-websocket version higher than 0.4.6. Thin response duplicates (see code) when try to use faye-websockets with thin adapter on version > 0.4.6

HTTP/1.1 101 Switching Protocols
Connection: keep-alive
Server: thin 1.6.2 codename Doc Brown

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: itIs6clgPwlktYYxEOu6s2pMNs8=

I found this problem in selected apps:
https://github.com/heroku-examples/ruby-websockets-chat-demo
and https://github.com/lifo/cramp
Looks like issue shows when trying to use thin, rack, faye-websocket mix.

If I downgrade from gem 0.4.7 to 0.4.6 problem disappear. Maybe Its related with this commit 799103f

Anyone else encounter this issue ?
Thank you

faye must not drop HTTP headers coming from another endpoint

I just spend 5 hours debugging all my Rack stack to find why my simple EventSource integration test wasn't working (not using faye, simple Sinatra middleware) to finally find out my thin server wasn't sending the HTTP headers because of faye-websocket monkeypatch:

class Thin::Response
  attr_accessor :async
  alias :thin_head :head

  def head
    async ? '' : thin_head
  end
end

async being set by:

def async_connection?
  websocket? or eventsource?
end

I only use faye-websocket as a dependency of poltergeist for my integration test, and it broke my testing stack because of that monkey patch dropping rack headers on my EventSource.

This is how I gorilla patched it for my application to work, but this is definitely an issue that need to be fixed, faye MUST NOT drop HTTP headers coming from another endpoint.

module Faye
  class WebSocket
    module Adapter
      def async_connection?
        websocket?# or eventsource?
      end
    end
  end
end

NoMethodError: undefined method `has_key?' for nil:NilClass on @listeners in WebSocket::Driver::EventEmitter

I am using ruby 1.8.7 (2012-02-08 MBARI 8/0x6770 on patchlevel 358) [x86_64-linux], MBARI 0x6770, Ruby Enterprise Edition 2012.02.

My application just crashed with the stack trace shown below. The offending line in WebSocket::Driver::EventEmitter#listener_count is

        return 0 unless @listeners.has_key?(event.to_s)

@listeners is nil. Looking in event_emitter.rb, @listeners is initialized to a hash and is never set to nil. WebSocket::Driver::EventEmitter is included into Faye::WebSocket::API::EventTarget, which is included into Faye::WebSocket::API, which is included into Faye::WebSocket::Client, which is where the error occurred, but I do not quickly see where any of those modules access @listeners at all, much less set it to nil. So far, I am at a loss to see how @listeners was set to nil.

Notice also that the stack trace contains lib/faye/websocket/client.rb:31:in `initialize'. This line is inside a rescue block, trying to report a network error. It raised an additional exception, so the original exception is lost. If dispatch_event() is supposed to be able to raise an exception, it should be caught so as to preserve the original exception. However, I suspect it is not supposed to be able to raise here, and this bug is merely secondary to whatever caused the original bug.

I'm going to keep looking into this myself, but let me know if you have suggestions.

The stack trace follows.

NoMethodError: undefined method `has_key?' for nil:NilClass
/usr/local/lib/ruby/gems/1.8/gems/websocket-driver-0.3.2/lib/websocket/driver/event_emitter.rb:36:in `listener_count'
/usr/local/lib/ruby/gems/1.8/gems/faye-websocket-0.7.2/lib/faye/websocket/api/event_target.rb:36:in `dispatch_event'
/usr/local/lib/ruby/gems/1.8/gems/faye-websocket-0.7.2/lib/faye/websocket/client.rb:31:in `initialize'
/usr/local/lib/ruby/gems/1.8/gems/logtailor-0.0.3/lib/logtailor/connection.rb:272:in `new'
/usr/local/lib/ruby/gems/1.8/gems/logtailor-0.0.3/lib/logtailor/connection.rb:272:in `connect'
/usr/local/lib/ruby/gems/1.8/gems/eventmachine-1.0.3/lib/eventmachine.rb:959:in `call'
/usr/local/lib/ruby/gems/1.8/gems/eventmachine-1.0.3/lib/eventmachine.rb:959:in `run_deferred_callbacks'
/usr/local/lib/ruby/gems/1.8/gems/eventmachine-1.0.3/lib/eventmachine.rb:956:in `times'
/usr/local/lib/ruby/gems/1.8/gems/eventmachine-1.0.3/lib/eventmachine.rb:956:in `run_deferred_callbacks'
/usr/local/lib/ruby/gems/1.8/gems/eventmachine-1.0.3/lib/eventmachine.rb:187:in `run_machine'
/usr/local/lib/ruby/gems/1.8/gems/eventmachine-1.0.3/lib/eventmachine.rb:187:in `run'
/usr/local/sbin/ah-logtailord:261:in `main'
/usr/local/sbin/ah-logtailord:307

`gem install faye` fails on windows

Hello, I'm trying to build faye on windows 7 x64 with devkit , however it fails. Looks like compiling works fine, but it fails with moving files. I'm unable to copy-paste log, so I'm pasting it with picture.

/usr/bin/install FAIL

websocket? method fails with first SSL request using Goliath

When using Goliath and the first request comes through SSL, faye-websocket fails with:

`websocket?': undefined method `[]' for nil:NilClass (NoMethodError)

Full backtrace:

gems/websocket-driver-0.3.1/lib/websocket/driver.rb:171:in `websocket?': undefined method `[]' for nil:NilClass (NoMethodError)
from gems/faye-websocket-0.7.1/lib/faye/websocket.rb:58:in `websocket?'
from gems/faye-websocket-0.7.1/lib/faye/websocket/adapter.rb:7:in `websocket?'
from gems/faye-websocket-0.7.1/lib/faye/adapters/goliath.rb:11:in `receive_data'
from gems/eventmachine-1.0.3/lib/eventmachine.rb:187:in `run_machine'
from gems/eventmachine-1.0.3/lib/eventmachine.rb:187:in `run'
from gems/em-synchrony-1.0.3/lib/em-synchrony.rb:38:in `synchrony'
from gems/goliath-1.0.3/lib/goliath/server.rb:73:in `start' 
from gems/goliath-1.0.3/lib/goliath/runner.rb:304:in `run_server'
from gems/goliath-1.0.3/lib/goliath/runner.rb:224:in `run'
from gems/goliath-1.0.3/lib/goliath/application.rb:111:in `run!'
from gems/goliath-1.0.3/lib/goliath/application.rb:133:in `block in <module:Goliath>'

As long as I could see, I think this is what happens:

If Goliath was started without SSL enabled and it receives an SSL request, an exception is raised when it parses the request and closes the connection. See here.

Then Faye tries to figure out if the request is a websocket request, but fails because Goliath didn't set the env. See here

To reproduce it:

# echo_server.rb
require 'goliath'
require 'faye/websocket'

Faye::WebSocket.load_adapter('goliath')

class EchoServer < Goliath::API
  def response(env)
    [200, {'Content-Type' => 'text/plain'}, ['Hello']]
  end
end

Then:

$ ruby echo_server.rb -sv
$ curl https://localhost:9000

The last command breaks the app.

I'm using ruby 2.0.0-p247.

Not throwing exceptions when inside callback

Hey everyone,

My Faye implementation racked upon Rails as a middleware isn't throwing exceptions when they occur inside callback's block. For example:

ws = Faye::WebSocket.new(env, nil, {ping: KEEPALIVE_TIME })
ws.on :open do |event|
  non_existing_method #this method will doesn't exists
end

when I call the non_existing_method, it should raise an exception, but it doesn't. I already tried
raise 'Something' but it also doesn't work. Any clues?

ruby 1.9.x too short escaped multibyte character

I get error when using -Ku option for ruby:

lib/faye/websocket.rb:34: too short escaped multibyte character: /^([\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})*$/ (SyntaxError)

It's similar to flavorjones/loofah#25

Eventsource should not return 101 header

Performing

source = Faye::EventSource.new(env)

will result in two sets of response headers being put out on the wire, first a 101 response then a 200 response.

As per my understanding, it is due to the following code:

 if callback = @env['async.callback']
    callback.call([101, {}, @stream])
 end

 @stream.write("HTTP/1.1 200 OK\r\n" +
               "Content-Type: text/event-stream\r\n" +
               "Cache-Control: no-cache, no-store\r\n" +
               "Connection: close\r\n" +
               headers.to_s +
               "\r\n" +
               "retry: #{ (@retry * 1000).floor }\r\n\r\n")

The reason for this is that the async.callback only returns early if the code is -1 (indicating an async response). Since it is 101, it will actually send the 101 headers on the wire, followed by the @stream which will put out the 200 response.

Instead, it should skip the callback.call([101, {}, @stream]) altogether, and simply return a 200 response with the body set to the @stream , which will achieve the same effect.

Thoughts?

EventSource close callback is not run when connection dropped.

I have a case where we are using Faye::EventSource, and setting the es.on :close block. The app sits behind an nginx reverse proxy and the Rails app itself is running on Phusion Passenger Enterprise 4.

Every 60 seconds, the EventSource connection is dropped (because of this I believe). We can adjust that timeout, so that shouldn't be too much of a problem. The issue is that the block give to es.on :close is never run, and the thread being blocked by this connection is never released. Eventually we hit the passenger thread count and the api becomes non responsive.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.