em-websocket 0.3.6 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,16 @@
1
1
  = Changelog
2
2
 
3
+ == 0.3.7 / 2012-07-11
4
+
5
+ - new features:
6
+ - Supports sending 1009 error code when incoming frame is too large to handle, and added associated exception class WSMessageTooBigError [Martyn Loughran]
7
+ - Supports overriding the maximum frame size by setting the max_frame_size accessor on the connection object (in bytes). Default unchanged at 10MB. [Martyn Loughran]
8
+
9
+ - bug fixes:
10
+ - Fixes some encoding issues on Ruby 1.9 [Dingding Ye]
11
+ - Raises a HandshakeError if WS header is empty [Markus Fenske]
12
+ - Connection#send would mutate passed string to BINARY encoding. The fix still mutates the string by forcing the encoding back to UTF-8 before returning, but if the passed string was encoded as UTF-8 this is equivalent [Martyn Loughran]
13
+
3
14
  == 0.3.6 / 2011-12-23
4
15
 
5
16
  - new features:
@@ -5,6 +5,8 @@ module EventMachine
5
5
  class Connection < EventMachine::Connection
6
6
  include Debugger
7
7
 
8
+ attr_writer :max_frame_size
9
+
8
10
  # define WebSocket callbacks
9
11
  def onopen(&blk); @onopen = blk; end
10
12
  def onclose(&blk); @onclose = blk; end
@@ -75,10 +77,10 @@ module EventMachine
75
77
  trigger_on_error(e)
76
78
  # Errors during the handshake require the connection to be aborted
77
79
  abort
78
- rescue WebSocketError => e
80
+ rescue WSProtocolError => e
79
81
  debug [:error, e]
80
82
  trigger_on_error(e)
81
- close_websocket_private(1002) # 1002 indicates a protocol error
83
+ close_websocket_private(e.code)
82
84
  rescue => e
83
85
  debug [:error, e]
84
86
  # These are application errors - raise unless onerror defined
@@ -127,16 +129,26 @@ module EventMachine
127
129
  close_connection_after_writing
128
130
  end
129
131
 
132
+ # Cache encodings since it's moderately expensive to look them up each time
133
+ ENCODING_SUPPORTED = "string".respond_to?(:force_encoding)
134
+ UTF8 = Encoding.find("UTF-8") if ENCODING_SUPPORTED
135
+ BINARY = Encoding.find("BINARY") if ENCODING_SUPPORTED
136
+
137
+ # Send a WebSocket text frame.
138
+ #
139
+ # A WebSocketError may be raised if the connection is in an opening or a
140
+ # closing state, or if the passed in data is not valid UTF-8
141
+ #
130
142
  def send(data)
131
143
  # If we're using Ruby 1.9, be pedantic about encodings
132
- if data.respond_to?(:force_encoding)
144
+ if ENCODING_SUPPORTED
133
145
  # Also accept ascii only data in other encodings for convenience
134
- unless (data.encoding == Encoding.find("UTF-8") && data.valid_encoding?) || data.ascii_only?
146
+ unless (data.encoding == UTF8 && data.valid_encoding?) || data.ascii_only?
135
147
  raise WebSocketError, "Data sent to WebSocket must be valid UTF-8 but was #{data.encoding} (valid: #{data.valid_encoding?})"
136
148
  end
137
149
  # This labels the encoding as binary so that it can be combined with
138
150
  # the BINARY framing
139
- data.force_encoding("BINARY")
151
+ data.force_encoding(BINARY)
140
152
  else
141
153
  # TODO: Check that data is valid UTF-8
142
154
  end
@@ -146,6 +158,10 @@ module EventMachine
146
158
  else
147
159
  raise WebSocketError, "Cannot send data before onopen callback"
148
160
  end
161
+
162
+ # Revert data back to the original encoding (which we assume is UTF-8)
163
+ # Doing this to avoid duping the string - there may be a better way
164
+ data.force_encoding(UTF8)
149
165
  end
150
166
 
151
167
  # Send a ping to the client. The client must respond with a pong.
