websocket-rack 0.1.4 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +11 -0
- data/README.md +42 -24
- data/Rakefile +1 -1
- data/example/example.ru +5 -5
- data/lib/rack/websocket/application.rb +31 -60
- data/lib/rack/websocket/extensions/common.rb +61 -0
- data/lib/rack/websocket/extensions/thin/connection.rb +2 -50
- data/lib/rack/websocket/extensions/thin.rb +3 -3
- data/lib/rack/websocket/extensions.rb +14 -0
- data/lib/rack/websocket/handler/base.rb +41 -0
- data/lib/rack/websocket/handler/stub.rb +14 -0
- data/lib/rack/websocket/handler/thin/connection.rb +89 -0
- data/lib/rack/websocket/handler/thin/handler_factory.rb +56 -0
- data/lib/rack/websocket/handler/thin.rb +61 -0
- data/lib/rack/websocket/handler.rb +15 -38
- data/lib/rack/websocket/version.rb +5 -0
- data/lib/rack/websocket.rb +5 -31
- data/spec/spec_helper.rb +18 -0
- data/spec/support/all_drafts.rb +43 -0
- data/spec/support/all_handlers.rb +31 -0
- data/spec/support/requests.rb +100 -0
- data/spec/thin_spec.rb +46 -0
- data/websocket-rack.gemspec +4 -4
- metadata +41 -47
- data/lib/rack/websocket/connection.rb +0 -112
- data/lib/rack/websocket/debugger.rb +0 -17
- data/lib/rack/websocket/framing03.rb +0 -178
- data/lib/rack/websocket/framing76.rb +0 -115
- data/lib/rack/websocket/handler03.rb +0 -14
- data/lib/rack/websocket/handler75.rb +0 -8
- data/lib/rack/websocket/handler76.rb +0 -11
- data/lib/rack/websocket/handler_factory.rb +0 -61
- data/lib/rack/websocket/handshake75.rb +0 -21
- data/lib/rack/websocket/handshake76.rb +0 -71
- data/spec/helper.rb +0 -44
- data/spec/integration/draft03_spec.rb +0 -252
- data/spec/integration/draft76_spec.rb +0 -212
- data/spec/unit/framing_spec.rb +0 -108
- data/spec/unit/handler_spec.rb +0 -136
- data/spec/websocket_spec.rb +0 -210
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.2.1 / 2011-04-01
|
4
|
+
|
5
|
+
- bugfix: env passed to callbacks should be valid now
|
6
|
+
|
7
|
+
## 0.2.0 / 2011-04-01
|
8
|
+
|
9
|
+
- prepare for supporting server other that thin
|
10
|
+
- small changes of API
|
11
|
+
- change handling backend options
|
12
|
+
- depend on em-websocket instead of copying source
|
13
|
+
|
3
14
|
## 0.1.4 / 2011-03-13
|
4
15
|
|
5
16
|
- performance improvements thanks to rbtrace
|
data/README.md
CHANGED
@@ -12,7 +12,7 @@ Create sample rack config file, and inside build app basing on Rack::WebSocket::
|
|
12
12
|
end
|
13
13
|
|
14
14
|
map '/' do
|
15
|
-
run MyApp
|
15
|
+
run MyApp.new
|
16
16
|
end
|
17
17
|
|
18
18
|
After that just run Rack config from Rack server:
|
@@ -25,50 +25,50 @@ Done.
|
|
25
25
|
|
26
26
|
Rack::WebSocket::Application make following methods available:
|
27
27
|
|
28
|
-
### on_open
|
28
|
+
### on_open(env)
|
29
29
|
|
30
|
-
Called after client is connected.
|
30
|
+
Called after client is connected. Rack env of client is passed as attribute.
|
31
31
|
|
32
32
|
Example:
|
33
33
|
|
34
34
|
class MyApp < Rack::WebSocket::Application
|
35
|
-
def on_open
|
35
|
+
def on_open(env)
|
36
36
|
puts "Clien connected"
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
### on_close
|
40
|
+
### on_close(env)
|
41
41
|
|
42
|
-
Called after client is disconnected
|
42
|
+
Called after client is disconnected. Rack env of client is passed as attribute.
|
43
43
|
|
44
44
|
Example:
|
45
45
|
|
46
46
|
class MyApp < Rack::WebSocket::Application
|
47
|
-
def on_close
|
47
|
+
def on_close(env)
|
48
48
|
puts "Clien disconnected"
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
### on_message(msg)
|
52
|
+
### on_message(env, msg)
|
53
53
|
|
54
|
-
Called after server receive message
|
54
|
+
Called after server receive message. Rack env of client is passed as attribute.
|
55
55
|
|
56
56
|
Example:
|
57
57
|
|
58
58
|
class MyApp < Rack::WebSocket::Application
|
59
|
-
def on_message(msg)
|
59
|
+
def on_message(env, msg)
|
60
60
|
puts "Received message: " + msg
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
-
### on_error(error)
|
64
|
+
### on_error(env, error)
|
65
65
|
|
66
66
|
Called after server catch error. Variable passed is instance of Ruby Exception class.
|
67
67
|
|
68
68
|
Example:
|
69
69
|
|
70
70
|
class MyApp < Rack::WebSocket::Application
|
71
|
-
def on_error(error)
|
71
|
+
def on_error(env, error)
|
72
72
|
puts "Error occured: " + error.message
|
73
73
|
end
|
74
74
|
end
|
@@ -80,34 +80,50 @@ Sends data do client.
|
|
80
80
|
Example:
|
81
81
|
|
82
82
|
class MyApp < Rack::WebSocket::Application
|
83
|
-
def on_open
|
83
|
+
def on_open(env)
|
84
84
|
send_data "Hello to you!"
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
+
### close_websocket
|
89
|
+
|
90
|
+
Closes connection.
|
91
|
+
|
92
|
+
Example:
|
93
|
+
|
94
|
+
class MyApp < Rack::WebSocket::Application
|
95
|
+
def on_open(env)
|
96
|
+
close_websocket if env['REQUEST_PATH'] != '/websocket'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
88
100
|
## Available variables:
|
89
101
|
|
90
|
-
### @
|
102
|
+
### @options
|
91
103
|
|
92
|
-
|
104
|
+
Options passed to app on initialize.
|
93
105
|
|
94
|
-
|
106
|
+
Example:
|
95
107
|
|
96
|
-
|
108
|
+
# In config.ru
|
109
|
+
map '/' do
|
110
|
+
run MyApp.new :some => :variable
|
111
|
+
end
|
112
|
+
|
113
|
+
# In application instance
|
114
|
+
@options # => { :some => :variable }
|
97
115
|
|
98
116
|
## FAQ
|
99
117
|
|
100
118
|
### Which Rack servers are supported?
|
101
119
|
|
102
|
-
Currently
|
120
|
+
Currently we are supporting following servers:
|
103
121
|
|
104
|
-
|
105
|
-
|
106
|
-
This is bug in EventMachine < 1.0.0. Please consider updating to newer version or use thin-websocket wrapper around thin binary.
|
122
|
+
- Thin
|
107
123
|
|
108
124
|
### How to enable debugging?
|
109
125
|
|
110
|
-
Just use :
|
126
|
+
Just use :backend => { :debug => true } option when initializing your app.
|
111
127
|
|
112
128
|
### How to enable wss/SSL support?
|
113
129
|
|
@@ -117,10 +133,12 @@ Thin v1.2.8 have --ssl option - just use that! :)
|
|
117
133
|
|
118
134
|
Check [Thin](http://code.macournoyer.com/thin/) config - any option supported by Thin(like demonizing, SSL etc.) is supported by WebSocket-Rack.
|
119
135
|
|
136
|
+
### Why (using Thin) user is disconnected after 30 seconds?
|
137
|
+
|
138
|
+
This is bug in EventMachine < 1.0.0. Please consider updating to newer version or use thin-websocket wrapper around thin binary.
|
139
|
+
|
120
140
|
## About
|
121
141
|
|
122
142
|
Author: Bernard Potocki <<bernard.potocki@imanel.org>>
|
123
143
|
|
124
|
-
Most source taken from [em-websocket](http://github.com/igrigorik/em-websocket)
|
125
|
-
|
126
144
|
Released under MIT license.
|
data/Rakefile
CHANGED
data/example/example.ru
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'lib/rack/websocket'
|
2
2
|
|
3
3
|
class MyApp < Rack::WebSocket::Application
|
4
|
-
def on_open
|
4
|
+
def on_open(env)
|
5
5
|
puts "client connected"
|
6
6
|
EM.add_timer(5) do
|
7
7
|
send_data "This message should show-up 5 secs later"
|
@@ -12,12 +12,12 @@ class MyApp < Rack::WebSocket::Application
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
def on_message(msg)
|
15
|
+
def on_message(env, msg)
|
16
16
|
puts "message received: " + msg
|
17
|
-
send_data "
|
17
|
+
send_data "Message: #{msg}"
|
18
18
|
end
|
19
19
|
|
20
|
-
def on_close
|
20
|
+
def on_close(env)
|
21
21
|
puts "client disconnected"
|
22
22
|
end
|
23
23
|
end
|
@@ -29,5 +29,5 @@ map '/' do
|
|
29
29
|
end
|
30
30
|
|
31
31
|
map '/websocket' do
|
32
|
-
run MyApp.new # :
|
32
|
+
run MyApp.new # :backend => { :debug => true }
|
33
33
|
end
|
@@ -2,77 +2,48 @@ module Rack
|
|
2
2
|
module WebSocket
|
3
3
|
class Application
|
4
4
|
|
5
|
+
DEFAULT_OPTIONS = {}
|
6
|
+
|
5
7
|
class << self
|
6
|
-
|
7
|
-
if args.last == {:real_run => true}
|
8
|
-
args.pop
|
9
|
-
super(*args)
|
10
|
-
else
|
11
|
-
proc do |env|
|
12
|
-
self.new(*(args << {:real_run => true})).call(env)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
8
|
+
attr_accessor :websocket_handler
|
16
9
|
end
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def
|
22
|
-
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
def initialize(*args)
|
27
|
-
app, options = args[0], args[1]
|
28
|
-
app, options = nil, app if app.is_a?(Hash)
|
29
|
-
@options = DEFAULT_OPTIONS.merge(options || {})
|
30
|
-
@app = app
|
10
|
+
|
11
|
+
def on_open(env); end
|
12
|
+
def on_message(env, msg); end
|
13
|
+
def on_close(env); end
|
14
|
+
def on_error(env, error); end
|
15
|
+
|
16
|
+
def initialize(options = {})
|
17
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
31
18
|
end
|
32
|
-
|
19
|
+
|
33
20
|
def call(env)
|
34
|
-
|
35
|
-
|
36
|
-
socket = env['async.connection']
|
37
|
-
@conn = Connection.new(self, socket, :debug => !!@options[:websocket_debug])
|
38
|
-
@conn.dispatch(env) ? async_response : failure_response
|
39
|
-
elsif @app
|
40
|
-
@app.call(env)
|
41
|
-
else
|
42
|
-
not_found_response
|
43
|
-
end
|
21
|
+
detect_handler(env)
|
22
|
+
dup._call(env)
|
44
23
|
end
|
45
|
-
|
46
|
-
def
|
47
|
-
|
48
|
-
@conn.close_websocket
|
49
|
-
else
|
50
|
-
raise WebSocketError, "WebSocket not opened"
|
51
|
-
end
|
24
|
+
|
25
|
+
def _call(env)
|
26
|
+
websocket_handler.call(env)
|
52
27
|
end
|
53
|
-
|
54
|
-
def
|
55
|
-
if
|
56
|
-
|
28
|
+
|
29
|
+
def method_missing(sym, *args, &block)
|
30
|
+
if websocket_handler && websocket_handler.respond_to?(sym)
|
31
|
+
websocket_handler.send sym, *args, &block
|
57
32
|
else
|
58
|
-
|
33
|
+
super
|
59
34
|
end
|
60
35
|
end
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
def
|
65
|
-
|
66
|
-
end
|
67
|
-
|
68
|
-
def failure_response
|
69
|
-
[ 400, { "Content-Type" => "text/plain" }, [ 'invalid data' ] ]
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def detect_handler(env)
|
40
|
+
self.class.websocket_handler ||= Handler.detect(env)
|
70
41
|
end
|
71
|
-
|
72
|
-
def
|
73
|
-
|
42
|
+
|
43
|
+
def websocket_handler
|
44
|
+
@websocket_handler ||= self.class.websocket_handler.new(self, @options || {})
|
74
45
|
end
|
75
|
-
|
46
|
+
|
76
47
|
end
|
77
48
|
end
|
78
49
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Rack
|
2
|
+
module WebSocket
|
3
|
+
module Extensions
|
4
|
+
module Common
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
alias :receive_data_without_websocket :receive_data
|
9
|
+
alias :receive_data :receive_data_with_websocket
|
10
|
+
|
11
|
+
alias :unbind_without_websocket :unbind
|
12
|
+
alias :unbind :unbind_with_websocket
|
13
|
+
|
14
|
+
alias :receive_data_without_flash_policy_file :receive_data
|
15
|
+
alias :receive_data :receive_data_with_flash_policy_file
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_accessor :websocket
|
20
|
+
|
21
|
+
def websocket?
|
22
|
+
!self.websocket.nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def receive_data_with_websocket(data)
|
26
|
+
if self.websocket?
|
27
|
+
self.websocket.receive_data(data)
|
28
|
+
else
|
29
|
+
receive_data_without_websocket(data)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def unbind_with_websocket
|
34
|
+
if self.websocket?
|
35
|
+
self.websocket.unbind
|
36
|
+
else
|
37
|
+
unbind_without_websocket
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def receive_data_with_flash_policy_file(data)
|
42
|
+
# thin require data to be proper http request - in it's not
|
43
|
+
# then @request.parse raises exception and data isn't parsed
|
44
|
+
# by futher methods. Here we only check if it is flash
|
45
|
+
# policy file request ("<policy-file-request/>\000") and
|
46
|
+
# if so then flash policy file is returned. if not then
|
47
|
+
# rest of request is handled.
|
48
|
+
if (data == "<policy-file-request/>\000")
|
49
|
+
file = '<?xml version="1.0"?><cross-domain-policy><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>'
|
50
|
+
# ignore errors - we will close this anyway
|
51
|
+
send_data(file) rescue nil
|
52
|
+
close_connection_after_writing
|
53
|
+
else
|
54
|
+
receive_data_without_flash_policy_file(data)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -4,66 +4,18 @@ module Rack
|
|
4
4
|
module Thin
|
5
5
|
module Connection
|
6
6
|
|
7
|
-
def self.included(
|
8
|
-
|
7
|
+
def self.included(base)
|
8
|
+
base.class_eval do
|
9
9
|
alias :pre_process_without_websocket :pre_process
|
10
10
|
alias :pre_process :pre_process_with_websocket
|
11
|
-
|
12
|
-
alias :receive_data_without_websocket :receive_data
|
13
|
-
alias :receive_data :receive_data_with_websocket
|
14
|
-
|
15
|
-
alias :unbind_without_websocket :unbind
|
16
|
-
alias :unbind :unbind_with_websocket
|
17
|
-
|
18
|
-
alias :receive_data_without_flash_policy_file :receive_data
|
19
|
-
alias :receive_data :receive_data_with_flash_policy_file
|
20
11
|
end
|
21
12
|
end
|
22
13
|
|
23
|
-
attr_accessor :websocket
|
24
|
-
|
25
|
-
def websocket?
|
26
|
-
!self.websocket.nil?
|
27
|
-
end
|
28
|
-
|
29
14
|
def pre_process_with_websocket
|
30
15
|
@request.env['async.connection'] = self
|
31
16
|
pre_process_without_websocket
|
32
17
|
end
|
33
18
|
|
34
|
-
def receive_data_with_websocket(data)
|
35
|
-
if self.websocket?
|
36
|
-
self.websocket.receive_data(data)
|
37
|
-
else
|
38
|
-
receive_data_without_websocket(data)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def unbind_with_websocket
|
43
|
-
if self.websocket?
|
44
|
-
self.websocket.unbind
|
45
|
-
else
|
46
|
-
unbind_without_websocket
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def receive_data_with_flash_policy_file(data)
|
51
|
-
# thin require data to be proper http request - in it's not
|
52
|
-
# then @request.parse raises exception and data isn't parsed
|
53
|
-
# by futher methods. Here we only check if it is flash
|
54
|
-
# policy file request ("<policy-file-request/>\000") and
|
55
|
-
# if so then flash policy file is returned. if not then
|
56
|
-
# rest of request is handled.
|
57
|
-
if (data == "<policy-file-request/>\000")
|
58
|
-
file = '<?xml version="1.0"?><cross-domain-policy><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>'
|
59
|
-
# ignore errors - we will close this anyway
|
60
|
-
send_data(file) rescue nil
|
61
|
-
close_connection_after_writing
|
62
|
-
else
|
63
|
-
receive_data_without_flash_policy_file(data)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
19
|
end
|
68
20
|
end
|
69
21
|
end
|
@@ -5,9 +5,9 @@ module Rack
|
|
5
5
|
|
6
6
|
autoload :Connection, "#{::File.dirname(__FILE__)}/thin/connection"
|
7
7
|
|
8
|
-
def self.
|
9
|
-
|
10
|
-
|
8
|
+
def self.apply!
|
9
|
+
::Thin::Connection.send(:include, ::Rack::WebSocket::Extensions::Common)
|
10
|
+
::Thin::Connection.send(:include, ::Rack::WebSocket::Extensions::Thin::Connection)
|
11
11
|
end
|
12
12
|
|
13
13
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Rack
|
2
|
+
module WebSocket
|
3
|
+
module Extensions
|
4
|
+
|
5
|
+
autoload :Common, "#{ROOT_PATH}/websocket/extensions/common"
|
6
|
+
autoload :Thin, "#{ROOT_PATH}/websocket/extensions/thin"
|
7
|
+
|
8
|
+
def self.apply!
|
9
|
+
Thin.apply! if defined?(::Thin)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Rack
|
2
|
+
module WebSocket
|
3
|
+
module Handler
|
4
|
+
class Base
|
5
|
+
|
6
|
+
def on_open; @parent.on_open(@env); end # Fired when a client is connected.
|
7
|
+
def on_message(msg); @parent.on_message(@env, msg); end # Fired when a message from a client is received.
|
8
|
+
def on_close; @parent.on_close(@env); end # Fired when a client is disconnected.
|
9
|
+
def on_error(error); @parent.on_error(@env, error); end # Fired when error occurs.
|
10
|
+
|
11
|
+
def initialize(parent, options = {})
|
12
|
+
@parent = parent
|
13
|
+
@options = options[:backend] || {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
raise 'Not implemented'
|
18
|
+
end
|
19
|
+
|
20
|
+
def send_data(data)
|
21
|
+
raise 'Not implemented'
|
22
|
+
end
|
23
|
+
|
24
|
+
def close_websocket
|
25
|
+
raise 'Not implemented'
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def async_response
|
31
|
+
[-1, {}, []]
|
32
|
+
end
|
33
|
+
|
34
|
+
def failure_response
|
35
|
+
[ 400, {'Content-Type' => 'text/plain'}, [ 'Bad request' ] ]
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module WebSocket
|
5
|
+
module Handler
|
6
|
+
class Thin
|
7
|
+
class Connection < ::EventMachine::WebSocket::Connection
|
8
|
+
|
9
|
+
# Overwrite new from EventMachine
|
10
|
+
def self.new(*args)
|
11
|
+
instance = allocate
|
12
|
+
instance.__send__(:initialize, *args)
|
13
|
+
instance
|
14
|
+
end
|
15
|
+
|
16
|
+
def trigger_on_message(msg)
|
17
|
+
@app.on_message(msg)
|
18
|
+
end
|
19
|
+
def trigger_on_open
|
20
|
+
@app.on_open
|
21
|
+
end
|
22
|
+
def trigger_on_close
|
23
|
+
@app.on_close
|
24
|
+
end
|
25
|
+
def trigger_on_error(error)
|
26
|
+
@app.on_error(error)
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(app, socket, options = {})
|
30
|
+
@app = app
|
31
|
+
@socket = socket
|
32
|
+
@options = options
|
33
|
+
@debug = options[:debug] || false
|
34
|
+
@ssl = socket.backend.respond_to?(:ssl?) && socket.backend.ssl?
|
35
|
+
|
36
|
+
socket.websocket = self
|
37
|
+
socket.comm_inactivity_timeout = 0
|
38
|
+
|
39
|
+
if socket.comm_inactivity_timeout != 0
|
40
|
+
puts "WARNING: You are using old EventMachine version. " +
|
41
|
+
"Please consider updating to EM version >= 1.0.0 " +
|
42
|
+
"or running Thin using thin-websocket."
|
43
|
+
end
|
44
|
+
|
45
|
+
debug [:initialize]
|
46
|
+
end
|
47
|
+
|
48
|
+
def dispatch(request)
|
49
|
+
return false if request.nil?
|
50
|
+
debug [:inbound_headers, request]
|
51
|
+
@handler = HandlerFactory.build_with_request(self, request, request['body'], @ssl, @debug)
|
52
|
+
unless @handler
|
53
|
+
# The whole header has not been received yet.
|
54
|
+
return false
|
55
|
+
end
|
56
|
+
@handler.run
|
57
|
+
return true
|
58
|
+
rescue => e
|
59
|
+
debug [:error, e]
|
60
|
+
process_bad_request(e)
|
61
|
+
return false
|
62
|
+
end
|
63
|
+
|
64
|
+
def process_bad_request(reason)
|
65
|
+
trigger_on_error(reason)
|
66
|
+
send_data "HTTP/1.1 400 Bad request\r\n\r\n"
|
67
|
+
close_connection_after_writing
|
68
|
+
end
|
69
|
+
|
70
|
+
def close_with_error(message)
|
71
|
+
trigger_on_error(message)
|
72
|
+
close_connection_after_writing
|
73
|
+
end
|
74
|
+
|
75
|
+
# Overwrite send_data from EventMachine
|
76
|
+
def send_data(*args)
|
77
|
+
@socket.send_data(*args)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Overwrite close_connection from EventMachine
|
81
|
+
def close_connection(*args)
|
82
|
+
@socket.close_connection(*args)
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Rack
|
2
|
+
module WebSocket
|
3
|
+
module Handler
|
4
|
+
class Thin
|
5
|
+
class HandlerFactory < ::EventMachine::WebSocket::HandlerFactory
|
6
|
+
|
7
|
+
# Bottom half of em-websocket HandlerFactory
|
8
|
+
# Taken from http://github.com/dj2/em-websocket
|
9
|
+
# This method is also used in experimental branch of Goliath
|
10
|
+
def self.build_with_request(connection, request, remains, secure = false, debug = false)
|
11
|
+
version = request['Sec-WebSocket-Key1'] ? 76 : 75
|
12
|
+
case version
|
13
|
+
when 75
|
14
|
+
if !remains.empty?
|
15
|
+
raise ::EventMachine::WebSocket::HandshakeError, "Extra bytes after header"
|
16
|
+
end
|
17
|
+
when 76
|
18
|
+
if remains.length < 8
|
19
|
+
# The whole third-key has not been received yet.
|
20
|
+
return nil
|
21
|
+
elsif remains.length > 8
|
22
|
+
raise ::EventMachine::WebSocket::HandshakeError, "Extra bytes after third key"
|
23
|
+
end
|
24
|
+
request['Third-Key'] = remains
|
25
|
+
else
|
26
|
+
raise ::EventMachine::WebSocket::WebSocketError, "Must not happen"
|
27
|
+
end
|
28
|
+
|
29
|
+
unless request['Connection'] == 'Upgrade' and request['Upgrade'] == 'WebSocket'
|
30
|
+
raise ::EventMachine::WebSocket::HandshakeError, "Connection and Upgrade headers required"
|
31
|
+
end
|
32
|
+
|
33
|
+
# transform headers
|
34
|
+
protocol = (secure ? "wss" : "ws")
|
35
|
+
request['Host'] = Addressable::URI.parse("#{protocol}://"+request['Host'])
|
36
|
+
|
37
|
+
if version = request['Sec-WebSocket-Draft']
|
38
|
+
if version == '1' || version == '2' || version == '3'
|
39
|
+
# We'll use handler03 - I believe they're all compatible
|
40
|
+
::EventMachine::WebSocket::Handler03.new(connection, request, debug)
|
41
|
+
else
|
42
|
+
# According to spec should abort the connection
|
43
|
+
raise ::EventMachine::WebSocket::WebSocketError, "Unknown draft version: #{version}"
|
44
|
+
end
|
45
|
+
elsif request['Sec-WebSocket-Key1']
|
46
|
+
::EventMachine::WebSocket::Handler76.new(connection, request, debug)
|
47
|
+
else
|
48
|
+
::EventMachine::WebSocket::Handler75.new(connection, request, debug)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|