skinny 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +6 -2
- data/lib/skinny.rb +255 -94
- metadata +62 -69
- data/.gitignore +0 -1
- data/Rakefile +0 -33
- 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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
152
|
-
"Connection: Upgrade\r\n"
|
153
|
-
"Upgrade: WebSocket\r\n"
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
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
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
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
|
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
|
185
|
-
if @buffer
|
186
|
-
if
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
if @buffer
|
200
|
-
@buffer
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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
|
-
|
208
|
-
|
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
|
-
|
220
|
-
|
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
|
-
|
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
|
-
|
230
|
-
|
231
|
-
|
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
|
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
|
-
|
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'] == '
|
425
|
+
env['HTTP_CONNECTION'].downcase == 'upgrade' && env['HTTP_UPGRADE'].downcase == 'websocket'
|
265
426
|
end
|
266
|
-
|
267
|
-
def websocket
|
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!
|
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
|
-
|
5
|
-
prerelease:
|
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
|
-
|
19
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
44
|
-
segments:
|
45
|
-
- 0
|
46
|
-
version: "0"
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.2'
|
47
33
|
type: :runtime
|
48
|
-
|
49
|
-
|
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
|
-
-
|
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
|
-
|
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
|
-
|
80
|
-
|
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
|
-
|
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.
|
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
|