websocket-rack 0.1.4 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|