skinny 0.1.2 → 0.2.0

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