@@ -193,6 +209,18 @@ module EventMachine
193
209
  @handler ? @handler.state : :handshake
194
210
  end
195
211
 
212
+ # Returns the maximum frame size which this connection is configured to
213
+ # accept. This can be set globally or on a per connection basis, and
214
+ # defaults to a value of 10MB if not set.
215
+ #
216
+ # The behaviour when a too large frame is received varies by protocol,
217
+ # but in the newest protocols the connection will be closed with the
218
+ # correct close code (1009) immediately after receiving the frame header
219
+ #
220
+ def max_frame_size
221
+ @max_frame_size || WebSocket.max_frame_size
222
+ end
223
+
196
224
  private
197
225
 
198
226
  # As definited in draft 06 7.2.2, some failures require that the server
@@ -3,11 +3,6 @@
3
3
  module EventMachine
4
4
  module WebSocket
5
5
  module Framing03
6
-
7
- # Set the max frame lenth to very high value (10MB) until there is a
8
- # limit specified in the spec to protect against malicious attacks
9
- MAXIMUM_FRAME_LENGTH = 10 * 1024 * 1024
10
-
11
6
  def initialize_framing
12
7
  @data = ''
13
8
  @application_data_buffer = '' # Used for MORE frames
@@ -57,9 +52,8 @@ module EventMachine
57
52
  length
58
53
  end
59
54
 
60
- # Addition to the spec to protect against malicious requests
61
- if payload_length > MAXIMUM_FRAME_LENGTH
62
- raise DataError, "Frame length too long (#{payload_length} bytes)"
55
+ if payload_length > @connection.max_frame_size
56
+ raise WSMessageTooBigError, "Frame length too long (#{payload_length} bytes)"
63
57
  end
64
58
 
65
59
  # Check buffer size
@@ -78,7 +72,7 @@ module EventMachine
78
72
  frame_type = opcode_to_type(opcode)
79
73
 
80
74
  if frame_type == :continuation && !@frame_type
81
- raise WebSocketError, 'Continuation frame not expected'
75
+ raise WSProtocolError, 'Continuation frame not expected'
82
76
  end
83
77
 
84
78
  if more
@@ -156,7 +150,7 @@ module EventMachine
156
150
  end
157
151
 
158
152
  def opcode_to_type(opcode)
159
- FRAME_TYPES_INVERSE[opcode] || raise(DataError, "Unknown opcode")
153
+ FRAME_TYPES_INVERSE[opcode] || raise(WSProtocolError, "Unknown opcode")
160
154
  end
161
155
 
162
156
  def data_frame?(type)
@@ -3,11 +3,6 @@
3
3
  module EventMachine
4
4
  module WebSocket
5
5
  module Framing05
6
-
7
- # Set the max frame lenth to very high value (10MB) until there is a
8
- # limit specified in the spec to protect against malicious attacks
9
- MAXIMUM_FRAME_LENGTH = 10 * 1024 * 1024
10
-
11
6
  def initialize_framing
12
7
  @data = MaskedString.new
13
8
  @application_data_buffer = '' # Used for MORE frames
@@ -60,9 +55,8 @@ module EventMachine
60
55
  length
61
56
  end
62
57
 
63
- # Addition to the spec to protect against malicious requests
64
- if payload_length > MAXIMUM_FRAME_LENGTH
65
- raise DataError, "Frame length too long (#{payload_length} bytes)"
58
+ if payload_length > @connection.max_frame_size
59
+ raise WSMessageTooBigError, "Frame length too long (#{payload_length} bytes)"
66
60
  end
67
61
 
68
62
  # Check buffer size
@@ -83,7 +77,7 @@ module EventMachine
83
77
  frame_type = opcode_to_type(opcode)
84
78
 
85
79
  if frame_type == :continuation && !@frame_type
86
- raise WebSocketError, 'Continuation frame not expected'
80
+ raise WSProtocolError, 'Continuation frame not expected'
87
81
  end
88
82
 
89
83
  if !fin
@@ -157,7 +151,7 @@ module EventMachine
157
151
  end
158
152
 
