websocket-driver 0.6.5-java → 0.7.4-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,41 +1,32 @@
1
1
  #include <ruby.h>
2
2
 
3
- VALUE WebSocket = Qnil;
4
- VALUE WebSocketMask = Qnil;
5
-
6
- void Init_websocket_mask();
7
- VALUE method_websocket_mask(VALUE self, VALUE payload, VALUE mask);
8
-
9
- void
10
- Init_websocket_mask()
3
+ VALUE method_websocket_mask(VALUE self, VALUE payload, VALUE mask)
11
4
  {
12
- WebSocket = rb_define_module("WebSocket");
13
- WebSocketMask = rb_define_module_under(WebSocket, "Mask");
14
- rb_define_singleton_method(WebSocketMask, "mask", method_websocket_mask, 2);
15
- }
5
+ char *payload_s, *mask_s, *unmasked_s;
6
+ long i, n;
7
+ VALUE unmasked;
16
8
 
17
- VALUE
18
- method_websocket_mask(VALUE self,
19
- VALUE payload,
20
- VALUE mask)
21
- {
22
- char *payload_s, *mask_s, *unmasked_s;
23
- long i, n;
24
- VALUE unmasked;
9
+ if (mask == Qnil || RSTRING_LEN(mask) != 4) {
10
+ return payload;
11
+ }
25
12
 
26
- if (mask == Qnil || RSTRING_LEN(mask) != 4) {
27
- return payload;
28
- }
13
+ payload_s = RSTRING_PTR(payload);
14
+ mask_s = RSTRING_PTR(mask);
15
+ n = RSTRING_LEN(payload);
29
16
 
30
- payload_s = RSTRING_PTR(payload);
31
- mask_s = RSTRING_PTR(mask);
32
- n = RSTRING_LEN(payload);
17
+ unmasked = rb_str_new(0, n);
18
+ unmasked_s = RSTRING_PTR(unmasked);
33
19
 
34
- unmasked = rb_str_new(0, n);
35
- unmasked_s = RSTRING_PTR(unmasked);
20
+ for (i = 0; i < n; i++) {
21
+ unmasked_s[i] = payload_s[i] ^ mask_s[i % 4];
22
+ }
23
+ return unmasked;
24
+ }
25
+
26
+ void Init_websocket_mask()
27
+ {
28
+ VALUE WebSocket = rb_define_module("WebSocket");
29
+ VALUE Mask = rb_define_module_under(WebSocket, "Mask");
36
30
 
37
- for (i = 0; i < n; i++) {
38
- unmasked_s[i] = payload_s[i] ^ mask_s[i % 4];
39
- }
40
- return unmasked;
31
+ rb_define_singleton_method(Mask, "mask", method_websocket_mask, 2);
41
32
  }
@@ -44,12 +44,11 @@ module WebSocket
44
44
  MAX_LENGTH = 0x3ffffff
45
45
  STATES = [:connecting, :open, :closing, :closed]
46
46
 
47
- BINARY = 'ASCII-8BIT'
48
- UNICODE = 'UTF-8'
49
-
50
47
  ConnectEvent = Struct.new(nil)
51
48
  OpenEvent = Struct.new(nil)
52
49
  MessageEvent = Struct.new(:data)
50
+ PingEvent = Struct.new(:data)
51
+ PongEvent = Struct.new(:data)
53
52
  CloseEvent = Struct.new(:code, :reason)
54
53
 
55
54
  ProtocolError = Class.new(StandardError)
@@ -99,15 +98,24 @@ module WebSocket
99
98
 
100
99
  def start
101
100
  return false unless @ready_state == 0
102
- response = handshake_response
103
- return false unless response
101
+
102
+ unless Driver.websocket?(@socket.env)
103
+ return fail_handshake(ProtocolError.new('Not a WebSocket request'))
104
+ end
105
+
106
+ begin
107
+ response = handshake_response
108
+ rescue => error
109
+ return fail_handshake(error)
110
+ end
111
+
104
112
  @socket.write(response)
105
113
  open unless @stage == -1
106
114
  true
107
115
  end
108
116
 
