faye 0.6.8 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of faye might be problematic. Click here for more details.
- data/History.txt +10 -3
- data/README.rdoc +1 -2
- data/lib/faye-browser-min.js +1 -1
- data/lib/faye.rb +89 -32
- data/lib/faye/adapters/rack_adapter.rb +20 -26
- data/lib/faye/engines/base.rb +5 -0
- data/lib/faye/engines/memory.rb +9 -3
- data/lib/faye/engines/redis.rb +26 -11
- data/lib/faye/mixins/publisher.rb +4 -8
- data/lib/faye/protocol/channel.rb +8 -8
- data/lib/faye/protocol/client.rb +45 -4
- data/lib/faye/protocol/publication.rb +5 -0
- data/lib/faye/protocol/server.rb +10 -19
- data/lib/faye/thin_extensions.rb +1 -1
- data/lib/faye/transport/http.rb +17 -8
- data/lib/faye/transport/local.rb +6 -3
- data/lib/faye/transport/transport.rb +23 -9
- data/lib/faye/transport/web_socket.rb +102 -0
- data/lib/faye/util/web_socket.rb +34 -80
- data/lib/faye/util/web_socket/api.rb +103 -0
- data/lib/faye/util/web_socket/client.rb +82 -0
- data/lib/faye/util/web_socket/draft75_parser.rb +3 -5
- data/lib/faye/util/web_socket/draft76_parser.rb +5 -7
- data/lib/faye/util/web_socket/protocol8_parser.rb +111 -46
- data/spec/javascript/client_spec.js +99 -7
- data/spec/javascript/engine_spec.js +116 -3
- data/spec/javascript/node_adapter_spec.js +2 -4
- data/spec/javascript/server/handshake_spec.js +0 -12
- data/spec/javascript/server/integration_spec.js +74 -29
- data/spec/javascript/server_spec.js +0 -11
- data/spec/javascript/web_socket/client_spec.js +121 -0
- data/spec/javascript/web_socket/protocol8parser_spec.js +26 -3
- data/spec/node.js +2 -0
- data/spec/redis.conf +10 -280
- data/spec/ruby/client_spec.rb +101 -8
- data/spec/ruby/engine_spec.rb +106 -0
- data/spec/ruby/server/handshake_spec.rb +0 -12
- data/spec/ruby/server/integration_spec.rb +56 -18
- data/spec/ruby/server_spec.rb +1 -12
- data/spec/ruby/transport_spec.rb +14 -8
- data/spec/ruby/web_socket/client_spec.rb +126 -0
- data/spec/ruby/web_socket/protocol8_parser_spec.rb +28 -3
- metadata +96 -150
data/lib/faye/util/web_socket.rb
CHANGED
@@ -1,135 +1,89 @@
|
|
1
1
|
module Faye
|
2
2
|
class WebSocket
|
3
3
|
|
4
|
-
|
5
|
-
autoload :Draft76Parser, File.expand_path('..', __FILE__) + '/web_socket/draft76_parser'
|
6
|
-
autoload :Protocol8Parser, File.expand_path('..', __FILE__) + '/web_socket/protocol8_parser'
|
4
|
+
root = File.expand_path('..', __FILE__) + '/web_socket'
|
7
5
|
|
8
|
-
|
6
|
+
autoload :API, root + '/api'
|
7
|
+
autoload :Client, root + '/client'
|
8
|
+
autoload :Draft75Parser, root + '/draft75_parser'
|
9
|
+
autoload :Draft76Parser, root + '/draft76_parser'
|
10
|
+
autoload :Protocol8Parser, root + '/protocol8_parser'
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
CLOSING = 2
|
13
|
-
CLOSED = 3
|
14
|
-
|
15
|
-
attr_reader :request, :url, :ready_state
|
16
|
-
attr_accessor :onopen, :onmessage, :onerror, :onclose
|
12
|
+
attr_reader :env
|
13
|
+
include API
|
17
14
|
|
18
15
|
extend Forwardable
|
19
16
|
def_delegators :@parser, :version
|
20
17
|
|
21
|
-
def self.parser(
|
22
|
-
if
|
18
|
+
def self.parser(env)
|
19
|
+
if env['HTTP_SEC_WEBSOCKET_VERSION']
|
23
20
|
Protocol8Parser
|
24
|
-
elsif
|
21
|
+
elsif env['HTTP_SEC_WEBSOCKET_KEY1']
|
25
22
|
Draft76Parser
|
26
23
|
else
|
27
24
|
Draft75Parser
|
28
25
|
end
|
29
26
|
end
|
30
27
|
|
31
|
-
def initialize(
|
32
|
-
@
|
33
|
-
@callback = @
|
34
|
-
@stream = Stream.new
|
28
|
+
def initialize(env)
|
29
|
+
@env = env
|
30
|
+
@callback = @env['async.callback']
|
31
|
+
@stream = Stream.new(self, @env['em.connection'])
|
35
32
|
@callback.call [200, RackAdapter::TYPE_JSON, @stream]
|
36
33
|
|
37
34
|
@url = determine_url
|
38
35
|
@ready_state = CONNECTING
|
39
36
|
@buffered_amount = 0
|
40
37
|
|
41
|
-
@parser = WebSocket.parser(@
|
38
|
+
@parser = WebSocket.parser(@env).new(self)
|
42
39
|
@stream.write(@parser.handshake_response)
|
43
40
|
|
44
41
|
@ready_state = OPEN
|
45
42
|
|
46
|
-
event = Event.new
|
43
|
+
event = Event.new('open')
|
47
44
|
event.init_event('open', false, false)
|
48
45
|
dispatch_event(event)
|
49
46
|
|
50
|
-
@
|
51
|
-
end
|
52
|
-
|
53
|
-
def receive(data)
|
54
|
-
event = Event.new
|
55
|
-
event.init_event('message', false, false)
|
56
|
-
event.data = Faye.encode(data)
|
57
|
-
dispatch_event(event)
|
58
|
-
end
|
59
|
-
|
60
|
-
def send(data, type = nil, error_type = nil)
|
61
|
-
frame = @parser.frame(Faye.encode(data), type, error_type)
|
62
|
-
@stream.write(frame) if frame
|
63
|
-
end
|
64
|
-
|
65
|
-
def close(*args)
|
66
|
-
end
|
67
|
-
|
68
|
-
def add_event_listener(type, listener, use_capture)
|
69
|
-
add_subscriber(type, listener)
|
70
|
-
end
|
71
|
-
|
72
|
-
def remove_event_listener(type, listener, use_capture)
|
73
|
-
remove_subscriber(type, listener)
|
74
|
-
end
|
75
|
-
|
76
|
-
def dispatch_event(event)
|
77
|
-
event.target = event.current_target = self
|
78
|
-
event.event_phase = Event::AT_TARGET
|
79
|
-
|
80
|
-
publish_event(event.type, event)
|
81
|
-
callback = __send__("on#{ event.type }")
|
82
|
-
callback.call(event) if callback
|
47
|
+
@env[Thin::Request::WEBSOCKET_RECEIVE_CALLBACK] = @parser.method(:parse)
|
83
48
|
end
|
84
49
|
|
85
50
|
private
|
86
51
|
|
87
52
|
def determine_url
|
88
|
-
|
89
|
-
|
90
|
-
env['HTTP_X_FORWARDED_PROTO'] == 'https'
|
53
|
+
secure = if @env.has_key?('HTTP_X_FORWARDED_PROTO')
|
54
|
+
@env['HTTP_X_FORWARDED_PROTO'] == 'https'
|
91
55
|
else
|
92
|
-
env['HTTP_ORIGIN'] =~ /^https:/i
|
56
|
+
@env['HTTP_ORIGIN'] =~ /^https:/i
|
93
57
|
end
|
94
58
|
|
95
59
|
scheme = secure ? 'wss:' : 'ws:'
|
96
|
-
"#{ scheme }//#{ env['HTTP_HOST'] }#{ env['REQUEST_URI'] }"
|
60
|
+
"#{ scheme }//#{ @env['HTTP_HOST'] }#{ @env['REQUEST_URI'] }"
|
97
61
|
end
|
98
62
|
end
|
99
63
|
|
100
64
|
class WebSocket::Stream
|
101
65
|
include EventMachine::Deferrable
|
102
66
|
|
103
|
-
|
104
|
-
|
105
|
-
end
|
67
|
+
extend Forwardable
|
68
|
+
def_delegators :@connection, :close_connection, :close_connection_after_writing
|
106
69
|
|
107
|
-
def
|
108
|
-
|
109
|
-
@
|
70
|
+
def initialize(web_socket, connection)
|
71
|
+
@web_socket = web_socket
|
72
|
+
@connection = connection
|
110
73
|
end
|
111
|
-
end
|
112
|
-
|
113
|
-
class WebSocket::Event
|
114
|
-
attr_reader :type, :bubbles, :cancelable
|
115
|
-
attr_accessor :target, :current_target, :event_phase, :data
|
116
|
-
|
117
|
-
CAPTURING_PHASE = 1
|
118
|
-
AT_TARGET = 2
|
119
|
-
BUBBLING_PHASE = 3
|
120
74
|
|
121
|
-
def
|
122
|
-
@
|
123
|
-
@bubbles = can_bubble
|
124
|
-
@cancelable = cancelable
|
75
|
+
def each(&callback)
|
76
|
+
@data_callback = callback
|
125
77
|
end
|
126
78
|
|
127
|
-
def
|
79
|
+
def fail
|
80
|
+
@web_socket.close(1006, '', false)
|
128
81
|
end
|
129
82
|
|
130
|
-
def
|
83
|
+
def write(data)
|
84
|
+
return unless @data_callback
|
85
|
+
@data_callback.call(data)
|
131
86
|
end
|
132
87
|
end
|
133
88
|
|
134
89
|
end
|
135
|
-
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Faye
|
2
|
+
class WebSocket
|
3
|
+
|
4
|
+
CONNECTING = 0
|
5
|
+
OPEN = 1
|
6
|
+
CLOSING = 2
|
7
|
+
CLOSED = 3
|
8
|
+
|
9
|
+
module API
|
10
|
+
attr_reader :url, :ready_state, :buffered_amount
|
11
|
+
attr_accessor :onopen, :onmessage, :onerror, :onclose
|
12
|
+
|
13
|
+
include Publisher
|
14
|
+
|
15
|
+
def receive(data)
|
16
|
+
return false unless ready_state == OPEN
|
17
|
+
event = Event.new('message')
|
18
|
+
event.init_event('message', false, false)
|
19
|
+
event.data = data
|
20
|
+
dispatch_event(event)
|
21
|
+
end
|
22
|
+
|
23
|
+
def send(data, type = nil, error_type = nil)
|
24
|
+
return false if ready_state == CLOSED
|
25
|
+
data = Faye.encode(data) if String === data
|
26
|
+
frame = @parser.frame(data, type, error_type)
|
27
|
+
@stream.write(frame) if frame
|
28
|
+
end
|
29
|
+
|
30
|
+
def close(code = nil, reason = nil, ack = true)
|
31
|
+
return if [CLOSING, CLOSED].include?(ready_state)
|
32
|
+
|
33
|
+
@ready_state = CLOSING
|
34
|
+
|
35
|
+
close = lambda do
|
36
|
+
@ready_state = CLOSED
|
37
|
+
@stream.close_connection_after_writing
|
38
|
+
event = Event.new('close', :code => code || 1000, :reason => reason || '')
|
39
|
+
event.init_event('close', false, false)
|
40
|
+
dispatch_event(event)
|
41
|
+
end
|
42
|
+
|
43
|
+
if ack
|
44
|
+
if @parser.respond_to?(:close)
|
45
|
+
@parser.close(code, reason, &close)
|
46
|
+
else
|
47
|
+
close.call
|
48
|
+
end
|
49
|
+
else
|
50
|
+
@parser.close(code, reason) if @parser.respond_to?(:close)
|
51
|
+
close.call
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_event_listener(type, listener, use_capture)
|
56
|
+
bind(type, listener)
|
57
|
+
end
|
58
|
+
|
59
|
+
def remove_event_listener(type, listener, use_capture)
|
60
|
+
unbind(type, listener)
|
61
|
+
end
|
62
|
+
|
63
|
+
def dispatch_event(event)
|
64
|
+
event.target = event.current_target = self
|
65
|
+
event.event_phase = Event::AT_TARGET
|
66
|
+
|
67
|
+
trigger(event.type, event)
|
68
|
+
callback = __send__("on#{ event.type }")
|
69
|
+
callback.call(event) if callback
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class Event
|
74
|
+
attr_reader :type, :bubbles, :cancelable
|
75
|
+
attr_accessor :target, :current_target, :event_phase, :data
|
76
|
+
|
77
|
+
CAPTURING_PHASE = 1
|
78
|
+
AT_TARGET = 2
|
79
|
+
BUBBLING_PHASE = 3
|
80
|
+
|
81
|
+
def initialize(event_type, options = {})
|
82
|
+
@type = event_type
|
83
|
+
metaclass = (class << self ; self ; end)
|
84
|
+
options.each do |key, value|
|
85
|
+
metaclass.__send__(:define_method, key) { value }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def init_event(event_type, can_bubble, cancelable)
|
90
|
+
@type = event_type
|
91
|
+
@bubbles = can_bubble
|
92
|
+
@cancelable = cancelable
|
93
|
+
end
|
94
|
+
|
95
|
+
def stop_propagation
|
96
|
+
end
|
97
|
+
|
98
|
+
def prevent_default
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Faye
|
2
|
+
class WebSocket
|
3
|
+
|
4
|
+
class Client
|
5
|
+
include API
|
6
|
+
attr_reader :uri
|
7
|
+
|
8
|
+
def initialize(url)
|
9
|
+
@parser = Protocol8Parser.new(self, :masking => true)
|
10
|
+
@url = url
|
11
|
+
@uri = URI.parse(url)
|
12
|
+
|
13
|
+
@ready_state = CONNECTING
|
14
|
+
@buffered_amount = 0
|
15
|
+
|
16
|
+
port = @uri.port || (@uri.scheme == 'wss' ? 443 : 80)
|
17
|
+
|
18
|
+
EventMachine.connect(@uri.host, port, Connection) do |conn|
|
19
|
+
@stream = conn
|
20
|
+
conn.parent = self
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def on_connect
|
27
|
+
@stream.start_tls if @uri.scheme == 'wss'
|
28
|
+
@handshake = @parser.create_handshake
|
29
|
+
@message = []
|
30
|
+
@stream.write(@handshake.request_data)
|
31
|
+
end
|
32
|
+
|
33
|
+
def receive_data(data)
|
34
|
+
data = Faye.encode(data)
|
35
|
+
|
36
|
+
case @ready_state
|
37
|
+
when CONNECTING then
|
38
|
+
@message += @handshake.parse(data)
|
39
|
+
return unless @handshake.complete?
|
40
|
+
|
41
|
+
if @handshake.valid?
|
42
|
+
@ready_state = OPEN
|
43
|
+
event = Event.new('open')
|
44
|
+
event.init_event('open', false, false)
|
45
|
+
dispatch_event(event)
|
46
|
+
|
47
|
+
receive_data(@message)
|
48
|
+
else
|
49
|
+
@ready_state = CLOSED
|
50
|
+
event = Event.new('error')
|
51
|
+
event.init_event('error', false, false)
|
52
|
+
dispatch_event(event)
|
53
|
+
end
|
54
|
+
|
55
|
+
when OPEN, CLOSING then
|
56
|
+
@parser.parse(data)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module Connection
|
61
|
+
attr_accessor :parent
|
62
|
+
|
63
|
+
def connection_completed
|
64
|
+
parent.__send__(:on_connect)
|
65
|
+
end
|
66
|
+
|
67
|
+
def receive_data(data)
|
68
|
+
parent.__send__(:receive_data, data)
|
69
|
+
end
|
70
|
+
|
71
|
+
def unbind
|
72
|
+
parent.close(1006, '', false)
|
73
|
+
end
|
74
|
+
|
75
|
+
def write(data)
|
76
|
+
send_data(data)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -13,12 +13,10 @@ module Faye
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def handshake_response
|
16
|
-
request = @socket.request
|
17
|
-
|
18
16
|
upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
|
19
17
|
upgrade << "Upgrade: WebSocket\r\n"
|
20
18
|
upgrade << "Connection: Upgrade\r\n"
|
21
|
-
upgrade << "WebSocket-Origin: #{
|
19
|
+
upgrade << "WebSocket-Origin: #{@socket.env['HTTP_ORIGIN']}\r\n"
|
22
20
|
upgrade << "WebSocket-Location: #{@socket.url}\r\n"
|
23
21
|
upgrade << "\r\n"
|
24
22
|
upgrade
|
@@ -40,12 +38,12 @@ module Faye
|
|
40
38
|
@buffering = true
|
41
39
|
|
42
40
|
when 0xFF then
|
43
|
-
@socket.receive(Faye.encode(@buffer
|
41
|
+
@socket.receive(Faye.encode(@buffer))
|
44
42
|
@buffer = []
|
45
43
|
@buffering = false
|
46
44
|
|
47
45
|
else
|
48
|
-
@buffer.push(data
|
46
|
+
@buffer.push(data) if @buffering
|
49
47
|
end
|
50
48
|
end
|
51
49
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'digest/md5'
|
2
|
-
|
3
1
|
module Faye
|
4
2
|
class WebSocket
|
5
3
|
|
@@ -9,22 +7,22 @@ module Faye
|
|
9
7
|
end
|
10
8
|
|
11
9
|
def handshake_response
|
12
|
-
|
10
|
+
env = @socket.env
|
13
11
|
|
14
|
-
key1 =
|
12
|
+
key1 = env['HTTP_SEC_WEBSOCKET_KEY1']
|
15
13
|
value1 = number_from_key(key1) / spaces_in_key(key1)
|
16
14
|
|
17
|
-
key2 =
|
15
|
+
key2 = env['HTTP_SEC_WEBSOCKET_KEY2']
|
18
16
|
value2 = number_from_key(key2) / spaces_in_key(key2)
|
19
17
|
|
20
18
|
hash = Digest::MD5.digest(big_endian(value1) +
|
21
19
|
big_endian(value2) +
|
22
|
-
|
20
|
+
env['rack.input'].read)
|
23
21
|
|
24
22
|
upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
|
25
23
|
upgrade << "Upgrade: WebSocket\r\n"
|
26
24
|
upgrade << "Connection: Upgrade\r\n"
|
27
|
-
upgrade << "Sec-WebSocket-Origin: #{
|
25
|
+
upgrade << "Sec-WebSocket-Origin: #{env['HTTP_ORIGIN']}\r\n"
|
28
26
|
upgrade << "Sec-WebSocket-Location: #{@socket.url}\r\n"
|
29
27
|
upgrade << "\r\n"
|
30
28
|
upgrade << hash
|
@@ -1,7 +1,3 @@
|
|
1
|
-
require 'base64'
|
2
|
-
require 'digest/sha1'
|
3
|
-
require 'net/http'
|
4
|
-
|
5
1
|
module Faye
|
6
2
|
class WebSocket
|
7
3
|
|
@@ -24,15 +20,22 @@ module Faye
|
|
24
20
|
:pong => 10
|
25
21
|
}
|
26
22
|
|
23
|
+
FRAGMENTED_OPCODES = OPCODES.values_at(:continuation, :text, :binary)
|
24
|
+
OPENING_OPCODES = OPCODES.values_at(:text, :binary)
|
25
|
+
|
27
26
|
ERRORS = {
|
28
|
-
:normal_closure
|
29
|
-
:going_away
|
30
|
-
:protocol_error
|
31
|
-
:unacceptable
|
32
|
-
:
|
33
|
-
:
|
27
|
+
:normal_closure => 1000,
|
28
|
+
:going_away => 1001,
|
29
|
+
:protocol_error => 1002,
|
30
|
+
:unacceptable => 1003,
|
31
|
+
:encoding_error => 1007,
|
32
|
+
:policy_violation => 1008,
|
33
|
+
:too_large => 1009,
|
34
|
+
:extension_error => 1010
|
34
35
|
}
|
35
36
|
|
37
|
+
ERROR_CODES = ERRORS.values
|
38
|
+
|
36
39
|
class Handshake
|
37
40
|
def initialize(uri)
|
38
41
|
@uri = uri
|
@@ -44,7 +47,7 @@ module Faye
|
|
44
47
|
def request_data
|
45
48
|
hostname = @uri.host + (@uri.port ? ":#{@uri.port}" : '')
|
46
49
|
|
47
|
-
handshake = "GET #{@uri.path} HTTP/1.1\r\n"
|
50
|
+
handshake = "GET #{@uri.path}#{@uri.query ? '?' : ''}#{@uri.query} HTTP/1.1\r\n"
|
48
51
|
handshake << "Host: #{hostname}\r\n"
|
49
52
|
handshake << "Upgrade: websocket\r\n"
|
50
53
|
handshake << "Connection: Upgrade\r\n"
|
@@ -56,15 +59,25 @@ module Faye
|
|
56
59
|
end
|
57
60
|
|
58
61
|
def parse(data)
|
59
|
-
|
62
|
+
message = []
|
63
|
+
complete = false
|
64
|
+
data.each_byte do |byte|
|
65
|
+
if complete
|
66
|
+
message << byte
|
67
|
+
else
|
68
|
+
@buffer << byte
|
69
|
+
complete ||= complete?
|
70
|
+
end
|
71
|
+
end
|
72
|
+
message
|
60
73
|
end
|
61
74
|
|
62
75
|
def complete?
|
63
|
-
@buffer[-4..-1] == [
|
76
|
+
@buffer[-4..-1] == [0x0D, 0x0A, 0x0D, 0x0A]
|
64
77
|
end
|
65
78
|
|
66
79
|
def valid?
|
67
|
-
data = Faye.encode(@buffer
|
80
|
+
data = Faye.encode(@buffer)
|
68
81
|
response = Net::HTTPResponse.read_new(Net::BufferedIO.new(StringIO.new(data)))
|
69
82
|
return false unless response.code.to_i == 101
|
70
83
|
|
@@ -76,10 +89,11 @@ module Faye
|
|
76
89
|
end
|
77
90
|
end
|
78
91
|
|
79
|
-
def initialize(web_socket)
|
92
|
+
def initialize(web_socket, options = {})
|
80
93
|
reset
|
81
|
-
@socket
|
82
|
-
@stage
|
94
|
+
@socket = web_socket
|
95
|
+
@stage = 0
|
96
|
+
@masking = options[:masking]
|
83
97
|
end
|
84
98
|
|
85
99
|
def version
|
@@ -87,7 +101,7 @@ module Faye
|
|
87
101
|
end
|
88
102
|
|
89
103
|
def handshake_response
|
90
|
-
sec_key = @socket.
|
104
|
+
sec_key = @socket.env['HTTP_SEC_WEBSOCKET_KEY']
|
91
105
|
return '' unless String === sec_key
|
92
106
|
|
93
107
|
accept = Base64.encode64(Digest::SHA1.digest(sec_key + GUID)).strip
|
@@ -113,59 +127,85 @@ module Faye
|
|
113
127
|
when 3 then parse_mask(byte)
|
114
128
|
when 4 then parse_payload(byte)
|
115
129
|
end
|
130
|
+
emit_frame if @stage == 4 and @length == 0
|
116
131
|
end
|
117
132
|
end
|
118
133
|
|
119
|
-
def frame(data, type = nil,
|
134
|
+
def frame(data, type = nil, code = nil)
|
120
135
|
return nil if @closed
|
121
136
|
|
122
|
-
|
123
|
-
|
137
|
+
type ||= (String === data ? :text : :binary)
|
138
|
+
data = data.bytes.to_a if data.respond_to?(:bytes)
|
139
|
+
|
140
|
+
if code
|
141
|
+
data = [code].pack('n').bytes.to_a + data
|
124
142
|
end
|
125
143
|
|
126
|
-
|
127
|
-
|
128
|
-
|
144
|
+
frame = (FIN | OPCODES[type]).chr
|
145
|
+
length = data.size
|
146
|
+
masked = @masking ? MASK : 0
|
129
147
|
|
130
148
|
case length
|
131
149
|
when 0..125 then
|
132
|
-
frame << length
|
150
|
+
frame << (masked | length).chr
|
133
151
|
when 126..65535 then
|
134
|
-
frame << 126
|
152
|
+
frame << (masked | 126).chr
|
135
153
|
frame << [length].pack('n')
|
136
154
|
else
|
137
|
-
frame << 127
|
155
|
+
frame << (masked | 127).chr
|
138
156
|
frame << [length >> 32, length & 0xFFFFFFFF].pack('NN')
|
139
157
|
end
|
140
158
|
|
159
|
+
if @masking
|
160
|
+
mask = (1..4).map { rand 256 }
|
161
|
+
data.each_with_index do |byte, i|
|
162
|
+
data[i] = byte ^ mask[i % 4]
|
163
|
+
end
|
164
|
+
frame << mask.pack('C*')
|
165
|
+
end
|
141
166
|
|
142
167
|
Faye.encode(frame) + Faye.encode(data)
|
143
168
|
end
|
144
169
|
|
145
|
-
def close(
|
170
|
+
def close(code = nil, reason = nil, &callback)
|
146
171
|
return if @closed
|
147
172
|
@closing_callback ||= callback
|
148
|
-
@socket.send('', :close,
|
173
|
+
@socket.send(reason || '', :close, code || ERRORS[:normal_closure])
|
149
174
|
@closed = true
|
150
175
|
end
|
151
176
|
|
152
177
|
private
|
153
178
|
|
154
179
|
def parse_opcode(data)
|
180
|
+
if [RSV1, RSV2, RSV3].any? { |rsv| (data & rsv) == rsv }
|
181
|
+
return @socket.close(ERRORS[:protocol_error], nil, false)
|
182
|
+
end
|
183
|
+
|
155
184
|
@final = (data & FIN) == FIN
|
156
185
|
@opcode = (data & OPCODE)
|
157
186
|
@mask = []
|
158
187
|
@payload = []
|
159
188
|
|
160
|
-
|
161
|
-
|
189
|
+
unless OPCODES.values.include?(@opcode)
|
190
|
+
return @socket.close(ERRORS[:protocol_error], nil, false)
|
191
|
+
end
|
192
|
+
|
193
|
+
unless FRAGMENTED_OPCODES.include?(@opcode) or @final
|
194
|
+
return @socket.close(ERRORS[:protocol_error], nil, false)
|
195
|
+
end
|
196
|
+
|
197
|
+
if @mode and OPENING_OPCODES.include?(@opcode)
|
198
|
+
return @socket.close(ERRORS[:protocol_error], nil, false)
|
199
|
+
end
|
200
|
+
|
201
|
+
@stage = 1
|
162
202
|
end
|
163
203
|
|
164
204
|
def parse_length(data)
|
165
205
|
@masked = (data & MASK) == MASK
|
166
206
|
@length = (data & LENGTH)
|
167
207
|
|
168
|
-
if
|
208
|
+
if @length <= 125
|
169
209
|
@stage = @masked ? 3 : 4
|
170
210
|
else
|
171
211
|
@length_buffer = []
|
@@ -191,7 +231,6 @@ module Faye
|
|
191
231
|
@payload << data
|
192
232
|
return if @payload.size < @length
|
193
233
|
emit_frame
|
194
|
-
@stage = 0
|
195
234
|
end
|
196
235
|
|
197
236
|
def emit_frame
|
@@ -199,32 +238,62 @@ module Faye
|
|
199
238
|
|
200
239
|
case @opcode
|
201
240
|
when OPCODES[:continuation] then
|
202
|
-
return unless @mode
|
203
|
-
@buffer
|
241
|
+
return @socket.close(ERRORS[:protocol_error], nil, false) unless @mode
|
242
|
+
@buffer += payload
|
204
243
|
if @final
|
205
|
-
message = @buffer
|
244
|
+
message = @buffer
|
245
|
+
message = Faye.encode(message, true) if @mode == :text
|
206
246
|
reset
|
207
|
-
|
247
|
+
if message
|
248
|
+
@socket.receive(message)
|
249
|
+
else
|
250
|
+
@socket.close(ERRORS[:encoding_error], nil, false)
|
251
|
+
end
|
208
252
|
end
|
209
253
|
|
210
254
|
when OPCODES[:text] then
|
211
255
|
if @final
|
212
|
-
|
256
|
+
message = Faye.encode(payload, true)
|
257
|
+
if message
|
258
|
+
@socket.receive(message)
|
259
|
+
else
|
260
|
+
@socket.close(ERRORS[:encoding_error], nil, false)
|
261
|
+
end
|
213
262
|
else
|
214
263
|
@mode = :text
|
215
|
-
@buffer
|
264
|
+
@buffer += payload
|
216
265
|
end
|
217
266
|
|
218
267
|
when OPCODES[:binary] then
|
219
|
-
@
|
268
|
+
if @final
|
269
|
+
@socket.receive(payload)
|
270
|
+
else
|
271
|
+
@mode = :binary
|
272
|
+
@buffer += payload
|
273
|
+
end
|
220
274
|
|
221
275
|
when OPCODES[:close] then
|
222
|
-
|
276
|
+
code = (payload.size >= 2) ? 256 * payload[0] + payload[1] : nil
|
277
|
+
|
278
|
+
unless (payload.size == 0) or
|
279
|
+
(code && code >= 3000 && code < 5000) or
|
280
|
+
ERROR_CODES.include?(code)
|
281
|
+
code = ERRORS[:protocol_error]
|
282
|
+
end
|
283
|
+
|
284
|
+
if payload.size > 125 or not Faye.valid_utf8?(payload[2..-1] || [])
|
285
|
+
code = ERRORS[:protocol_error]
|
286
|
+
end
|
287
|
+
|
288
|
+
reason = (payload.size > 2) ? Faye.encode(payload[2..-1], true) : nil
|
289
|
+
@socket.close(code, reason, false)
|
223
290
|
@closing_callback.call if @closing_callback
|
224
291
|
|
225
292
|
when OPCODES[:ping] then
|
293
|
+
return @socket.close(ERRORS[:protocol_error], nil, false) if payload.size > 125
|
226
294
|
@socket.send(payload, :pong)
|
227
295
|
end
|
296
|
+
@stage = 0
|
228
297
|
end
|
229
298
|
|
230
299
|
def reset
|
@@ -232,10 +301,6 @@ module Faye
|
|
232
301
|
@mode = nil
|
233
302
|
end
|
234
303
|
|
235
|
-
def getbyte(data, offset)
|
236
|
-
data.respond_to?(:getbyte) ? data.getbyte(offset) : data[offset]
|
237
|
-
end
|
238
|
-
|
239
304
|
def integer(bytes)
|
240
305
|
number = 0
|
241
306
|
bytes.each_with_index do |data, i|
|
@@ -245,7 +310,7 @@ module Faye
|
|
245
310
|
end
|
246
311
|
|
247
312
|
def unmask(payload, mask)
|
248
|
-
unmasked =
|
313
|
+
unmasked = []
|
249
314
|
payload.each_with_index do |byte, i|
|
250
315
|
byte = byte ^ mask[i % 4] if mask.size > 0
|
251
316
|
unmasked << byte
|