159
153
  def opcode_to_type(opcode)
160
- FRAME_TYPES_INVERSE[opcode] || raise(DataError, "Unknown opcode")
154
+ FRAME_TYPES_INVERSE[opcode] || raise(WSProtocolError, "Unknown opcode")
161
155
  end
162
156
 
163
157
  def data_frame?(type)
@@ -59,6 +59,10 @@ module EventMachine
59
59
  frame_length = pointer + payload_length
60
60
  frame_length += 4 if mask
61
61
 
62
+ if frame_length > @connection.max_frame_size
63
+ raise WSMessageTooBigError, "Frame length too long (#{frame_length} bytes)"
64
+ end
65
+
62
66
  # Check buffer size
63
67
  if @data.getbyte(frame_length - 1) == nil
64
68
  debug [:buffer_incomplete, @data]
@@ -83,7 +87,7 @@ module EventMachine
83
87
  frame_type = opcode_to_type(opcode)
84
88
 
85
89
  if frame_type == :continuation && !@frame_type
86
- raise WebSocketError, 'Continuation frame not expected'
90
+ raise WSProtocolError, 'Continuation frame not expected'
87
91
  end
88
92
 
89
93
  if !fin
@@ -158,7 +162,7 @@ module EventMachine
158
162
  end
159
163
 
160
164
  def opcode_to_type(opcode)
161
- FRAME_TYPES_INVERSE[opcode] || raise(DataError, "Unknown opcode")
165
+ FRAME_TYPES_INVERSE[opcode] || raise(WSProtocolError, "Unknown opcode")
162
166
  end
163
167
 
164
168
  def data_frame?(type)
@@ -3,11 +3,6 @@
3
3
  module EventMachine
4
4
  module WebSocket
5
5
  module Framing76
6
-
7
- # Set the max frame lenth to very high value (10MB) until there is a
8
- # limit specified in the spec to protect against malicious attacks
9
- MAXIMUM_FRAME_LENGTH = 10 * 1024 * 1024
10
-
11
6
  def initialize_framing
12
7
  @data = ''
13
8
  end
@@ -40,9 +35,8 @@ module EventMachine
40
35
  break unless (b & 0x80) == 0x80
41
36
  end
42
37
 
43
- # Addition to the spec to protect against malicious requests
44
- if length > MAXIMUM_FRAME_LENGTH
45
- raise DataError, "Frame length too long (#{length} bytes)"
38
+ if length > @connection.max_frame_size
39
+ raise WSMessageTooBigError, "Frame length too long (#{length} bytes)"
46
40
  end
47
41
 
48
42
  if @data.getbyte(pointer+length-1) == nil
@@ -69,12 +63,12 @@ module EventMachine
69
63
 
70
64
  if @data.getbyte(0) != 0x00
71
65
  # Close the connection since this buffer can never match
72
- raise DataError, "Invalid frame received"
66
+ raise WSProtocolError, "Invalid frame received"
73
67
  end
74
68
 
75
69
  # Addition to the spec to protect against malicious requests
76
- if @data.size > MAXIMUM_FRAME_LENGTH
77
- raise DataError, "Frame length too long (#{@data.size} bytes)"
70
+ if @data.size > @connection.max_frame_size
71
+ raise WSMessageTooBigError, "Frame length too long (#{@data.size} bytes)"
78
72
  end
79
73
 
80
74
  # Optimization to avoid calling slice! unnecessarily
@@ -15,6 +15,8 @@ module EventMachine
15
15
 
16
16
  lines = header.split("\r\n")
17
17
 
18
+ raise HandshakeError, "Empty HTTP header" unless lines.size > 0
19
+
18
20
  # extract request path
19
21
  first_line = lines.shift.match(PATH)
20
22
  raise HandshakeError, "Invalid HTTP header" unless first_line
@@ -99,7 +101,7 @@ module EventMachine
99
101
  Handler13.new(connection, request, debug)
100
102
  else
101
103
  # According to spec should abort the connection
102
- raise WebSocketError, "Protocol version #{version} not supported"
104
+ raise HandshakeError, "Protocol version #{version} not supported"
103
105
  end