109
117
  def text(message)
110
- message = message.encode(UNICODE) unless message.encoding.name == UNICODE
118
+ message = message.encode(Encoding::UTF_8) unless message.encoding == Encoding::UTF_8
111
119
  frame(message, :text)
112
120
  end
113
121
 
@@ -132,6 +140,24 @@ module WebSocket
132
140
 
133
141
  private
134
142
 
143
+ def fail_handshake(error)
144
+ headers = Headers.new
145
+ headers['Content-Type'] = 'text/plain'
146
+ headers['Content-Length'] = error.message.bytesize
147
+
148
+ headers = ['HTTP/1.1 400 Bad Request', headers.to_s, error.message]
149
+ @socket.write(headers.join("\r\n"))
150
+ fail(:protocol_error, error.message)
151
+
152
+ false
153
+ end
154
+
155
+ def fail(type, message)
156
+ @ready_state = 2
157
+ emit(:error, ProtocolError.new(message))
158
+ close
159
+ end
160
+
135
161
  def open
136
162
  @ready_state = 1
137
163
  @queue.each { |message| frame(*message) }
@@ -153,10 +179,15 @@ module WebSocket
153
179
  end
154
180
 
155
181
  def self.rack(socket, options = {})
156
- env = socket.env
157
- if env['HTTP_SEC_WEBSOCKET_VERSION']
182
+ env = socket.env
183
+ version = env['HTTP_SEC_WEBSOCKET_VERSION']
184
+ key = env['HTTP_SEC_WEBSOCKET_KEY']
185
+ key1 = env['HTTP_SEC_WEBSOCKET_KEY1']
186
+ key2 = env['HTTP_SEC_WEBSOCKET_KEY2']
187
+
188
+ if version or key
158
189
  Hybi.new(socket, options.merge(:require_masking => true))
159
- elsif env['HTTP_SEC_WEBSOCKET_KEY1']
190
+ elsif key1 or key2
160
191
  Draft76.new(socket, options)
161
192
  else
162
193
  Draft75.new(socket, options)
@@ -167,21 +198,21 @@ module WebSocket
167
198
  case string
168
199
  when Array then
169
200
  string = string.pack('C*')
170
- encoding ||= BINARY
201
+ encoding ||= Encoding::BINARY
171
202
  when String then
172
- encoding ||= UNICODE
203
+ encoding ||= Encoding::UTF_8
173
204
  end
174
- unless string.encoding.name == encoding
205
+ unless string.encoding == encoding
175
206
  string = string.dup if string.frozen?
176
207
  string.force_encoding(encoding)
177
208
  end
178
- string.valid_encoding? ? string : nil
209
+ string
179
210
  end
180
211
 
181
212
  def self.validate_options(options, valid_keys)
182
213
  options.keys.each do |key|
183
214
  unless valid_keys.include?(key)
184
- raise ConfigurationError, "Unrecognized option: #{key.inspect}"
215
+ raise ConfigurationError, "Unrecognized option: #{ key.inspect }"
185
216
  end
186
217
  end
187
218
  end
@@ -20,10 +20,10 @@ module WebSocket
20
20
 
21
21
  uri = URI.parse(@socket.url)
22
22
  unless VALID_SCHEMES.include?(uri.scheme)
23
- raise URIError, "#{socket.url} is not a valid WebSocket URL"
23
+ raise URIError, "#{ socket.url } is not a valid WebSocket URL"
24
24
  end
25
25
 
26
- host = uri.host + (uri.port ? ":#{uri.port}" : '')
26
+ host = uri.host + (uri.port ? ":#{ uri.port }" : '')
27
27
  path = (uri.path == '') ? '/' : uri.path
28
28
  @pathname = path + (uri.query ? '?' + uri.query : '')
29
29
 
@@ -31,7 +31,7 @@ module WebSocket
31
31
  @headers['Upgrade'] = 'websocket'
32
32
  @headers['Connection'] = 'Upgrade'
33
33
  @headers['Sec-WebSocket-Key'] = @key
34
- @headers['Sec-WebSocket-Version'] = '13'
34
+ @headers['Sec-WebSocket-Version'] = VERSION
35
35
 
