skinny 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/README.md +6 -2
  2. data/lib/skinny.rb +255 -94
  3. metadata +62 -69
  4. data/.gitignore +0 -1
  5. data/Rakefile +0 -33
  6. data/VERSION +0 -1
data/README.md CHANGED
@@ -16,7 +16,7 @@ simple, not-yet-optimised example I'm using at the moment:
16
16
  class Sinatra::Request
17
17
  include Skinny::Helpers
18
18
  end
19
-
19
+
20
20
  module MailCatcher
21
21
  class Web < Sinatra::Base
22
22
  get '/messages' do
@@ -34,7 +34,7 @@ simple, not-yet-optimised example I'm using at the moment:
34
34
  end
35
35
  end
36
36
  end
37
-
37
+
38
38
  This syntax will probably get cleaned up. I would like to build a
39
39
  nice Sinatra handler with DSL with unbound handlers so Sinatra
40
40
  requests can be recycled.
@@ -46,6 +46,10 @@ requests can be recycled.
46
46
  * Tests
47
47
  * Make more generic for alternate server implementations?
48
48
 
49
+ ## Thanks
50
+
51
+ The latest WebSocket draft support is adapted from https://github.com/gimite/web-socket-ruby -- thank you!
52
+
49
53
  ## Copyright
50
54
 
51
55
  Copyright (c) 2010 Samuel Cochran. See LICENSE for details.
data/lib/skinny.rb CHANGED
@@ -10,7 +10,7 @@ module Skinny
10
10
  include InstanceMethods
11
11
  end
12
12
  end
13
-
13
+
14
14
  module ClassMethods
15
15
  def define_callback *names
16
16
  names.each do |name|
@@ -20,7 +20,7 @@ module Skinny
20
20
  end
21
21
  end
22
22
  end
23
-
23
+
24
24
  module InstanceMethods
25
25
  def add_callback name, &block
26
26
  @callbacks ||= {}
@@ -37,18 +37,27 @@ module Skinny
37
37
 
38
38
  class WebSocketError < RuntimeError; end
39
39
  class WebSocketProtocolError < WebSocketError; end
40
-
40
+
41
41
  # We need to be really careful not to throw an exception too high
42
42
  # or we'll kill the server.
43
43
  class Websocket < EventMachine::Connection
44
44
  include Callbacks
45
45
  include Thin::Logging
46
-
46
+
47
47
  define_callback :on_open, :on_start, :on_handshake, :on_message, :on_error, :on_finish, :on_close
48
48
 
49
49
  # 4mb is almost too generous, imho.
50
50
  MAX_BUFFER_LENGTH = 2 ** 32
51
-
51
+
52
+ GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
53
+
54
+ OPCODE_CONTINUATION = 0x00
55
+ OPCODE_TEXT = 0x01
56
+ OPCODE_BINARY = 0x02
57
+ OPCODE_CLOSE = 0x08
58
+ OPCODE_PING = 0x09
59
+ OPCODE_PONG = 0x0a
60
+
52
61
  # Create a new WebSocket from a Thin::Request environment
53
62
  def self.from_env env, options={}
54
63
  # Pull the connection out of the env
@@ -58,31 +67,31 @@ module Skinny
58
67
  # We have all the events now, muahaha
59
68
  EM.attach(io, self, env, options)
60
69
  end
61
-
70
+
62
71
  def initialize env, options={}
63
72
  @env = env.dup
64
73
  @buffer = ''
65
-
66
- self.protocol = options.delete :protocol if options.has_key? :protocol
74
+
75
+ @protocol = options.delete :protocol if options.has_key? :protocol
67
76
  [:on_open, :on_start, :on_handshake, :on_message, :on_error, :on_finish, :on_close].each do |name|
68
77
  send name, &options.delete(name) if options.has_key?(name)
69
78
  end
70
79
  raise ArgumentError, "Unknown options: #{options.inspect}" unless options.empty?
71
80
  end
72
81
 
73
- # Connection is now open
82
+ # Connection is now open
74
83
  def post_init
75
84
  EM.next_tick { callback :on_open, self rescue error! "Error in open callback" }
76
85
  @state = :open
77
86
  rescue
78
87
  error! "Error opening connection"