104
106
  end
105
107
  end
@@ -29,7 +29,7 @@ module EventMachine
29
29
  end
30
30
 
31
31
  def getbytes(start_index, count)
32
- data = ''
32
+ data = ''.force_encoding('ASCII-8BIT')
33
33
  count.times do |i|
34
34
  data << getbyte(start_index + i)
35
35
  end
@@ -12,7 +12,7 @@ module EventMachine
12
12
  nil
13
13
  when 1
14
14
  # Illegal close frame
15
- raise DataError, "Close frames with a body must contain a 2 byte status code"
15
+ raise WSProtocolError, "Close frames with a body must contain a 2 byte status code"
16
16
  else
17
17
  application_data.slice!(0, 2).unpack('n').first
18
18
  end
@@ -20,8 +20,8 @@ module EventMachine
20
20
  debug [:close_frame_received, status_code, application_data]
21
21
 
22
22
  if @state == :closing
23
- # We can close connection immediately since there is no more data
24
- # is allowed to be sent or received on this connection
23
+ # We can close connection immediately since no more data may be
24
+ # sent or received on this connection
25
25
  @connection.close_connection
26
26
  @state = :closed
27
27
  else
@@ -1,5 +1,5 @@
1
1
  module EventMachine
2
2
  module Websocket
3
- VERSION = "0.3.6"
3
+ VERSION = "0.3.7"
4
4
  end
5
5
  end
@@ -1,8 +1,24 @@
1
1
  module EventMachine
2
2
  module WebSocket
3
+ # All errors raised by em-websocket should descend from this class
4
+ #
3
5
  class WebSocketError < RuntimeError; end
6
+
7
+ # Used for errors that occur during WebSocket handshake
8
+ #
4
9
  class HandshakeError < WebSocketError; end
5
- class DataError < WebSocketError; end
10
+
11
+ # Used for errors which should cause the connection to close.
12
+ # See RFC6455 §7.4.1 for a full description of meanings
13
+ #
14
+ class WSProtocolError < WebSocketError
15
+ def code; 1002; end
16
+ end
17
+
18
+ # 1009: Message too big to process
19
+ class WSMessageTooBigError < WSProtocolError
20
+ def code; 1009; end
21
+ end
6
22
 
7
23
  def self.start(options, &blk)
8
24
  EM.epoll
@@ -22,5 +38,10 @@ module EventMachine
22
38
  puts "Terminating WebSocket Server"
23
39
  EventMachine.stop
24
40
  end
41
+
42
+ class << self
43
+ attr_accessor :max_frame_size
44
+ end
45
+ @max_frame_size = 10 * 1024 * 1024 # 10MB
25
46
  end
26
47
  end
@@ -107,6 +107,7 @@ class Draft75WebSocketClient
107
107
  @ws.errback { @onerror.call if @onerror }
108
108
  @ws.callback { @onopen.call if @onopen }
109
109
  @ws.stream { |msg| @onmessage.call(msg) if @onmessage }
110
+ @ws.disconnect { @onclose.call if @onclose }
110
111
  end
111
112
 
112
113
  def send(message)
@@ -1,6 +1,6 @@
1
1
  require 'helper'
2
2
 
3
- # These tests are not specifi to any particular draft of the specification
3
+ # These tests are not specific to any particular draft of the specification
4
4
  #
5
5
  describe "WebSocket server" do
6
6
  include EM::SpecHelper