36
36
  if @protocols.size > 0
37
37
  @headers['Sec-WebSocket-Protocol'] = @protocols * ', '
@@ -44,7 +44,7 @@ module WebSocket
44
44
  end
45
45
 
46
46
  def version
47
- 'hybi-13'
47
+ "hybi-#{ VERSION }"
48
48
  end
49
49
 
50
50
  def proxy(origin, options = {})
@@ -73,19 +73,19 @@ module WebSocket
73
73
  parse(@http.body)
74
74
  end
75
75
 
76
- private
76
+ private
77
77
 
78
78
  def handshake_request
79
79
  extensions = @extensions.generate_offer
80
80
  @headers['Sec-WebSocket-Extensions'] = extensions if extensions
81
81
 
82
- start = "GET #{@pathname} HTTP/1.1"
82
+ start = "GET #{ @pathname } HTTP/1.1"
83
83
  headers = [start, @headers.to_s, '']
84
84
  headers.join("\r\n")
85
85
  end
86
86
 
87
87
  def fail_handshake(message)
88
- message = "Error during WebSocket handshake: #{message}"
88
+ message = "Error during WebSocket handshake: #{ message }"
89
89
  @ready_state = 3
90
90
  emit(:error, ProtocolError.new(message))
91
91
  emit(:close, CloseEvent.new(ERRORS[:protocol_error], message))
@@ -96,7 +96,7 @@ module WebSocket
96
96
  @headers = Headers.new(@http.headers)
97
97
 
98
98
  unless @http.code == 101
99
- return fail_handshake("Unexpected response code: #{@http.code}")
99
+ return fail_handshake("Unexpected response code: #{ @http.code }")
100
100
  end
101
101
 
102
102
  upgrade = @http['Upgrade'] || ''
@@ -56,7 +56,7 @@ module WebSocket
56
56
  when 2 then
57
57
  if octet == 0xFF
58
58
  @stage = 0
59
- emit(:message, MessageEvent.new(Driver.encode(@buffer, UNICODE)))
59
+ emit(:message, MessageEvent.new(Driver.encode(@buffer, Encoding::UTF_8)))
60
60
  else
61
61
  if @length
62
62
  @skipped += 1
@@ -6,9 +6,10 @@ module WebSocket
6
6
 
7
7
  def initialize(socket, options = {})
8
8
  super
9
- input = @socket.env['rack.input']
9
+ input = (@socket.env['rack.input'] || StringIO.new('')).read
10
+ input = input.dup if input.frozen?
10
11
  @stage = -1
11
- @body = (input ? input.read : String.new('')).force_encoding(BINARY)
12
+ @body = input.force_encoding(Encoding::BINARY)
12
13
 
13
14
  @headers.clear
14
15
  @headers['Upgrade'] = 'WebSocket'
@@ -29,7 +30,7 @@ module WebSocket
29
30
 
30
31
  def close(reason = nil, code = nil)
31
32
  return false if @ready_state == 3
32
- @socket.write([0xFF, 0x00].pack('C*'))
33
+ @socket.write([0xFF, 0x00].pack('C*')) if @ready_state == 1
33
34
  @ready_state = 3
34
35
  emit(:close, CloseEvent.new(nil, nil))
35
36
  true
@@ -39,19 +40,20 @@ module WebSocket
39
40
 
40
41
  def handshake_response
41
42
  env = @socket.env
42
-
43
43
  key1 = env['HTTP_SEC_WEBSOCKET_KEY1']
44
+ key2 = env['HTTP_SEC_WEBSOCKET_KEY2']
45
+
46
+ raise ProtocolError.new('Missing required header: Sec-WebSocket-Key1') unless key1
47
+ raise ProtocolError.new('Missing required header: Sec-WebSocket-Key2') unless key2
48
+
44
49
  number1 = number_from_key(key1)
45
50
  spaces1 = spaces_in_key(key1)
46
51
 
47
- key2 = env['HTTP_SEC_WEBSOCKET_KEY2']
48
52
  number2 = number_from_key(key2)