79
88
  end
80
-
89
+
81
90
  # Return an async response -- stops Thin doing anything with connection.
82
91
  def response
83
92
  Thin::Connection::AsyncResponse
84
93
  end
85
-
94
+
86
95
  # Arrayify self into a response tuple
87
96
  alias :to_a :response
88
97
 
@@ -90,189 +99,341 @@ module Skinny
90
99
  def start!
91
100
  # Steal any remaining data from rack.input
92
101
  @buffer = @env[Thin::Request::RACK_INPUT].read + @buffer
93
-
102
+
94
103
  # Remove references to Thin connection objects, freeing memory
95
104
  @env.delete Thin::Request::RACK_INPUT
96
105
  @env.delete Thin::Request::ASYNC_CALLBACK
97
106
  @env.delete Thin::Request::ASYNC_CLOSE
98
107
 
108
+ # Figure out which version we're using
109
+ @version = @env['HTTP_SEC_WEBSOCKET_VERSION']
110
+ @version ||= "hixie-76" if @env.has_key?('HTTP_SEC_WEBSOCKET_KEY1') and @env.has_key?('HTTP_SEC_WEBSOCKET_KEY2')
111
+ @version ||= "hixie-75"
112
+
99
113
  # Pull out the details we care about
100
- @origin ||= @env['HTTP_ORIGIN']
114
+ @origin ||= @env['HTTP_SEC_WEBSOCKET_ORIGIN'] || @env['HTTP_ORIGIN']
101
115
  @location ||= "ws#{secure? ? 's' : ''}://#{@env['HTTP_HOST']}#{@env['REQUEST_PATH']}"
102
- @protocol ||= @env['HTTP_SEC_WEBSOCKET_PROTOCOL']
103
-
116
+ @protocol ||= @env['HTTP_SEC_WEBSOCKET_PROTOCOL'] || @env['HTTP_WEBSOCKET_PROTOCOL']
117
+
104
118
  EM.next_tick { callback :on_start, self rescue error! "Error in start callback" }
105
-
119
+
106
120
  # Queue up the actual handshake
107
121
  EM.next_tick method :handshake!
108
-
122
+
109
123
  @state = :started
110
-
124
+
111
125
  # Return self so we can be used as a response
112
126
  self
113
127
  rescue
114
128
  error! "Error starting connection"
115
129
  end
116
-
117
- attr_reader :env
118
- attr_accessor :origin, :location, :protocol
119
-
130
+
131
+ attr_reader :env, :version, :origin, :location, :protocol
132
+
133
+ def hixie_75?
134
+ @version == "hixie-75"
135
+ end
136
+
137
+ def hixie_76?
138
+ @version == "hixie-76"
139
+ end
140
+
120
141
  def secure?
121
142
  @env['HTTPS'] == 'on' or
143
+ # XXX: This could be faked... do we care?
122
144
  @env['HTTP_X_FORWARDED_PROTO'] == 'https' or
123
145
  @env['rack.url_scheme'] == 'https'
124
146
  end
125
147
 
148
+ def key
149
+ @env['HTTP_SEC_WEBSOCKET_KEY']
150
+ end
151
+
126
152
  [1, 2].each do |i|
127
153
  define_method :"key#{i}" do
128
154
  key = env["HTTP_SEC_WEBSOCKET_KEY#{i}"]
129
155
  key.scan(/[0-9]/).join.to_i / key.count(' ')
130
156
  end
131
157
  end
132
-
158
+
133
159
  def key3
134
160
  @key3 ||= @buffer.slice!(0...8)
135
161
  end
136
-
162
+
137
163
  def challenge?
138
164
  env.has_key? 'HTTP_SEC_WEBSOCKET_KEY1'
139
165
  end
140
-
166
+
141
167
  def challenge
142
- [key1, key2].pack("N*") + key3
168
+ if hixie_75?
169
+ nil
170
+ elsif hixie_76?
171
+ [key1, key2].pack("N*") + key3
172
+ else
173
+ key + GUID
174
+ end
143
175
  end
144
-
176
+
145
177
  def challenge_response
