em-websocket 0.3.6 → 0.3.7

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,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: