websocket-driver 0.6.5-java → 0.7.4-java

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.
@@ -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