146
- Digest::MD5.digest(challenge)
178
+ if hixie_75?
179
+ nil
180
+ elsif hixie_76?
181
+ Digest::MD5.digest(challenge)
182
+ else
183
+ ActiveSupport::Base64.encode64(Digest::SHA1.digest(challenge)).strip
184
+ end
147
185
  end
148
-
186
+
149
187
  # Generate the handshake
150
188
  def handshake
151
- "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" +
152
- "Connection: Upgrade\r\n" +
153
- "Upgrade: WebSocket\r\n" +
154
- "Sec-WebSocket-Location: #{location}\r\n" +
155
- "Sec-WebSocket-Origin: #{origin}\r\n" +
156
- (protocol ? "Sec-WebSocket-Protocol: #{protocol}\r\n" : "") +
157
- "\r\n" +
158
- "#{challenge_response}"
189
+ "HTTP/1.1 101 Switching Protocols\r\n" <<
190
+ "Connection: Upgrade\r\n" <<
191
+ "Upgrade: WebSocket\r\n" <<
192
+ if hixie_75?
193
+ "WebSocket-Location: #{location}\r\n" <<
194
+ "WebSocket-Origin: #{origin}\r\n"
195
+ elsif hixie_76?
196
+ "Sec-WebSocket-Location: #{location}\r\n" <<
197
+ "Sec-WebSocket-Origin: #{origin}\r\n"
198
+ else
199
+ "Sec-WebSocket-Accept: #{challenge_response}\r\n"
200
+ end <<
201
+ (protocol ? "Sec-WebSocket-Protocol: #{protocol}\r\n" : "") <<
202
+ "\r\n" <<
203
+ (if hixie_76? then challenge_response else "" end)
159
204
  end
160
-
205
+
161
206
  def handshake!
162
- [key1, key2].each { |key| raise WebSocketProtocolError, "Invalid key: #{key}" if key >= 2**32 }
163
-
164
- # XXX: Should we wait for 8 bytes?
165
- raise WebSocketProtocolError, "Invalid challenge: #{key3}" if key3.length < 8
166
-
207
+ if hixie_76?
208
+ [key1, key2].each { |key| raise WebSocketProtocolError, "Invalid key: #{key}" if key >= 2**32 }
209
+ raise WebSocketProtocolError, "Invalid challenge: #{key3}" if key3.length < 8
210
+ end
211
+
167
212
  send_data handshake
213
+
168
214
  @state = :handshook
169
-
215
+
170
216
  EM.next_tick { callback :on_handshake, self rescue error! "Error in handshake callback" }
171
217
  rescue
172
218
  error! "Error during WebSocket connection handshake"
173
219
  end
174
-
220
+
175
221
  def receive_data data
176
- @buffer += data
177
-
222
+ @buffer << data
223
+
178
224
  EM.next_tick { process_frame } if @state == :handshook
179
225
  rescue
180
226
  error! "Error while receiving WebSocket data"
181
227
  end
182
-
228
+
229
+ def mask payload, mask_key
230
+ payload.unpack("C*").map.with_index do |byte, index|
231
+ byte ^ mask_key[index % 4]
232
+ end.pack("C*")
233
+ end
234
+
183
235
  def process_frame
184
- if @buffer.length >= 1
185
- if @buffer[0].ord < 0x7f
186
- if ending = @buffer.index("\xff")
187
- frame = @buffer.slice! 0..ending
188
- message = frame[1..-2]
189
-
190
- EM.next_tick { receive_message message }
191
-
192
- # There might be more frames to process
193
- EM.next_tick { process_frame }
194
- elsif @buffer.length > MAX_BUFFER_LENGTH
195
- raise WebSocketProtocolError, "Maximum buffer length (#{MAX_BUFFER_LENGTH}) exceeded: #{@buffer.length}"
196
- end
197
- elsif @buffer[0] == "\xff"
198
- if @buffer.length > 1
199
- if @buffer[1] == "\x00"
200
- @buffer.slice! 0..1
201
-
202
- EM.next_tick { finish! }
203
- else
204
- raise WebSocketProtocolError, "Incorrect finish frame length: #{@buffer[1].inspect}"
236
+ if hixie_75? or hixie_76?
237
+ if @buffer.length >= 1
238
+ if @buffer[0].ord < 0x7f
239
+ if ending = @buffer.index("\xff")
240
+ frame = @buffer.slice! 0..ending
241
+ message = frame[1..-2]
242
+
243
+ EM.next_tick { receive_message message }
244
+
245
+ # There might be more frames to process
246
+ EM.next_tick { process_frame }
247
+ elsif @buffer.length > MAX_BUFFER_LENGTH
248
+ raise WebSocketProtocolError, "Maximum buffer length (#{MAX_BUFFER_LENGTH}) exceeded: #{@buffer.length}"
249
+ end
250
+ elsif @buffer[0] == "\xff"
251
+ if @buffer.length > 1
252
+ if @buffer[1] == "\x00"
253
+ @buffer.slice! 0..1
254
+
255
+ EM.next_tick { finish! }
256
+ else
257
+ raise WebSocketProtocolError, "Incorrect finish frame length: #{@buffer[1].inspect}"
258
+ end
205
259
  end