@@ -140,7 +140,7 @@ describe "WebSocket server draft76" do
140
140
  em {
141
141
  EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |server|
142
142
  server.onerror { |error|
143
- error.should be_an_instance_of EM::WebSocket::DataError
143
+ error.should be_an_instance_of EM::WebSocket::WSMessageTooBigError
144
144
  error.message.should == "Frame length too long (1180591620717411303296 bytes)"
145
145
  done
146
146
  }
@@ -164,7 +164,7 @@ describe "WebSocket server draft76" do
164
164
  em {
165
165
  EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |server|
166
166
  server.onerror { |error|
167
- error.should be_an_instance_of EM::WebSocket::DataError
167
+ error.should be_an_instance_of EM::WebSocket::WSProtocolError
168
168
  error.message.should == "Invalid frame received"
169
169
  done
170
170
  }
@@ -1,5 +1,7 @@
1
1
  # encoding: UTF-8
2
2
 
3
+ # These tests are run against all draft versions
4
+ #
3
5
  shared_examples_for "a websocket server" do
4
6
  it "should call onerror if an application error raised in onopen" do
5
7
  em {
@@ -62,6 +64,41 @@ shared_examples_for "a websocket server" do
62
64
  }
63
65
  end
64
66
 
67
+ it "should close the connection when a too long frame is sent" do
68
+ em {
69
+ start_server { |server|
70
+ server.max_frame_size = 20
71
+
72
+ server.onerror { |e|
73
+ # 3: Error should be reported to server
74
+ e.class.should == EventMachine::WebSocket::WSMessageTooBigError
75
+ e.message.should =~ /Frame length too long/
76
+ }
77
+ }
78
+
79
+ start_client { |client|
80
+ client.onopen {
81
+ EM.next_tick {
82
+ client.send("This message is longer than 20 characters")
83
+ }
84
+
85
+ }
86
+
87
+ client.onmessage { |msg|
88
+ # 4: This is actually the close message. Really need to use a real
89
+ # WebSocket client in these tests...
90
+ done
91
+ }
92
+
93
+ client.onclose {
94
+ # 4: Drafts 75 & 76 don't send a close message, they just close the
95
+ # connection
96
+ done
97
+ }
98
+ }
99
+ }
100
+ end
101
+
65
102
  # Only run these tests on ruby 1.9
66
103
  if "a".respond_to?(:force_encoding)
67
104
  it "should raise error if you try to send non utf8 text data to ws" do
@@ -87,5 +124,23 @@ shared_examples_for "a websocket server" do
87
124
  start_client { }
88
125
  }
89
126
  end
127
+
128
+ it "should not change the encoding of strings sent to send [antiregression]" do
129
+ em {
130
+ start_server { |server|
131
+ server.onopen {
132
+ s = "example string"
133
+ s.force_encoding("UTF-8")
134
+
135
+ server.send(s)
136
+
137
+ s.encoding.should == Encoding.find("UTF-8")
138
+ done
139
+ }
140
+ }
141
+
142
+ start_client { }
143
+ }
144
+ end
90
145
  end
91
146
  end
@@ -4,6 +4,13 @@ describe EM::WebSocket::Framing03 do
4
4
  class FramingContainer
5
5
  include EM::WebSocket::Framing03
6
6
 
7
+ def initialize
8
+ @connection = Object.new
9
+ def @connection.max_frame_size
10
+ 1000000
11
+ end
12
+ end
13
+
7
14
  def <<(data)
8
15
  @data << data
9
16
  process_data(data)
@@ -122,6 +129,13 @@ describe EM::WebSocket::Framing04 do
122
129
  class FramingContainer04
123
130
  include EM::WebSocket::Framing04
124
131
 
132
+ def initialize
133
+ @connection = Object.new
134
+ def @connection.max_frame_size
135
+ 1000000
136
+ end
137
+ end
138
+
125
139
  def <<(data)
126
140
  @data << data
127
141
  process_data(data)
@@ -184,6 +198,13 @@ describe EM::WebSocket::Framing07 do
184
198
  class FramingContainer07
185
199
  include EM::WebSocket::Framing07
186
200
 
201
+ def initialize
202
+ @connection = Object.new
203
+ def @connection.max_frame_size
204
+ 1000000
205
+ end
206
+ end
207
+
187
208
  def <<(data)
188
209
  @data << data
189
210
  process_data(data)
@@ -240,11 +261,11 @@ describe EM::WebSocket::Framing07 do
240
261
  end
241
262
 
242
263
  describe "other tests" do
243
- it "should raise a DataError if an invalid frame type is requested" do
264
+ it "should raise a WSProtocolError if an invalid frame type is requested" do
244
265
  lambda {
245
266
  # Opcode 3 is not supported by this draft
246
267
  @f << "\x83\x05Hello"
247
- }.should raise_error(EventMachine::WebSocket::DataError, "Unknown opcode")
268
+ }.should raise_error(EventMachine::WebSocket::WSProtocolError, "Unknown opcode")
248
269
  end
