faye-tls1-websocket 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +117 -0
- data/README.md +473 -0
- data/examples/app.rb +53 -0
- data/examples/autobahn_client.rb +51 -0
- data/examples/client.rb +32 -0
- data/examples/config.ru +14 -0
- data/examples/haproxy.conf +21 -0
- data/examples/rainbows.conf +3 -0
- data/examples/server.rb +53 -0
- data/examples/sse.html +39 -0
- data/examples/ws.html +44 -0
- data/lib/faye/adapters/goliath.rb +47 -0
- data/lib/faye/adapters/rainbows.rb +31 -0
- data/lib/faye/adapters/rainbows_client.rb +69 -0
- data/lib/faye/adapters/thin.rb +62 -0
- data/lib/faye/eventsource.rb +121 -0
- data/lib/faye/rack_stream.rb +70 -0
- data/lib/faye/websocket.rb +100 -0
- data/lib/faye/websocket/adapter.rb +21 -0
- data/lib/faye/websocket/api.rb +115 -0
- data/lib/faye/websocket/api/event.rb +33 -0
- data/lib/faye/websocket/api/event_target.rb +57 -0
- data/lib/faye/websocket/client.rb +64 -0
- metadata +232 -0
data/examples/app.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'faye/websocket'
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
static = Rack::File.new(File.dirname(__FILE__))
|
5
|
+
|
6
|
+
App = lambda do |env|
|
7
|
+
if Faye::WebSocket.websocket?(env)
|
8
|
+
ws = Faye::WebSocket.new(env, ['irc', 'xmpp'], :ping => 5)
|
9
|
+
p [:open, ws.url, ws.version, ws.protocol]
|
10
|
+
|
11
|
+
ws.onmessage = lambda do |event|
|
12
|
+
ws.send(event.data)
|
13
|
+
end
|
14
|
+
|
15
|
+
ws.onclose = lambda do |event|
|
16
|
+
p [:close, event.code, event.reason]
|
17
|
+
ws = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
ws.rack_response
|
21
|
+
|
22
|
+
elsif Faye::EventSource.eventsource?(env)
|
23
|
+
es = Faye::EventSource.new(env)
|
24
|
+
time = es.last_event_id.to_i
|
25
|
+
|
26
|
+
p [:open, es.url, es.last_event_id]
|
27
|
+
|
28
|
+
loop = EM.add_periodic_timer(2) do
|
29
|
+
time += 1
|
30
|
+
es.send("Time: #{time}")
|
31
|
+
EM.add_timer(1) do
|
32
|
+
es.send('Update!!', :event => 'update', :id => time) if es
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
es.send("Welcome!\n\nThis is an EventSource server.")
|
37
|
+
|
38
|
+
es.onclose = lambda do |event|
|
39
|
+
EM.cancel_timer(loop)
|
40
|
+
p [:close, es.url]
|
41
|
+
es = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
es.rack_response
|
45
|
+
|
46
|
+
else
|
47
|
+
static.call(env)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def App.log(message)
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'faye/websocket'
|
4
|
+
require 'cgi'
|
5
|
+
require 'progressbar'
|
6
|
+
|
7
|
+
EM.run {
|
8
|
+
host = 'ws://localhost:9001'
|
9
|
+
ruby = RUBY_PLATFORM =~ /java/ ? 'JRuby' : 'MRI'
|
10
|
+
agent = "#{ruby} #{RUBY_VERSION}"
|
11
|
+
cases = 0
|
12
|
+
skip = []
|
13
|
+
|
14
|
+
socket = Faye::WebSocket::Client.new("#{host}/getCaseCount")
|
15
|
+
progress = nil
|
16
|
+
|
17
|
+
socket.onmessage = lambda do |event|
|
18
|
+
puts "Total cases to run: #{event.data}"
|
19
|
+
cases = event.data.to_i
|
20
|
+
progress = ProgressBar.new('Autobahn', cases)
|
21
|
+
end
|
22
|
+
|
23
|
+
socket.onclose = lambda do |event|
|
24
|
+
run_case = lambda do |n|
|
25
|
+
progress.inc
|
26
|
+
|
27
|
+
if n > cases
|
28
|
+
socket = Faye::WebSocket::Client.new("#{host}/updateReports?agent=#{CGI.escape agent}")
|
29
|
+
progress.finish
|
30
|
+
socket.onclose = lambda { |e| EM.stop }
|
31
|
+
|
32
|
+
elsif skip.include?(n)
|
33
|
+
EM.next_tick { run_case.call(n+1) }
|
34
|
+
|
35
|
+
else
|
36
|
+
socket = Faye::WebSocket::Client.new("#{host}/runCase?case=#{n}&agent=#{CGI.escape agent}")
|
37
|
+
|
38
|
+
socket.onmessage = lambda do |event|
|
39
|
+
socket.send(event.data)
|
40
|
+
end
|
41
|
+
|
42
|
+
socket.on :close do |event|
|
43
|
+
run_case.call(n + 1)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
run_case.call(1)
|
49
|
+
end
|
50
|
+
}
|
51
|
+
|
data/examples/client.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'faye/websocket'
|
4
|
+
require 'eventmachine'
|
5
|
+
|
6
|
+
port = ARGV[0] || 7000
|
7
|
+
secure = ARGV[1] == 'ssl'
|
8
|
+
|
9
|
+
EM.run {
|
10
|
+
scheme = secure ? 'wss' : 'ws'
|
11
|
+
url = "#{scheme}://localhost:#{port}/"
|
12
|
+
headers = {'Origin' => 'http://faye.jcoglan.com'}
|
13
|
+
ws = Faye::WebSocket::Client.new(url, nil, :headers => headers)
|
14
|
+
|
15
|
+
puts "Connecting to #{ws.url}"
|
16
|
+
|
17
|
+
ws.onopen = lambda do |event|
|
18
|
+
p [:open]
|
19
|
+
ws.send("Hello, WebSocket!")
|
20
|
+
end
|
21
|
+
|
22
|
+
ws.onmessage = lambda do |event|
|
23
|
+
p [:message, event.data]
|
24
|
+
# ws.close 1002, 'Going away'
|
25
|
+
end
|
26
|
+
|
27
|
+
ws.onclose = lambda do |event|
|
28
|
+
p [:close, event.code, event.reason]
|
29
|
+
EM.stop
|
30
|
+
end
|
31
|
+
}
|
32
|
+
|
data/examples/config.ru
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# Run using your favourite server:
|
2
|
+
#
|
3
|
+
# thin start -R examples/config.ru -p 7000
|
4
|
+
# rainbows -c examples/rainbows.conf -E production examples/config.ru -p 7000
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'bundler/setup'
|
8
|
+
require File.expand_path('../app', __FILE__)
|
9
|
+
|
10
|
+
Faye::WebSocket.load_adapter('thin')
|
11
|
+
Faye::WebSocket.load_adapter('rainbows')
|
12
|
+
|
13
|
+
run App
|
14
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
defaults
|
2
|
+
mode http
|
3
|
+
timeout client 5s
|
4
|
+
timeout connect 5s
|
5
|
+
timeout server 5s
|
6
|
+
|
7
|
+
frontend all 0.0.0.0:3000
|
8
|
+
mode http
|
9
|
+
timeout client 120s
|
10
|
+
|
11
|
+
option forwardfor
|
12
|
+
option http-server-close
|
13
|
+
option http-pretend-keepalive
|
14
|
+
|
15
|
+
default_backend sockets
|
16
|
+
|
17
|
+
backend sockets
|
18
|
+
balance uri depth 2
|
19
|
+
timeout server 120s
|
20
|
+
server socket1 127.0.0.1:7000
|
21
|
+
|
data/examples/server.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'rack/content_length'
|
4
|
+
require 'rack/chunked'
|
5
|
+
|
6
|
+
port = ARGV[0] || 7000
|
7
|
+
secure = ARGV[1] == 'ssl'
|
8
|
+
engine = ARGV[2] || 'thin'
|
9
|
+
spec = File.expand_path('../../spec', __FILE__)
|
10
|
+
|
11
|
+
require File.expand_path('../app', __FILE__)
|
12
|
+
Faye::WebSocket.load_adapter(engine)
|
13
|
+
|
14
|
+
case engine
|
15
|
+
|
16
|
+
when 'goliath'
|
17
|
+
class WebSocketServer < Goliath::API
|
18
|
+
def response(env)
|
19
|
+
App.call(env)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
when 'puma'
|
24
|
+
events = Puma::Events.new($stdout, $stderr)
|
25
|
+
binder = Puma::Binder.new(events)
|
26
|
+
binder.parse(["tcp://0.0.0.0:#{port}"], App)
|
27
|
+
server = Puma::Server.new(App, events)
|
28
|
+
server.binder = binder
|
29
|
+
server.run.join
|
30
|
+
|
31
|
+
when 'rainbows'
|
32
|
+
rackup = Unicorn::Configurator::RACKUP
|
33
|
+
rackup[:port] = port
|
34
|
+
rackup[:set_listener] = true
|
35
|
+
options = rackup[:options]
|
36
|
+
options[:config_file] = File.expand_path('../rainbows.conf', __FILE__)
|
37
|
+
Rainbows::HttpServer.new(App, options).start.join
|
38
|
+
|
39
|
+
when 'thin'
|
40
|
+
EM.run {
|
41
|
+
thin = Rack::Handler.get('thin')
|
42
|
+
thin.run(App, :Port => port) do |server|
|
43
|
+
if secure
|
44
|
+
server.ssl_options = {
|
45
|
+
:private_key_file => spec + '/server.key',
|
46
|
+
:cert_chain_file => spec + '/server.crt'
|
47
|
+
}
|
48
|
+
server.ssl = true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
data/examples/sse.html
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
5
|
+
<title>EventSource test</title>
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
|
9
|
+
<h1>EventSource test</h1>
|
10
|
+
<ul></ul>
|
11
|
+
|
12
|
+
<script type="text/javascript">
|
13
|
+
var logger = document.getElementsByTagName('ul')[0],
|
14
|
+
socket = new EventSource('/');
|
15
|
+
|
16
|
+
var log = function(text) {
|
17
|
+
logger.innerHTML += '<li>' + text + '</li>';
|
18
|
+
};
|
19
|
+
|
20
|
+
socket.onopen = function() {
|
21
|
+
log('OPEN');
|
22
|
+
};
|
23
|
+
|
24
|
+
socket.onmessage = function(event) {
|
25
|
+
log('MESSAGE: ' + event.data);
|
26
|
+
};
|
27
|
+
|
28
|
+
socket.addEventListener('update', function(event) {
|
29
|
+
log('UPDATE(' + event.lastEventId + '): ' + event.data);
|
30
|
+
});
|
31
|
+
|
32
|
+
socket.onerror = function(event) {
|
33
|
+
log('ERROR: ' + event.message);
|
34
|
+
};
|
35
|
+
</script>
|
36
|
+
|
37
|
+
</body>
|
38
|
+
</html>
|
39
|
+
|
data/examples/ws.html
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
5
|
+
<title>WebSocket test</title>
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
|
9
|
+
<h1>WebSocket test</h1>
|
10
|
+
<ul></ul>
|
11
|
+
|
12
|
+
<script type="text/javascript">
|
13
|
+
var logger = document.getElementsByTagName('ul')[0],
|
14
|
+
Socket = window.MozWebSocket || window.WebSocket,
|
15
|
+
protos = ['foo', 'bar', 'xmpp'],
|
16
|
+
socket = new Socket('ws://' + location.hostname + ':' + location.port + '/', protos),
|
17
|
+
index = 0;
|
18
|
+
|
19
|
+
var log = function(text) {
|
20
|
+
logger.innerHTML += '<li>' + text + '</li>';
|
21
|
+
};
|
22
|
+
|
23
|
+
socket.addEventListener('open', function() {
|
24
|
+
log('OPEN: ' + socket.protocol);
|
25
|
+
socket.send('Hello, world');
|
26
|
+
});
|
27
|
+
|
28
|
+
socket.onerror = function(event) {
|
29
|
+
log('ERROR: ' + event.message);
|
30
|
+
};
|
31
|
+
|
32
|
+
socket.onmessage = function(event) {
|
33
|
+
log('MESSAGE: ' + event.data);
|
34
|
+
setTimeout(function() { socket.send(++index + ' ' + event.data) }, 2000);
|
35
|
+
};
|
36
|
+
|
37
|
+
socket.onclose = function(event) {
|
38
|
+
log('CLOSE: ' + event.code + ', ' + event.reason);
|
39
|
+
};
|
40
|
+
</script>
|
41
|
+
|
42
|
+
</body>
|
43
|
+
</html>
|
44
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class Goliath::Connection
|
2
|
+
attr_accessor :socket_stream
|
3
|
+
alias :goliath_receive_data :receive_data
|
4
|
+
|
5
|
+
def receive_data(data)
|
6
|
+
if @serving == :websocket
|
7
|
+
socket_stream.receive(data) if socket_stream
|
8
|
+
else
|
9
|
+
goliath_receive_data(data)
|
10
|
+
socket_stream.receive(@parser.upgrade_data) if socket_stream
|
11
|
+
@serving = :websocket if @api.websocket?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def unbind
|
16
|
+
super
|
17
|
+
ensure
|
18
|
+
socket_stream.fail if socket_stream
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Goliath::API
|
23
|
+
include Faye::WebSocket::Adapter
|
24
|
+
end
|
25
|
+
|
26
|
+
class Goliath::Request
|
27
|
+
alias :goliath_process :process
|
28
|
+
|
29
|
+
def process
|
30
|
+
env['em.connection'] = conn
|
31
|
+
goliath_process
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Goliath::Response
|
36
|
+
alias :goliath_head :head
|
37
|
+
alias :goliath_headers_output :headers_output
|
38
|
+
|
39
|
+
def head
|
40
|
+
(status == 101) ? '' : goliath_head
|
41
|
+
end
|
42
|
+
|
43
|
+
def headers_output
|
44
|
+
(status == 101) ? '' : goliath_headers_output
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# WebSocket extensions for Rainbows
|
2
|
+
# Based on code from the Cramp project
|
3
|
+
# http://github.com/lifo/cramp
|
4
|
+
|
5
|
+
# Copyright (c) 2009-2011 Pratik Naik
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
15
|
+
# all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
23
|
+
# SOFTWARE.
|
24
|
+
|
25
|
+
module Faye
|
26
|
+
class WebSocket
|
27
|
+
autoload :RainbowsClient, File.expand_path('../rainbows_client', __FILE__)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
Rainbows::O[:em_client_class] = "Faye::WebSocket::RainbowsClient"
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# WebSocket extensions for Rainbows
|
2
|
+
# Based on code from the Cramp project
|
3
|
+
# http://github.com/lifo/cramp
|
4
|
+
|
5
|
+
# Copyright (c) 2009-2011 Pratik Naik
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
15
|
+
# all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
23
|
+
# SOFTWARE.
|
24
|
+
|
25
|
+
module Faye
|
26
|
+
class WebSocket
|
27
|
+
|
28
|
+
class RainbowsClient < Rainbows::EventMachine::Client
|
29
|
+
include Faye::WebSocket::Adapter
|
30
|
+
attr_accessor :socket_stream
|
31
|
+
|
32
|
+
def receive_data(data)
|
33
|
+
return super unless @state == :websocket
|
34
|
+
socket_stream.receive(data) if socket_stream
|
35
|
+
end
|
36
|
+
|
37
|
+
def app_call(*args)
|
38
|
+
@env['em.connection'] = self
|
39
|
+
if args.first == NULL_IO and @hp.content_length == 0 and websocket?
|
40
|
+
prepare_request_body
|
41
|
+
else
|
42
|
+
super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def on_read(data)
|
47
|
+
if @state == :body and websocket? and @hp.body_eof?
|
48
|
+
@state = :websocket
|
49
|
+
@input.rewind
|
50
|
+
app_call StringIO.new(@buf)
|
51
|
+
else
|
52
|
+
super
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def unbind
|
57
|
+
super
|
58
|
+
ensure
|
59
|
+
socket_stream.fail if socket_stream
|
60
|
+
end
|
61
|
+
|
62
|
+
def write_headers(status, headers, *args)
|
63
|
+
super unless socket_connection? and status == 101
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|