260
+ else
261
+ raise WebSocketProtocolError, "Unknown frame type: #{@buffer[0].inspect}"
206
262
  end
207
- else
208
- raise WebSocketProtocolError, "Unknown frame type: #{@buffer[0].inspect}"
263
+ end
264
+ else
265
+ @frame_state ||= :opcode
266
+
267
+ if @frame_state == :opcode
268
+ return unless @buffer.length >= 2
269
+
270
+ bytes = @buffer.slice!(0...2).unpack("C*")
271
+
272
+ @opcode = bytes[0] & 0x0f
273
+ @fin = (bytes[0] & 0x80) != 0
274
+ @payload_length = bytes[1] & 0x7f
275
+ @masked = (bytes[1] & 0x80) != 0
276
+
277
+ return error! "Received unmasked data" unless @masked
278
+
279
+ if @payload_length == 126
280
+ @frame_state = :payload_2
281
+ elsif @payload_length == 127
282
+ @frame_state = :payload_8
283
+ else
284
+ @frame_state = :payload
285
+ end
286
+
287
+ elsif @frame_state == :payload_2
288
+ return unless @buffer.length >= 2
289
+
290
+ @payload_length = @buffer.slice!(0...2).unpack("n")[0]
291
+
292
+ @frame_state = :mask
293
+
294
+ elsif @frame_state == :payload_8
295
+ return unless @buffer.length >= 8
296
+
297
+ (high, low) = @buffer.slice!(0...8).unpack("NN")
298
+ @payload_length = high * (2 ** 32) + low
299
+
300
+ @frame_state = :mask
301
+
302
+ elsif @frame_state == :mask
303
+ return unless @buffer.length >= 4
304
+
305
+ bytes = @buffer[(offset)...(offset += 4)]
306
+ @mask_key = bytes.unpack("C*")
307
+
308
+ @frame_state = :payload
309
+
310
+ elsif @frame_state == :payload
311
+ return unless @buffer.length >= @payload_length
312
+
313
+ payload = @buffer.slice!(0...@payload_length)
314
+ payload = mask(payload, @mask_key)
315
+
316
+ if @opcode == OPCODE_TEXT
317
+ message = payload.force_encoding("UTF-8") if payload.respond_to? :force_encoding
318
+ EM.next_tick { receive_message payload }
319
+ elsif @opcode == OPCODE_CLOSE
320
+ EM.next_tick { finish! }
321
+ else
322
+ error! "Unsupported opcode: %d" % @opcode
323
+ end
324
+
325
+ @frame_state = nil
326
+ @opcode = @fin = @payload_length = @masked = nil
209
327
  end
210
328
  end
211
329
  rescue
212
330
  error! "Error while processing WebSocket frames"
213
331
  end
214
-
332
+
215
333
  def receive_message message
216
334
  EM.next_tick { callback :on_message, self, message rescue error! "Error in message callback" }
217
335
  end