49
53
  spaces2 = spaces_in_key(key2)
50
54
 
51
55
  if number1 % spaces1 != 0 or number2 % spaces2 != 0
52
- emit(:error, ProtocolError.new('Client sent invalid Sec-WebSocket-Key headers'))
53
- close
54
- return nil
56
+ raise ProtocolError.new('Client sent invalid Sec-WebSocket-Key headers')
55
57
  end
56
58
 
57
59
  @key_values = [number1 / spaces1, number2 / spaces2]
@@ -84,7 +86,8 @@ module WebSocket
84
86
  end
85
87
 
86
88
  def number_from_key(key)
87
- key.scan(/[0-9]/).join('').to_i(10)
89
+ number = key.scan(/[0-9]/).join('')
90
+ number == '' ? Float::NAN : number.to_i(10)
88
91
  end
89
92
 
90
93
  def spaces_in_key(key)
@@ -25,7 +25,7 @@ module WebSocket
25
25
  return if value.nil?
26
26
  key = HTTP.normalize_header(name)
27
27
  return unless @sent.add?(key) or ALLOWED_DUPLICATES.include?(key)
28
- @lines << "#{name.strip}: #{value.to_s.strip}\r\n"
28
+ @lines << "#{ name.strip }: #{ value.to_s.strip }\r\n"
29
29
  end
30
30
 
31
31
  def inspect
@@ -11,7 +11,8 @@ module WebSocket
11
11
  Base64.strict_encode64(Digest::SHA1.digest(key + GUID))
12
12
  end
13
13
 
14
- GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
14
+ VERSION = '13'
15
+ GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
15
16
 
16
17
  BYTE = 0b11111111
17
18
  FIN = MASK = 0b10000000
@@ -51,7 +52,7 @@ module WebSocket
51
52
  MIN_RESERVED_ERROR = 3000
52
53
  MAX_RESERVED_ERROR = 4999
53
54
 
54
- PACK_FORMATS = {2 => 'n', 8 => 'Q>'}
55
+ PACK_FORMATS = { 2 => 'S>', 8 => 'Q>' }
55
56
 
56
57
  def initialize(socket, options = {})
57
58
  super
@@ -68,22 +69,16 @@ module WebSocket
68
69
 
69
70
  return unless @socket.respond_to?(:env)
70
71
 
71
- sec_key = @socket.env['HTTP_SEC_WEBSOCKET_KEY']
72
- protos = @socket.env['HTTP_SEC_WEBSOCKET_PROTOCOL']
73
-
74
- @headers['Upgrade'] = 'websocket'
75
- @headers['Connection'] = 'Upgrade'
76
- @headers['Sec-WebSocket-Accept'] = Hybi.generate_accept(sec_key)
77
-
78
72
  if protos = @socket.env['HTTP_SEC_WEBSOCKET_PROTOCOL']
79
73
  protos = protos.split(/ *, */) if String === protos
80
74
  @protocol = protos.find { |p| @protocols.include?(p) }
81
- @headers['Sec-WebSocket-Protocol'] = @protocol if @protocol
75
+ else
76
+ @protocol = nil
82
77
  end
83
78
  end
84
79
 
85
80
  def version
86
- "hybi-#{@socket.env['HTTP_SEC_WEBSOCKET_VERSION']}"
81
+ "hybi-#{ VERSION }"
87
82
  end
88
83
 
89
84
  def add_extension(extension)
@@ -165,14 +160,13 @@ module WebSocket
165
160
 
166
161
  message = Message.new
167
162
  frame = Frame.new
168
- is_text = String === buffer
169
163
 
170
164
  message.rsv1 = message.rsv2 = message.rsv3 = false
171
- message.opcode = OPCODES[type || (is_text ? :text : :binary)]
165
+ message.opcode = OPCODES[type || (String === buffer ? :text : :binary)]
172
166
 
173
- payload = is_text ? buffer.bytes.to_a : buffer
174
- payload = [code].pack(PACK_FORMATS[2]).bytes.to_a + payload if code
175
- message.data = payload.pack('C*')
167
+ payload = Driver.encode(buffer, Encoding::BINARY)
168
+ payload = [code, payload].pack('S>a*') if code
169
+ message.data = payload
176
170
 
