Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Standards-compliant WebSocket client and server

License

NotificationsYou must be signed in to change notification settings

faye/faye-websocket-ruby

Repository files navigation

This is a general-purpose WebSocket implementation extracted from theFaye project. It provides classes for easily buildingWebSocket servers and clients in Ruby. It does not provide a server itself, butrather makes it easy to handle WebSocket connections within an existingRack application. It does not provide any abstractionother than the standardWebSocketAPI.

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

The following web servers are supported. Other servers that implement therack.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 theFaye::WebSocket.websocket? method, and creating a new socket for the request.This socket object exposes the usual WebSocket methods for receiving and sendingmessages. For example this is how you'd implement an echo server:

# app.rbrequire'faye/websocket'App=lambdado |env|ifFaye::WebSocket.websocket?(env)ws=Faye::WebSocket.new(env)ws.on:messagedo |event|ws.send(event.data)endws.on:closedo |event|p[:close,event.code,event.reason]ws=nilend# Return async Rack responsews.rack_responseelse# Normal HTTP request[200,{'Content-Type'=>'text/plain'},['Hello']]endend

Note that under certain circumstances (notably a draft-76 client connectingthrough an HTTP proxy), the WebSocket handshake will not be complete after youcallFaye::WebSocket.new because the server will not have received the entirehandshake from the client yet. In this case, calls tows.send will buffer themessage in memory until the handshake is complete, at which point any bufferedmessages will be sent to the client.

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

If the connection's protocol version supports it, you can callws.ping() tosend a ping message and wait for the client's response. This method takes amessage string, and an optional callback that fires when a matching pong messageis received. It returnstrue if and only if a ping message was sent. If theclient does not support ping/pong, this method sends no data and returnsfalse.

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

Using the WebSocket client

The client supports both the plain-textws protocol and the encryptedwssprotocol, and has exactly the same interface as a socket you would use in a webbrowser. On the wire it identifies itself ashybi-13.

require'faye/websocket'require'eventmachine'EM.run{ws=Faye::WebSocket::Client.new('ws://www.example.com/')ws.on:opendo |event|p[:open]ws.send('Hello, world!')endws.on:messagedo |event|p[:message,event.data]endws.on:closedo |event|p[:close,event.code,event.reason]ws=nilend}

The WebSocket client also lets you inspect the status and headers of thehandshake response via itsstatus andheaders methods.

To connect via a proxy, set theproxy 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:password@proxy.example.com',:headers=>{'User-Agent'=>'ruby'}}})

Subprotocol negotiation

The WebSocket protocol allows peers to select and identify the applicationprotocol to use over the connection. On the client side, you can set whichprotocols the client accepts by passing a list of protocol names when youconstruct 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 serversupports 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-sidesocket objects expose the selected protocol through thews.protocol property.

Protocol extensions

faye-websocket is based on thewebsocket-extensionsframework that allows extensions to be negotiated via theSec-WebSocket-Extensions header. To add extensions to a connection, pass anarray of extensions to the:extensions option. For example, to addpermessage-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 inat 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, ornil.optionsis an optional hash containing any of these keys:

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

Secure sockets

Starting with version 0.11.0,Faye::WebSocket::Client will verify the servercertificate forwss connections. This is not the default behaviour forEventMachine's TLS interface, and so our defaults for the:tls option are alittle different.

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

By default, we use your system's root certificate store by invokingOpenSSL::X509::Store#set_default_paths. If you want to use a different set ofroot certificates, you can pass them via the:root_cert_file option, whichtakes 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 tofalse.

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

WebSocket API

