websocket-rack-noodles 0.4.0
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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +61 -0
- data/Gemfile +7 -0
- data/README.md +196 -0
- data/Rakefile +11 -0
- data/example/example.ru +33 -0
- data/example/html/FABridge.js +604 -0
- data/example/html/WebSocketMain.swf +0 -0
- data/example/html/index.html +76 -0
- data/example/html/swfobject.js +4 -0
- data/example/html/web_socket.js +388 -0
- data/lib/rack/websocket/application.rb +56 -0
- data/lib/rack/websocket/extensions/common.rb +67 -0
- data/lib/rack/websocket/extensions/thin/connection.rb +24 -0
- data/lib/rack/websocket/extensions/thin.rb +16 -0
- data/lib/rack/websocket/extensions.rb +14 -0
- data/lib/rack/websocket/handler/base/connection.rb +83 -0
- data/lib/rack/websocket/handler/base.rb +68 -0
- data/lib/rack/websocket/handler/stub.rb +15 -0
- data/lib/rack/websocket/handler/thin.rb +58 -0
- data/lib/rack/websocket/handler.rb +21 -0
- data/lib/rack/websocket/version.rb +5 -0
- data/lib/rack/websocket.rb +14 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/all_drafts.rb +44 -0
- data/spec/support/all_handlers.rb +80 -0
- data/spec/support/masked_messages.rb +9 -0
- data/spec/support/requests.rb +249 -0
- data/spec/thin_spec.rb +48 -0
- data/websocket-rack.gemspec +24 -0
- metadata +137 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
module Rack
|
2
|
+
module WebSocket
|
3
|
+
module Handler
|
4
|
+
class Base
|
5
|
+
|
6
|
+
autoload :Connection, "#{ROOT_PATH}/websocket/handler/base/connection"
|
7
|
+
|
8
|
+
def on_open
|
9
|
+
set_env_instance_variable
|
10
|
+
@parent.on_open(@env)
|
11
|
+
end # Fired when a client is connected.
|
12
|
+
|
13
|
+
def on_message(msg)
|
14
|
+
set_env_instance_variable
|
15
|
+
@parent.on_message(@env, msg)
|
16
|
+
end # Fired when a message from a client is received.
|
17
|
+
|
18
|
+
def on_close
|
19
|
+
set_env_instance_variable
|
20
|
+
@parent.on_close(@env)
|
21
|
+
end # Fired when a client is disconnected.
|
22
|
+
|
23
|
+
def on_error(error)
|
24
|
+
set_env_instance_variable
|
25
|
+
@parent.on_error(@env, error)
|
26
|
+
end # Fired when error occurs.
|
27
|
+
|
28
|
+
# Set application as parent and forward options
|
29
|
+
def initialize(parent, options = {})
|
30
|
+
@parent = parent
|
31
|
+
@options = options[:backend] || {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def set_env_instance_variable
|
35
|
+
@parent.instance_variable_set("@env", @env)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Implemented in subclass
|
39
|
+
def call(env)
|
40
|
+
raise 'Not implemented'
|
41
|
+
end
|
42
|
+
|
43
|
+
# Implemented in subclass
|
44
|
+
def send_data(data)
|
45
|
+
raise 'Not implemented'
|
46
|
+
end
|
47
|
+
|
48
|
+
# Implemented in subclass
|
49
|
+
def close_websocket
|
50
|
+
raise 'Not implemented'
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
# Standard async response
|
56
|
+
def async_response
|
57
|
+
[-1, {}, []]
|
58
|
+
end
|
59
|
+
|
60
|
+
# Standard 400 response
|
61
|
+
def failure_response
|
62
|
+
[ 400, {'Content-Type' => 'text/plain'}, [ 'Bad request' ] ]
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'thin'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module WebSocket
|
5
|
+
module Handler
|
6
|
+
class Thin < Base
|
7
|
+
|
8
|
+
# Build request from Rack env
|
9
|
+
def call(env)
|
10
|
+
@env = env
|
11
|
+
socket = env['async.connection']
|
12
|
+
request = request_from_env(env)
|
13
|
+
@connection = Connection.new(self, socket, :debug => @options[:debug])
|
14
|
+
@connection.dispatch(request) ? async_response : failure_response
|
15
|
+
end
|
16
|
+
|
17
|
+
# Forward send_data to server
|
18
|
+
def send_data(data)
|
19
|
+
if @connection
|
20
|
+
@connection.send data
|
21
|
+
else
|
22
|
+
raise WebSocketError, "WebSocket not opened"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Forward close_websocket to server
|
27
|
+
def close_websocket
|
28
|
+
if @connection
|
29
|
+
@connection.close_websocket
|
30
|
+
else
|
31
|
+
raise WebSocketError, "WebSocket not opened"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Parse Rack env to em-websocket-compatible format
|
38
|
+
# this probably should be moved to Base in future
|
39
|
+
def request_from_env(env)
|
40
|
+
request = {}
|
41
|
+
request['path'] = env['REQUEST_URI'].to_s
|
42
|
+
request['method'] = env['REQUEST_METHOD']
|
43
|
+
request['query'] = env['QUERY_STRING'].to_s
|
44
|
+
request['Body'] = env['rack.input'].read
|
45
|
+
|
46
|
+
env.each do |key, value|
|
47
|
+
if key.match(/HTTP_(.+)/)
|
48
|
+
request[$1.downcase.gsub('_','-')] ||= value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
request
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Rack
|
2
|
+
module WebSocket
|
3
|
+
module Handler
|
4
|
+
|
5
|
+
autoload :Base, "#{ROOT_PATH}/websocket/handler/base"
|
6
|
+
autoload :Stub, "#{ROOT_PATH}/websocket/handler/stub"
|
7
|
+
autoload :Thin, "#{ROOT_PATH}/websocket/handler/thin"
|
8
|
+
|
9
|
+
# Detect current server using software Rack string
|
10
|
+
def self.detect(env)
|
11
|
+
server_software = env['SERVER_SOFTWARE']
|
12
|
+
if server_software.match(/\Athin /i)
|
13
|
+
Thin
|
14
|
+
else
|
15
|
+
Stub
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'em-websocket'
|
3
|
+
|
4
|
+
module Rack
|
5
|
+
module WebSocket
|
6
|
+
ROOT_PATH = ::File.expand_path(::File.dirname(__FILE__))
|
7
|
+
|
8
|
+
autoload :Application, "#{ROOT_PATH}/websocket/application"
|
9
|
+
autoload :Extensions, "#{ROOT_PATH}/websocket/extensions"
|
10
|
+
autoload :Handler, "#{ROOT_PATH}/websocket/handler"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Rack::WebSocket::Extensions.apply!
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rspec'
|
3
|
+
|
4
|
+
require 'rack/websocket'
|
5
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.mock_with :mocha
|
9
|
+
end
|
10
|
+
|
11
|
+
class TestApp < Rack::WebSocket::Application
|
12
|
+
end
|
13
|
+
|
14
|
+
TEST_PORT = 8081
|
15
|
+
|
16
|
+
def new_server_connection
|
17
|
+
TCPSocket.new('localhost', TEST_PORT)
|
18
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
shared_examples_for 'all drafts' do
|
3
|
+
it "should accept incoming connection" do
|
4
|
+
conn = new_server_connection
|
5
|
+
conn.write(handshake_request)
|
6
|
+
timeout(1) { conn.read(handshake_response.length).should eql(handshake_response) }
|
7
|
+
end
|
8
|
+
it "should call 'on_open' on new connection" do
|
9
|
+
TestApp.any_instance.expects(:on_open)
|
10
|
+
conn = new_server_connection
|
11
|
+
conn.write(handshake_request)
|
12
|
+
end
|
13
|
+
it "should call 'on_open' on new connection with proper env" do
|
14
|
+
TestApp.any_instance.expects(:on_open).once.with { |env| env.class == Hash && !env.keys.empty? }
|
15
|
+
conn = new_server_connection
|
16
|
+
conn.write(handshake_request)
|
17
|
+
end
|
18
|
+
it "should call 'on_close' on connection close" do
|
19
|
+
TestApp.any_instance.expects(:on_close)
|
20
|
+
conn = new_server_connection
|
21
|
+
conn.write(handshake_request)
|
22
|
+
conn.close
|
23
|
+
end
|
24
|
+
it "should call 'on_close' on connection close with proper env" do
|
25
|
+
TestApp.any_instance.expects(:on_close).once.with { |env| env.class == Hash && !env.keys.empty? }
|
26
|
+
conn = new_server_connection
|
27
|
+
conn.write(handshake_request)
|
28
|
+
conn.close
|
29
|
+
end
|
30
|
+
it "should call 'on_message' on connection sending data" do
|
31
|
+
TestApp.any_instance.expects(:on_message)
|
32
|
+
conn = new_server_connection
|
33
|
+
conn.write(handshake_request)
|
34
|
+
timeout(1) { conn.read(handshake_response.length) }
|
35
|
+
conn.write(message)
|
36
|
+
end
|
37
|
+
it "should call 'on_message' on connection sending data with proper env and message" do
|
38
|
+
TestApp.any_instance.expects(:on_message).once.with { |env, message| env.class == Hash && !env.keys.empty? && message == 'Hello' }
|
39
|
+
conn = new_server_connection
|
40
|
+
conn.write(handshake_request)
|
41
|
+
timeout(1) { conn.read(handshake_response.length) }
|
42
|
+
conn.write(message)
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
shared_examples_for 'all handlers' do
|
2
|
+
it "should return flash policy file" do
|
3
|
+
conn = new_server_connection
|
4
|
+
conn.write(flash_policy_request)
|
5
|
+
conn.read(flash_policy_response.length).should eql(flash_policy_response)
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'for draft75' do
|
9
|
+
let(:handshake_request) { spec75_handshake_request }
|
10
|
+
let(:handshake_response) { spec75_handshake_response }
|
11
|
+
let(:message) { spec75_message }
|
12
|
+
|
13
|
+
it_should_behave_like 'all drafts'
|
14
|
+
end
|
15
|
+
|
16
|
+
# Also draft00
|
17
|
+
context 'for draft76' do
|
18
|
+
let(:handshake_request) { spec76_handshake_request }
|
19
|
+
let(:handshake_response) { spec76_handshake_response }
|
20
|
+
let(:message) { spec76_message }
|
21
|
+
|
22
|
+
it_should_behave_like 'all drafts'
|
23
|
+
end
|
24
|
+
|
25
|
+
# Drafts 01, 02 and 03 are pretty the same so one test for all
|
26
|
+
context 'for draft03' do
|
27
|
+
let(:handshake_request) { spec03_handshake_request }
|
28
|
+
let(:handshake_response) { spec03_handshake_response }
|
29
|
+
let(:message) { spec03_message }
|
30
|
+
|
31
|
+
it_should_behave_like 'all drafts'
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'for draft05' do
|
35
|
+
let(:handshake_request) { spec05_handshake_request }
|
36
|
+
let(:handshake_response) { spec05_handshake_response }
|
37
|
+
let(:message) { spec05_message }
|
38
|
+
|
39
|
+
it_should_behave_like 'all drafts'
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'for draft06' do
|
43
|
+
let(:handshake_request) { spec06_handshake_request }
|
44
|
+
let(:handshake_response) { spec06_handshake_response }
|
45
|
+
let(:message) { spec06_message }
|
46
|
+
|
47
|
+
it_should_behave_like 'all drafts'
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'for draft07' do
|
51
|
+
let(:handshake_request) { spec07_handshake_request }
|
52
|
+
let(:handshake_response) { spec07_handshake_response }
|
53
|
+
let(:message) { spec07_unmasked_message }
|
54
|
+
let(:masked_message) { spec07_masked_message }
|
55
|
+
|
56
|
+
it_should_behave_like 'all drafts'
|
57
|
+
it_should_behave_like 'draft with masked messages'
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'for draft08' do
|
61
|
+
let(:handshake_request) { spec08_handshake_request }
|
62
|
+
let(:handshake_response) { spec08_handshake_response }
|
63
|
+
let(:message) { spec08_unmasked_message }
|
64
|
+
let(:masked_message) { spec08_masked_message }
|
65
|
+
|
66
|
+
it_should_behave_like 'all drafts'
|
67
|
+
it_should_behave_like 'draft with masked messages'
|
68
|
+
end
|
69
|
+
|
70
|
+
# Drafts 09, 10, 11, 12 and 13 are pretty the same so one test for all
|
71
|
+
context 'for draft13' do
|
72
|
+
let(:handshake_request) { spec13_handshake_request }
|
73
|
+
let(:handshake_response) { spec13_handshake_response }
|
74
|
+
let(:message) { spec13_unmasked_message }
|
75
|
+
let(:masked_message) { spec13_masked_message }
|
76
|
+
|
77
|
+
it_should_behave_like 'all drafts'
|
78
|
+
it_should_behave_like 'draft with masked messages'
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
shared_examples_for 'draft with masked messages' do
|
2
|
+
it "should call 'on_message' on connection sending masked data with proper env and message" do
|
3
|
+
TestApp.any_instance.expects(:on_message).once.with { |env, message| env.class == Hash && !env.keys.empty? && message == 'Hello' }
|
4
|
+
conn = new_server_connection
|
5
|
+
conn.write(handshake_request)
|
6
|
+
timeout(1) { conn.read(handshake_response.length) }
|
7
|
+
conn.write(masked_message)
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
def flash_policy_request
|
2
|
+
"<policy-file-request/>\000"
|
3
|
+
end
|
4
|
+
|
5
|
+
def flash_policy_response
|
6
|
+
'<?xml version="1.0"?><cross-domain-policy><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>'
|
7
|
+
end
|
8
|
+
|
9
|
+
def spec75_handshake_request
|
10
|
+
<<-EOF
|
11
|
+
GET /demo HTTP/1.1\r
|
12
|
+
Upgrade: WebSocket\r
|
13
|
+
Connection: Upgrade\r
|
14
|
+
Host: localhost:#{TEST_PORT}\r
|
15
|
+
Origin: http://localhost:#{TEST_PORT}\r
|
16
|
+
\r
|
17
|
+
EOF
|
18
|
+
end
|
19
|
+
|
20
|
+
def spec75_handshake_response
|
21
|
+
<<-EOF
|
22
|
+
HTTP/1.1 101 Web Socket Protocol Handshake\r
|
23
|
+
Upgrade: WebSocket\r
|
24
|
+
Connection: Upgrade\r
|
25
|
+
WebSocket-Origin: http://localhost:#{TEST_PORT}\r
|
26
|
+
WebSocket-Location: ws://localhost:#{TEST_PORT}/demo\r
|
27
|
+
\r
|
28
|
+
EOF
|
29
|
+
end
|
30
|
+
|
31
|
+
def spec75_message
|
32
|
+
"\x00Hello\xff"
|
33
|
+
end
|
34
|
+
|
35
|
+
def spec76_handshake_request
|
36
|
+
request = <<-EOF
|
37
|
+
GET /demo HTTP/1.1\r
|
38
|
+
Host: localhost:#{TEST_PORT}\r
|
39
|
+
Connection: Upgrade\r
|
40
|
+
Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r
|
41
|
+
Sec-WebSocket-Protocol: sample\r
|
42
|
+
Upgrade: WebSocket\r
|
43
|
+
Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r
|
44
|
+
Origin: http://localhost:#{TEST_PORT}\r
|
45
|
+
\r
|
46
|
+
^n:ds[4U
|
47
|
+
EOF
|
48
|
+
request.rstrip
|
49
|
+
end
|
50
|
+
|
51
|
+
def spec76_handshake_response
|
52
|
+
response = <<-EOF
|
53
|
+
HTTP/1.1 101 WebSocket Protocol Handshake\r
|
54
|
+
Upgrade: WebSocket\r
|
55
|
+
Connection: Upgrade\r
|
56
|
+
Sec-WebSocket-Location: ws://localhost:#{TEST_PORT}/demo\r
|
57
|
+
Sec-WebSocket-Origin: http://localhost:#{TEST_PORT}\r
|
58
|
+
Sec-WebSocket-Protocol: sample\r
|
59
|
+
\r
|
60
|
+
8jKS'y:G*Co,Wxa-
|
61
|
+
EOF
|
62
|
+
response.rstrip
|
63
|
+
end
|
64
|
+
|
65
|
+
def spec76_message
|
66
|
+
"\x00Hello\xff"
|
67
|
+
end
|
68
|
+
|
69
|
+
def spec03_handshake_request
|
70
|
+
request = <<-EOF
|
71
|
+
GET /demo HTTP/1.1\r
|
72
|
+
Host: localhost:#{TEST_PORT}\r
|
73
|
+
Connection: Upgrade\r
|
74
|
+
Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r
|
75
|
+
Sec-WebSocket-Protocol: sample\r
|
76
|
+
Upgrade: WebSocket\r
|
77
|
+
Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r
|
78
|
+
Origin: http://localhost:#{TEST_PORT}\r
|
79
|
+
Sec-WebSocket-Draft: 3\r
|
80
|
+
\r
|
81
|
+
^n:ds[4U
|
82
|
+
EOF
|
83
|
+
request.rstrip
|
84
|
+
end
|
85
|
+
|
86
|
+
def spec03_handshake_response
|
87
|
+
response = <<-EOF
|
88
|
+
HTTP/1.1 101 WebSocket Protocol Handshake\r
|
89
|
+
Upgrade: WebSocket\r
|
90
|
+
Connection: Upgrade\r
|
91
|
+
Sec-WebSocket-Location: ws://localhost:#{TEST_PORT}/demo\r
|
92
|
+
Sec-WebSocket-Origin: http://localhost:#{TEST_PORT}\r
|
93
|
+
Sec-WebSocket-Protocol: sample\r
|
94
|
+
\r
|
95
|
+
8jKS'y:G*Co,Wxa-
|
96
|
+
EOF
|
97
|
+
response.rstrip
|
98
|
+
end
|
99
|
+
|
100
|
+
def spec03_message
|
101
|
+
"\x04\x05Hello"
|
102
|
+
end
|
103
|
+
|
104
|
+
def spec05_handshake_request
|
105
|
+
<<-EOF
|
106
|
+
GET /chat HTTP/1.1\r
|
107
|
+
Host: localhost:#{TEST_PORT}\r
|
108
|
+
Upgrade: websocket\r
|
109
|
+
Connection: Upgrade\r
|
110
|
+
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r
|
111
|
+
Sec-WebSocket-Origin: http://localhost:#{TEST_PORT}\r
|
112
|
+
Sec-WebSocket-Protocol: chat, superchat\r
|
113
|
+
Sec-WebSocket-Version: 5\r
|
114
|
+
\r
|
115
|
+
EOF
|
116
|
+
end
|
117
|
+
|
118
|
+
def spec05_handshake_response
|
119
|
+
<<-EOF
|
120
|
+
HTTP/1.1 101 Switching Protocols\r
|
121
|
+
Upgrade: websocket\r
|
122
|
+
Connection: Upgrade\r
|
123
|
+
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r
|
124
|
+
EOF
|
125
|
+
end
|
126
|
+
|
127
|
+
def spec05_message
|
128
|
+
"\x00\x00\x01\x00\x84\x05Ielln"
|
129
|
+
end
|
130
|
+
|
131
|
+
def spec06_handshake_request
|
132
|
+
<<-EOF
|
133
|
+
GET /chat HTTP/1.1\r
|
134
|
+
Host: localhost:#{TEST_PORT}\r
|
135
|
+
Upgrade: websocket\r
|
136
|
+
Connection: Upgrade\r
|
137
|
+
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r
|
138
|
+
Sec-WebSocket-Origin: http://localhost:#{TEST_PORT}\r
|
139
|
+
Sec-WebSocket-Protocol: chat, superchat\r
|
140
|
+
Sec-WebSocket-Version: 6\r
|
141
|
+
\r
|
142
|
+
EOF
|
143
|
+
end
|
144
|
+
|
145
|
+
def spec06_handshake_response
|
146
|
+
<<-EOF
|
147
|
+
HTTP/1.1 101 Switching Protocols\r
|
148
|
+
Upgrade: websocket\r
|
149
|
+
Connection: Upgrade\r
|
150
|
+
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r
|
151
|
+
EOF
|
152
|
+
end
|
153
|
+
|
154
|
+
def spec06_message
|
155
|
+
"\x00\x00\x01\x00\x84\x05Ielln"
|
156
|
+
end
|
157
|
+
|
158
|
+
def spec07_handshake_request
|
159
|
+
<<-EOF
|
160
|
+
GET /chat HTTP/1.1\r
|
161
|
+
Host: localhost:#{TEST_PORT}\r
|
162
|
+
Upgrade: websocket\r
|
163
|
+
Connection: Upgrade\r
|
164
|
+
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r
|
165
|
+
Sec-WebSocket-Origin: http://localhost:#{TEST_PORT}\r
|
166
|
+
Sec-WebSocket-Protocol: chat, superchat\r
|
167
|
+
Sec-WebSocket-Version: 7\r
|
168
|
+
\r
|
169
|
+
EOF
|
170
|
+
end
|
171
|
+
|
172
|
+
def spec07_handshake_response
|
173
|
+
<<-EOF
|
174
|
+
HTTP/1.1 101 Switching Protocols\r
|
175
|
+
Upgrade: websocket\r
|
176
|
+
Connection: Upgrade\r
|
177
|
+
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r
|
178
|
+
EOF
|
179
|
+
end
|
180
|
+
|
181
|
+
def spec07_unmasked_message
|
182
|
+
"\x81\x05\x48\x65\x6c\x6c\x6f"
|
183
|
+
end
|
184
|
+
|
185
|
+
def spec07_masked_message
|
186
|
+
"\x81\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58"
|
187
|
+
end
|
188
|
+
|
189
|
+
def spec08_handshake_request
|
190
|
+
<<-EOF
|
191
|
+
GET /chat HTTP/1.1\r
|
192
|
+
Host: localhost:#{TEST_PORT}\r
|
193
|
+
Upgrade: websocket\r
|
194
|
+
Connection: Upgrade\r
|
195
|
+
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r
|
196
|
+
Sec-WebSocket-Origin: http://localhost:#{TEST_PORT}\r
|
197
|
+
Sec-WebSocket-Protocol: chat, superchat\r
|
198
|
+
Sec-WebSocket-Version: 8\r
|
199
|
+
\r
|
200
|
+
EOF
|
201
|
+
end
|
202
|
+
|
203
|
+
def spec08_handshake_response
|
204
|
+
<<-EOF
|
205
|
+
HTTP/1.1 101 Switching Protocols\r
|
206
|
+
Upgrade: websocket\r
|
207
|
+
Connection: Upgrade\r
|
208
|
+
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r
|
209
|
+
EOF
|
210
|
+
end
|
211
|
+
|
212
|
+
def spec08_unmasked_message
|
213
|
+
"\x81\x05\x48\x65\x6c\x6c\x6f"
|
214
|
+
end
|
215
|
+
|
216
|
+
def spec08_masked_message
|
217
|
+
"\x81\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58"
|
218
|
+
end
|
219
|
+
|
220
|
+
def spec13_handshake_request
|
221
|
+
<<-EOF
|
222
|
+
GET /chat HTTP/1.1\r
|
223
|
+
Host: localhost:#{TEST_PORT}\r
|
224
|
+
Upgrade: websocket\r
|
225
|
+
Connection: Upgrade\r
|
226
|
+
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r
|
227
|
+
Origin: http://localhost:#{TEST_PORT}\r
|
228
|
+
Sec-WebSocket-Protocol: chat, superchat\r
|
229
|
+
Sec-WebSocket-Version: 13\r
|
230
|
+
\r
|
231
|
+
EOF
|
232
|
+
end
|
233
|
+
|
234
|
+
def spec13_handshake_response
|
235
|
+
<<-EOF
|
236
|
+
HTTP/1.1 101 Switching Protocols\r
|
237
|
+
Upgrade: websocket\r
|
238
|
+
Connection: Upgrade\r
|
239
|
+
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r
|
240
|
+
EOF
|
241
|
+
end
|
242
|
+
|
243
|
+
def spec13_unmasked_message
|
244
|
+
"\x81\x05\x48\x65\x6c\x6c\x6f"
|
245
|
+
end
|
246
|
+
|
247
|
+
def spec13_masked_message
|
248
|
+
"\x81\x85\x37\xfa\x21\x3d\x7f\x9f\x4d\x51\x58"
|
249
|
+
end
|
data/spec/thin_spec.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'thin'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe 'Thin handler' do
|
5
|
+
let(:app) { TestApp.new }
|
6
|
+
|
7
|
+
before(:all) { silent_thin }
|
8
|
+
before { start_thin_server(app) }
|
9
|
+
after { stop_thin_server }
|
10
|
+
|
11
|
+
it "should include extensions" do
|
12
|
+
::Thin::Connection.include?(::Rack::WebSocket::Extensions::Common).should be_true
|
13
|
+
::Thin::Connection.include?(::Rack::WebSocket::Extensions::Thin::Connection).should be_true
|
14
|
+
end
|
15
|
+
|
16
|
+
it_should_behave_like 'all handlers'
|
17
|
+
end
|
18
|
+
|
19
|
+
def start_thin_server(app, options = {})
|
20
|
+
@server = Thin::Server.new('0.0.0.0', TEST_PORT, options, app)
|
21
|
+
@server.ssl = options[:ssl]
|
22
|
+
# @server.threaded = options[:threaded]
|
23
|
+
# @server.timeout = 3
|
24
|
+
|
25
|
+
@thread = Thread.new { @server.start }
|
26
|
+
sleep 1 until @server.running?
|
27
|
+
end
|
28
|
+
|
29
|
+
def stop_thin_server
|
30
|
+
sleep 0.1
|
31
|
+
@server.stop!
|
32
|
+
sleep 0.1
|
33
|
+
@thread.kill
|
34
|
+
sleep 0.1
|
35
|
+
raise "Reactor still running, wtf?" if EventMachine.reactor_running?
|
36
|
+
end
|
37
|
+
|
38
|
+
def silent_thin
|
39
|
+
::Thin::Logging.silent = true
|
40
|
+
if EM::VERSION < "1.0.0"
|
41
|
+
begin
|
42
|
+
old_verbose, $VERBOSE = $VERBOSE, nil
|
43
|
+
::Thin::Server.const_set 'DEFAULT_TIMEOUT', 0
|
44
|
+
ensure
|
45
|
+
$VERBOSE = old_verbose
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "rack/websocket/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "websocket-rack-noodles"
|
7
|
+
s.version = Rack::WebSocket::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Bernard Potocki"]
|
10
|
+
s.email = ["bernard.potocki@imanel.org"]
|
11
|
+
s.homepage = "http://github.com/DamirSvrtan/websocket-rack"
|
12
|
+
s.summary = %q{Rack-based WebSocket server for Noodles Web Framework}
|
13
|
+
s.description = %q{Rack-based WebSocket server for Noodles Web Framework. An fork from http://github.com/imanel/websocket-rack}
|
14
|
+
|
15
|
+
s.add_dependency 'rack'
|
16
|
+
s.add_dependency 'em-websocket', '~> 0.3.8'
|
17
|
+
s.add_dependency 'eventmachine', '~> 1.0.0'
|
18
|
+
s.add_dependency 'thin' # Temporary until we support more servers
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
end
|