177
171
  if MESSAGE_OPCODES.include?(message.opcode)
178
172
  message = @extensions.process_outgoing_message(message)
@@ -199,43 +193,59 @@ module WebSocket
199
193
 
200
194
  def send_frame(frame)
201
195
  length = frame.length
202
- buffer = []
196
+ values = []
197
+ format = 'C2'
203
198
  masked = frame.masked ? MASK : 0
204
199
 
205
- buffer[0] = (frame.final ? FIN : 0) |
200
+ values[0] = (frame.final ? FIN : 0) |
206
201
  (frame.rsv1 ? RSV1 : 0) |
207
202
  (frame.rsv2 ? RSV2 : 0) |
208
203
  (frame.rsv3 ? RSV3 : 0) |
209
204
  frame.opcode
210
205
 
211
206
  if length <= 125
212
- buffer[1] = masked | length
207
+ values[1] = masked | length
213
208
  elsif length <= 65535
214
- buffer[1] = masked | 126
215
- buffer[2..3] = [length].pack(PACK_FORMATS[2]).bytes.to_a
209
+ values[1] = masked | 126
210
+ values[2] = length
211
+ format << 'S>'
216
212
  else
217
- buffer[1] = masked | 127
218
- buffer[2..9] = [length].pack(PACK_FORMATS[8]).bytes.to_a
213
+ values[1] = masked | 127
214
+ values[2] = length
215
+ format << 'Q>'
219
216
  end
220
217
 
221
218
  if frame.masked
222
- buffer.concat(frame.masking_key.bytes.to_a)
223
- buffer.concat(Mask.mask(frame.payload, frame.masking_key).bytes.to_a)
219
+ values << frame.masking_key
220
+ values << Mask.mask(frame.payload, frame.masking_key)
221
+ format << 'a4a*'
224
222
  else
225
- buffer.concat(frame.payload.bytes.to_a)
223
+ values << frame.payload
224
+ format << 'a*'
226
225
  end
227
226
 
228
- @socket.write(buffer.pack('C*'))
227
+ @socket.write(values.pack(format))
229
228
  end
230
229
 
231
230
  def handshake_response
232
- begin
233
- extensions = @extensions.generate_response(@socket.env['HTTP_SEC_WEBSOCKET_EXTENSIONS'])
234
- rescue => error
235
- fail(:protocol_error, error.message)
236
- return nil
231
+ sec_key = @socket.env['HTTP_SEC_WEBSOCKET_KEY']
232
+ version = @socket.env['HTTP_SEC_WEBSOCKET_VERSION']
233
+
234
+ unless version == VERSION
235
+ raise ProtocolError.new("Unsupported WebSocket version: #{ VERSION }")
236
+ end
237
+
238
+ unless sec_key
239
+ raise ProtocolError.new('Missing handshake request header: Sec-WebSocket-Key')
237
240
  end
238
241
 
242
+ @headers['Upgrade'] = 'websocket'
243
+ @headers['Connection'] = 'Upgrade'
244
+ @headers['Sec-WebSocket-Accept'] = Hybi.generate_accept(sec_key)
245
+
246
+ @headers['Sec-WebSocket-Protocol'] = @protocol if @protocol
247
+
248
+ extensions = @extensions.generate_response(@socket.env['HTTP_SEC_WEBSOCKET_EXTENSIONS'])
239
249
  @headers['Sec-WebSocket-Extensions'] = extensions if extensions
240
250
 
241
251
  start = 'HTTP/1.1 101 Switching Protocols'
@@ -275,17 +285,17 @@ module WebSocket
275
285
 
276
286
  unless @extensions.valid_frame_rsv?(@frame)
277
287
  return fail(:protocol_error,
278
- "One or more reserved bits are on: reserved1 = #{@frame.rsv1 ? 1 : 0}" +
279
- ", reserved2 = #{@frame.rsv2 ? 1 : 0 }" +
280
- ", reserved3 = #{@frame.rsv3 ? 1 : 0 }")
288
+ "One or more reserved bits are on: reserved1 = #{ @frame.rsv1 ? 1 : 0 }" +
289
+ ", reserved2 = #{ @frame.rsv2 ? 1 : 0 }" +
290
+ ", reserved3 = #{ @frame.rsv3 ? 1 : 0 }")
281
291
  end
