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.

Files changed (43) hide show
  1. data/History.txt +10 -3
  2. data/README.rdoc +1 -2
  3. data/lib/faye-browser-min.js +1 -1
  4. data/lib/faye.rb +89 -32
  5. data/lib/faye/adapters/rack_adapter.rb +20 -26
  6. data/lib/faye/engines/base.rb +5 -0
  7. data/lib/faye/engines/memory.rb +9 -3
  8. data/lib/faye/engines/redis.rb +26 -11
  9. data/lib/faye/mixins/publisher.rb +4 -8
  10. data/lib/faye/protocol/channel.rb +8 -8
  11. data/lib/faye/protocol/client.rb +45 -4
  12. data/lib/faye/protocol/publication.rb +5 -0
  13. data/lib/faye/protocol/server.rb +10 -19
  14. data/lib/faye/thin_extensions.rb +1 -1
  15. data/lib/faye/transport/http.rb +17 -8
  16. data/lib/faye/transport/local.rb +6 -3
  17. data/lib/faye/transport/transport.rb +23 -9
  18. data/lib/faye/transport/web_socket.rb +102 -0
  19. data/lib/faye/util/web_socket.rb +34 -80
  20. data/lib/faye/util/web_socket/api.rb +103 -0
  21. data/lib/faye/util/web_socket/client.rb +82 -0
  22. data/lib/faye/util/web_socket/draft75_parser.rb +3 -5
  23. data/lib/faye/util/web_socket/draft76_parser.rb +5 -7
  24. data/lib/faye/util/web_socket/protocol8_parser.rb +111 -46
  25. data/spec/javascript/client_spec.js +99 -7
  26. data/spec/javascript/engine_spec.js +116 -3
  27. data/spec/javascript/node_adapter_spec.js +2 -4
  28. data/spec/javascript/server/handshake_spec.js +0 -12
  29. data/spec/javascript/server/integration_spec.js +74 -29
  30. data/spec/javascript/server_spec.js +0 -11
  31. data/spec/javascript/web_socket/client_spec.js +121 -0
  32. data/spec/javascript/web_socket/protocol8parser_spec.js +26 -3
  33. data/spec/node.js +2 -0
  34. data/spec/redis.conf +10 -280
  35. data/spec/ruby/client_spec.rb +101 -8
  36. data/spec/ruby/engine_spec.rb +106 -0
  37. data/spec/ruby/server/handshake_spec.rb +0 -12
  38. data/spec/ruby/server/integration_spec.rb +56 -18
  39. data/spec/ruby/server_spec.rb +1 -12
  40. data/spec/ruby/transport_spec.rb +14 -8
  41. data/spec/ruby/web_socket/client_spec.rb +126 -0
  42. data/spec/ruby/web_socket/protocol8_parser_spec.rb +28 -3
  43. metadata +96 -150
@@ -1,135 +1,89 @@
1
1
  module Faye
2
2
  class WebSocket
3
3
 
4
- autoload :Draft75Parser, File.expand_path('..', __FILE__) + '/web_socket/draft75_parser'
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
- include Publisher
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
- CONNECTING = 0
11
- OPEN = 1
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(request)
22
- if request.env['HTTP_SEC_WEBSOCKET_VERSION']
18
+ def self.parser(env)
19
+ if env['HTTP_SEC_WEBSOCKET_VERSION']
23
20
  Protocol8Parser
24
- elsif request.env['HTTP_SEC_WEBSOCKET_KEY1']
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(request)
32
- @request = request
33
- @callback = @request.env['async.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(@request).new(self)
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
- @request.env[Thin::Request::WEBSOCKET_RECEIVE_CALLBACK] = @parser.method(:parse)
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
- env = @request.env
89
- secure = if env.has_key?('HTTP_X_FORWARDED_PROTO')
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
- def each(&callback)
104
- @data_callback = callback
105
- end
67
+ extend Forwardable
68
+ def_delegators :@connection, :close_connection, :close_connection_after_writing
106
69
 
107
- def write(data)
108
- return unless @data_callback
109
- @data_callback.call(data)
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 init_event(event_type, can_bubble, cancelable)
122
- @type = event_type
123
- @bubbles = can_bubble
124
- @cancelable = cancelable
75
+ def each(&callback)
76
+ @data_callback = callback
125
77
  end
126
78
 
127
- def stop_propagation
79
+ def fail
80
+ @web_socket.close(1006, '', false)
128
81
  end
129
82
 
130
- def prevent_default
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: #{request.env['HTTP_ORIGIN']}\r\n"
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.join('')))
41
+ @socket.receive(Faye.encode(@buffer))
44
42
  @buffer = []
45
43
  @buffering = false
46
44
 
47
45
  else
48
- @buffer.push(data.chr) if @buffering
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
- request = @socket.request
10
+ env = @socket.env
13
11
 
14
- key1 = request.env['HTTP_SEC_WEBSOCKET_KEY1']
12
+ key1 = env['HTTP_SEC_WEBSOCKET_KEY1']
15
13
  value1 = number_from_key(key1) / spaces_in_key(key1)
16
14
 
17
- key2 = request.env['HTTP_SEC_WEBSOCKET_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
- request.body.read)
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: #{request.env['HTTP_ORIGIN']}\r\n"
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 => 1000,
29
- :going_away => 1001,
30
- :protocol_error => 1002,
31
- :unacceptable => 1003,
32
- :too_large => 1004,
33
- :encoding_error => 1007
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
- data.each_byte { |b| @buffer << b.chr }
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] == ["\r", "\n", "\r", "\n"]
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 = web_socket
82
- @stage = 0
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.request.env['HTTP_SEC_WEBSOCKET_KEY']
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, error_type = nil)
134
+ def frame(data, type = nil, code = nil)
120
135
  return nil if @closed
121
136
 
122
- if error_type
123
- data = [ERRORS[error_type]].pack('n') + data
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
- opcode = OPCODES[type || :text]
127
- frame = (FIN | opcode).chr
128
- length = data.respond_to?(:bytes) ? data.bytes.count : data.size
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(error_type = nil, &callback)
170
+ def close(code = nil, reason = nil, &callback)
146
171
  return if @closed
147
172
  @closing_callback ||= callback
148
- @socket.send('', :close, error_type || :normal_closure)
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
- return @socket.close(:protocol_error) unless OPCODES.values.include?(@opcode)
161
- @stage = 1
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 (1..125).include?(@length)
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 == :text
203
- @buffer << payload
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
- @socket.receive(Faye.encode(message))
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
- @socket.receive(Faye.encode(payload))
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 << payload
264
+ @buffer += payload
216
265
  end
217
266
 
218
267
  when OPCODES[:binary] then
219
- @socket.close(:unacceptable)
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
- @socket.close(:normal_closure)
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