218
-
219
- def frame_message message
220
- "\x00#{message}\xff"
336
+
337
+ # This is for post-hixie-76 versions only
338
+ def send_frame opcode, payload="", masked=false
339
+ payload = payload.dup.force_encoding("ASCII-8BIT") if payload.respond_to? :force_encoding
340
+ payload_length = payload.bytesize
341
+
342
+ # We don't support continuations (yet), so always send fin
343
+ fin_byte = 0x80
344
+ send_data [fin_byte | opcode].pack("C")
345
+
346
+ # We shouldn't be sending mask, we're a server only
347
+ masked_byte = masked ? 0x80 : 0x00
348
+
349
+ if payload_length <= 125
350
+ send_data [masked_byte | payload_length].pack("C")
351
+
352
+ elsif payload_length < 2 ** 16
353
+ send_data [masked_byte | 126].pack("C")
354
+ send_data [payload_length].pack("n")
355
+
356
+ else
357
+ send_data [masked_byte | 127].pack("C")
358
+ send_data [payload_length / (2 ** 32), payload_length % (2 ** 32)].pack("NN")
359
+ end
360
+
361
+ if payload_length
362
+ if masked
363
+ mask_key = Array.new(4) { rand(256) }.pack("C*")
364
+ send_data mask_key
365
+ payload = mask payload, mask_key
366
+ end
367
+
368
+ send_data payload
369
+ end
221
370
  end
222
-
371
+
223
372
  def send_message message
224
- send_data frame_message(message)
373
+ if hixie_75? or hixie_76?
374
+ send_data "\x00#{message}\xff"
375
+ else
376
+ send_frame OPCODE_TEXT, message
377
+ end
225
378
  end
226
-
379
+
227
380
  # Finish the connection read for closing
228
381
  def finish!
229
- send_data "\xff\x00"
230
-
231
- EM.next_tick { callback :on_finish, self rescue error! "Error in finish callback" }
382
+ if hixie_75? or hixie_76?
383
+ send_data "\xff\x00"
384
+ else
385
+ send_frame OPCODE_CLOSE
386
+ end
387
+
388
+ EM.next_tick { callback(:on_finish, self) rescue error! "Error in finish callback" }
232
389
  EM.next_tick { close_connection_after_writing }
233
390
 
234
391
  @state = :finished
235
392
  rescue
236
393
  error! "Error finishing WebSocket connection"
237
394
  end
238
-
395
+
239
396
  # Make sure we call the on_close callbacks when the connection
240
397
  # disappears
241
398
  def unbind
242
- EM.next_tick { callback :on_close, self rescue error! "Error in close callback" }
399
+ EM.next_tick { callback(:on_close, self) rescue error! "Error in close callback" }
243
400
  @state = :closed
244
401
  rescue
245
402
  error! "Error closing WebSocket connection"
246
403
  end
247
-
248
- def error! message=nil
404
+
405
+ def error! message=nil, callback=true
249
406
  log message unless message.nil?
250
- log_error
251
-
407
+ log_error # Logs the exception itself
408
+
252
409
  # Allow error messages to be handled, maybe
253
- EM.next_tick { callback :on_error, self rescue error! "Error in error callback" }
254
-
410
+ # but only if this error was not caused by the error callback
411
+ if callback
412
+ EM.next_tick { callback(:on_error, self) rescue error! "Error in error callback", true }
413
+ end
414
+
255
415
  # Try to finish and close nicely.
256
416
  EM.next_tick { finish! } unless [:finished, :closed, :error].include? @state
257
417
 
418
+ # We're closed!
258
419
  @state = :error
259
420
  end
260
421
  end
261
422
 
262
423
  module Helpers
263
424
  def websocket?
264
- env['HTTP_CONNECTION'] == 'Upgrade' && env['HTTP_UPGRADE'] == 'WebSocket'
425
+ env['HTTP_CONNECTION'].downcase == 'upgrade' && env['HTTP_UPGRADE'].downcase == 'websocket'
265
426
  end
266
-
267
- def websocket(options={}, &block)
427
+
428
+ def websocket options={}, &block
268
429
  env['skinny.websocket'] ||= begin
269
430
  raise RuntimerError, "Not a WebSocket request" unless websocket?
270
431
  options[:on_message] = block if block_given?
271
432
  Websocket.from_env(env, options)
272
433
  end
273
434
  end
274
-
275
- def websocket!(options={}, &block)
435
+
436
+ def websocket! options={}, &block
276
437
  websocket(options, &block).start!
277
438
  end
278
439
  end