249
270
 
250
271
  it "should accept a fragmented unmasked text message in 3 frames" do
@@ -138,6 +138,13 @@ describe "EventMachine::WebSocket::Handler" do
138
138
  }.should raise_error(EM::WebSocket::HandshakeError, 'Invalid Key "12998 5 Y3 1.P00"')
139
139
  end
140
140
 
141
+ it "should raise error if the HTTP header is empty" do
142
+ connection = Object.new
143
+ lambda {
144
+ EM::WebSocket::HandlerFactory.build(connection, "\r\n\r\nfoobar", false)
145
+ }.should raise_error(EM::WebSocket::HandshakeError, "Empty HTTP header")
146
+ end
147
+
141
148
  it "should leave request with incomplete header" do
142
149
  data = format_request(@request)
143
150
  # Sends only half of the request
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: em-websocket
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.6
4
+ version: 0.3.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-12-23 00:00:00.000000000 Z
13
+ date: 2012-07-11 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: eventmachine
17
- requirement: &70364656209880 !ruby/object:Gem::Requirement
17
+ requirement: &70199777824640 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 0.12.9
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *70364656209880
25
+ version_requirements: *70199777824640
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: addressable
28
- requirement: &70364656231520 !ruby/object:Gem::Requirement
28
+ requirement: &70199777824040 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,21 +33,21 @@ dependencies:
33
33
  version: 2.1.1
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *70364656231520
36
+ version_requirements: *70199777824040
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: em-spec
39
- requirement: &70364656231060 !ruby/object:Gem::Requirement
39
+ requirement: &70199777823420 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ~>
43
43
  - !ruby/object:Gem::Version
44
- version: 0.2.5
44
+ version: 0.2.6
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *70364656231060
47
+ version_requirements: *70199777823420
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: eventmachine
50
- requirement: &70364656230580 !ruby/object:Gem::Requirement
50
+ requirement: &70199777822580 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ~>
@@ -55,10 +55,10 @@ dependencies:
55
55
  version: 0.12.10
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *70364656230580
58
+ version_requirements: *70199777822580
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: em-http-request
61
- requirement: &70364656230100 !ruby/object:Gem::Requirement
61
+ requirement: &70199777821520 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
64
  - - ~>
@@ -66,21 +66,21 @@ dependencies:
66
66
  version: 0.2.6
67
67
  type: :development
68
68
  prerelease: false
69
- version_requirements: *70364656230100
69
+ version_requirements: *70199777821520
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: rspec
72
- requirement: &70364656229640 !ruby/object:Gem::Requirement
72
+ requirement: &70199777818940 !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
75
  - - ~>
76
76
  - !ruby/object:Gem::Version
77
- version: 2.6.0
77
+ version: 2.8.0
78
78
  type: :development
79
79
  prerelease: false
80
- version_requirements: *70364656229640
80
+ version_requirements: *70199777818940
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: rake
83
- requirement: &70364656229260 !ruby/object:Gem::Requirement
83
+ requirement: &70199777817860 !ruby/object:Gem::Requirement
84
84
  none: false
85
85
  requirements:
86
86
  - - ! '>='
@@ -88,7 +88,7 @@ dependencies:
88
88
  version: '0'
89
89
  type: :development
90
90
  prerelease: false
91
- version_requirements: *70364656229260
91
+ version_requirements: *70199777817860
92
92
  description: EventMachine based WebSocket server
93
93
  email:
94
94
  - ilya@igvita.com
@@ -191,3 +191,4 @@ test_files:
191
191
  - spec/unit/framing_spec.rb
192
192
  - spec/unit/handler_spec.rb
193
193
  - spec/unit/masking_spec.rb
194
+ has_rdoc: