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