metadata CHANGED
@@ -1,100 +1,93 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: skinny
3
- version: !ruby/object:Gem::Version
4
- hash: 31
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 1
9
- - 2
10
- version: 0.1.2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Samuel Cochran
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2010-11-01 00:00:00 +08:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
12
+ date: 2010-11-01 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
22
15
  name: eventmachine
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &70337454861360 !ruby/object:Gem::Requirement
25
17
  none: false
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- hash: 3
30
- segments:
31
- - 0
32
- version: "0"
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '0.12'
33
22
  type: :runtime
34
- version_requirements: *id001
35
- - !ruby/object:Gem::Dependency
36
- name: thin
37
23
  prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *70337454861360
25
+ - !ruby/object:Gem::Dependency
26
+ name: thin
27
+ requirement: &70337454860420 !ruby/object:Gem::Requirement
39
28
  none: false
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- hash: 3
44
- segments:
45
- - 0
46
- version: "0"
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '1.2'
47
33
  type: :runtime
48
- version_requirements: *id002
49
- description: " Simple, upgradable WebSockets for Thin.\n"
34
+ prerelease: false
35
+ version_requirements: *70337454860420
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &70337454859940 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70337454859940
47
+ - !ruby/object:Gem::Dependency
48
+ name: rdoc
49
+ requirement: &70337454859320 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70337454859320
58
+ description: Simple, upgradable WebSockets for Thin.
50
59
  email: sj26@sj26.com
51
60
  executables: []
52
-
53
61
  extensions: []
54
-
55
- extra_rdoc_files:
56
- - LICENSE
62
+ extra_rdoc_files:
57
63
  - README.md
58
- files:
59
- - .gitignore
60
64
  - LICENSE
65
+ files:
61
66
  - README.md
62
- - Rakefile
63
- - VERSION
67
+ - LICENSE
64
68
  - lib/skinny.rb
65
- has_rdoc: true
66
69
  homepage: http://github.com/sj26/skinny
67
70
  licenses: []
68
-
69
71
  post_install_message:
70
- rdoc_options:
71
- - --charset=UTF-8
72
- require_paths:
72
+ rdoc_options: []
73
+ require_paths:
73
74
  - lib
74
- required_ruby_version: !ruby/object:Gem::Requirement
75
+ required_ruby_version: !ruby/object:Gem::Requirement
75
76
  none: false
76
- requirements:
77
- - - ">="
78
- - !ruby/object:Gem::Version
79
- hash: 3
80
- segments:
81
- - 0
82
- version: "0"
83
- required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: 1.8.7
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
82
  none: false
85
- requirements:
86
- - - ">="
87
- - !ruby/object:Gem::Version
88
- hash: 3
89
- segments:
90
- - 0
91
- version: "0"
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
92
87
  requirements: []
93
-
94
88
  rubyforge_project:
95
- rubygems_version: 1.3.7
89
+ rubygems_version: 1.8.7
96
90
  signing_key:
97
91
  specification_version: 3
98
92
  summary: Thin WebSockets
99
93
  test_files: []
100
-
data/.gitignore DELETED
@@ -1 +0,0 @@
1
- pkg
data/Rakefile DELETED
@@ -1,33 +0,0 @@
1
- require 'rubygems'
2
- require 'rake'
3
-
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- gem.name = "skinny"
8
- gem.summary = %Q{Thin WebSockets}
9
- gem.description = <<-EOD
10
- Simple, upgradable WebSockets for Thin.
11
- EOD
12
- gem.email = "sj26@sj26.com"
13
- gem.homepage = "http://github.com/sj26/skinny"
14
- gem.authors = ["Samuel Cochran"]
15
-
16
- gem.add_dependency 'eventmachine'
17
- gem.add_dependency 'thin'
18
- end
19
- Jeweler::GemcutterTasks.new
20
- rescue LoadError
21
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
22
- end
23
-
24
- require 'rake/rdoctask'
25
- Rake::RDocTask.new do |rdoc|
26
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
27
-
28
- rdoc.rdoc_dir = 'rdoc'
29
- rdoc.title = "skinny #{version}"
30
- rdoc.rdoc_files.include('README*')
31
- rdoc.rdoc_files.include('lib/*.rb')
32
- rdoc.rdoc_files.include('lib/**/*.rb')
33
- end
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.1.2