282
292
 
283
293
  unless OPCODES.values.include?(@frame.opcode)
284
- return fail(:protocol_error, "Unrecognized frame opcode: #{@frame.opcode}")
294
+ return fail(:protocol_error, "Unrecognized frame opcode: #{ @frame.opcode }")
285
295
  end
286
296
 
287
297
  unless MESSAGE_OPCODES.include?(@frame.opcode) or @frame.final
288
- return fail(:protocol_error, "Received fragmented control frame: opcode = #{@frame.opcode}")
298
+ return fail(:protocol_error, "Received fragmented control frame: opcode = #{ @frame.opcode }")
289
299
  end
290
300
 
291
301
  if @message and OPENING_OPCODES.include?(@frame.opcode)
@@ -315,7 +325,7 @@ module WebSocket
315
325
  @stage = @frame.masked ? 3 : 4
316
326
 
317
327
  unless MESSAGE_OPCODES.include?(@frame.opcode) or @frame.length <= 125
318
- return fail(:protocol_error, "Received control frame having too long payload: #{@frame.length}")
328
+ return fail(:protocol_error, "Received control frame having too long payload: #{ @frame.length }")
319
329
  end
320
330
 
321
331
  return unless check_frame_length
@@ -337,7 +347,6 @@ module WebSocket
337
347
  opcode = frame.opcode
338
348
  payload = frame.payload = Mask.mask(buffer, @frame.masking_key)
339
349
  bytesize = payload.bytesize
340
- bytes = payload.bytes.to_a
341
350
 
342
351
  @frame = nil
343
352
 
@@ -351,8 +360,8 @@ module WebSocket
351
360
  @message << frame
352
361
 
353
362
  when OPCODES[:close] then
354
- code = (bytesize >= 2) ? payload.unpack(PACK_FORMATS[2]).first : nil
355
- reason = (bytesize > 2) ? Driver.encode(bytes[2..-1] || [], UNICODE) : nil
363
+ code, reason = payload.unpack('S>a*') if bytesize >= 2
364
+ reason = Driver.encode(reason || '', Encoding::UTF_8)
356
365
 
357
366
  unless (bytesize == 0) or
358
367
  (code && code >= MIN_RESERVED_ERROR && code <= MAX_RESERVED_ERROR) or
@@ -360,7 +369,7 @@ module WebSocket
360
369
  code = ERRORS[:protocol_error]
361
370
  end
362
371
 
363
- if bytesize > 125 or (bytesize > 2 and reason.nil?)
372
+ if bytesize > 125 or !reason.valid_encoding?
364
373
  code = ERRORS[:protocol_error]
365
374
  end
366
375
 
@@ -368,12 +377,14 @@ module WebSocket
368
377
 
369
378
  when OPCODES[:ping] then
370
379
  frame(payload, :pong)
380
+ emit(:ping, PingEvent.new(payload))
371
381
 
372
382
  when OPCODES[:pong] then
373
- message = Driver.encode(payload, UNICODE)
383
+ message = Driver.encode(payload, Encoding::UTF_8)
374
384
  callback = @ping_callbacks[message]
375
385
  @ping_callbacks.delete(message)
376
386
  callback.call if callback
387
+ emit(:pong, PongEvent.new(payload))
377
388
  end
378
389
 
379
390
  emit_message if frame.final and MESSAGE_OPCODES.include?(opcode)
@@ -387,7 +398,8 @@ module WebSocket
387
398
 
388
399
  case message.opcode
389
400
  when OPCODES[:text] then
390
- payload = Driver.encode(payload, UNICODE)
401
+ payload = Driver.encode(payload, Encoding::UTF_8)
402
+ payload = nil unless payload.valid_encoding?
391
403
  when OPCODES[:binary]
392
404
  payload = payload.bytes.to_a
393
405
  end