Both the server- and client-sideWebSocket 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. Eventhas one attribute,data, which is either aString (for text frames) oranArray of unsigned integers, i.e. integers in the range0..255 (forbinary frames).
  • on(:error) { |event| } fires when there is a protocol error due to baddata sent by the other peer. This event is purely informational, you do notneed to implement error recovery.
  • on(:close) { |event| } fires when either the client or the server closesthe connection. Event has two optional attributes,code andreason, that expose the status code and message sent by the peer thatclosed the connection.
  • send(message) accepts either aString or anArray of byte-sizedintegers and sends a text or binary message over the connection to the otherpeer; binary data must be encoded as anArray.
  • ping(message, &callback) sends a ping frame with an optional message andfires the callback when a matching pong is received.
  • close(code, reason) closes the connection, sending the given status codeand reason text, both of which are optional.
  • version is a string containing the version of theWebSocket protocolthe connection is using.
  • protocol is a string (which may be empty) identifying the subprotocolthe socket is using.

Handling EventSource connections in Rack

EventSource connections provide a very similar interface, although because theyonly allow the server to send data to the client, there is noonmessage API.EventSource allows the server to push text messages to the client, where eachmessage has an optional event-type and ID.

# app.rbrequire'faye/websocket'App=lambdado |env|ifFaye::EventSource.eventsource?(env)es=Faye::EventSource.new(env)p[:open,es.url,es.last_event_id]# Periodically send messagesloop=EM.add_periodic_timer(1){es.send('Hello')}es.on:closedo |event|EM.cancel_timer(loop)es=nilend# Return async Rack responsees.rack_responseelse# Normal HTTP request[200,{'Content-Type'=>'text/plain'},['Hello']]endend

Thesend method takes two optional parameters,:event and:id. The defaultevent-type is'message' with no ID. For example, to send anotificationevent with ID99:

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

TheEventSource object exposes the following properties:

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

When you initialize an EventSource withFaye::EventSource.new, you can passconfiguration options after theenv parameter. Available options are:

  • :headers is a hash containing custom headers to be set on theEventSource response.
  • :retry is a number that tells the client how long (in seconds) it shouldwait 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 timeoutsset by proxies. The client will ignore these messages.

For example, this creates a connection that allows access from any origin, pingsevery 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 callinges.ping. Unlike WebSocketthe client does not send a response to this; it is merely to send some data overthe wire to keep the connection alive.

Running your socket application

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

Running the app with Thin

If you use Thin to serve your application you need to include this line afterloadingfaye/websocket:

Faye::WebSocket.load_adapter('thin')

Thin can be started via the command line if you've set up aconfig.ru file foryour application:

$ thin start -R config.ru -p 9292

Or, you can userackup. In development mode, this adds middlewares that don'twork 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 theRack::Handler interface common to many Rubyservers. You can configure Thin further in a block passed torun:

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=trueend

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, itshould work out of the box on Passenger provided you use at least Passenger4.0.

However, you do need to insert the following code inconfig.ru for optimalWebSocket performance in Passenger. This isdocumented in the Passenger manual.

ifdefined?(PhusionPassenger)PhusionPassenger.advertised_concurrency_level=0end

Run your app on Passenger for Nginx by creating a virtual host entry whichpoints 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 onthe Passengerwebsite.

Running the app with Puma

Puma has a command line interface for starting your application:

$ puma config.ru -p 9292

Or, you can userackup. In development mode, this adds middlewares that don'twork 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 theEventMachine backend and enable the adapter. Put this in yourrainbows.conffile:

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 yourconfig.ru file from the command line. Again,Rack::Lintwill 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 lineafter loadingfaye/websocket:

Faye::WebSocket.load_adapter('goliath')

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

require'goliath'require'./app'Faye::WebSocket.load_adapter('goliath')classEchoServer <Goliath::APIdefresponse(env)App.call(env)endend

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

require'goliath'require'faye/websocket'Faye::WebSocket.load_adapter('goliath')classEchoServer <Goliath::APIdefresponse(env)ws=Faye::WebSocket.new(env)ws.on:messagedo |event|ws.send(event.data)endws.rack_responseendend

About

Standards-compliant WebSocket client and server

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published

Contributors20

Languages


[8]ページ先頭

©2009-2025 Movatter.jp