unixrack 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 24089d0352a60774bd7ab3c4f86d2b245fd879fe
4
+ data.tar.gz: fa77688738bec139120012adbc69727ce6eed88b
5
+ SHA512:
6
+ metadata.gz: f7603bef14c0a2c9104e82ef4133e1229379023f9b4da86a4d53e1f1f81da70f94b4aff01c209ded1919d825f5693e295c0f135a20d25fcc3ce050920bae4b7a
7
+ data.tar.gz: d32cecd6cfe640938927d0eee878a9528bec5a0dca8e7e7ea251bc204331c609c234e04141740cbfe6ed36946583c9ce4d6d49d2a2a204b698bdea774ade6218
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in unixrack.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ (This is an MIT style license)
2
+
3
+ Copyright (c) 2009 Dru Nelson http://xxeo.com/
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to
7
+ deal in the Software without restriction, including without limitation the
8
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9
+ sell copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # unixrack
2
+
3
+ A ruby RACK webserver only for unix using the old unix style.
4
+ We developed and used it in production at Brightroll.com.
5
+ We recommend it for any small rack, sinatra, etc app that needs to have
6
+ a high uptime. It is great for production use as well as development.
7
+
8
+ ## License
9
+
10
+ see LICENSE.txt (basically MIT license)
11
+
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'unixrack'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install unixrack
26
+
27
+ ## Sample sinatra bring up
28
+
29
+ put instructions here
30
+
31
+ $ gem install sinatra
32
+ $ gem build unixrack.gemspec
33
+ $ gem install unirack*gem
34
+
35
+ #!/usr/bin/ruby
36
+
37
+ require 'rubygems'
38
+ require 'sinatra/base'
39
+ require 'unixrack'
40
+
41
+ class MyApp < Sinatra::Base
42
+ get '/' do
43
+ "Hello"
44
+ end
45
+ end
46
+
47
+ Rack::Handler::UnixRack.run(MyApp.new)
48
+
49
+ ## Usage
50
+
51
+ Here is an example command to run the standard Rack lobster web app.
52
+ You can get the sample in the samples directory from the github repo
53
+ at: https://github.com/drudru/unixrack. Copy lobster.ru into your current dir
54
+ then run:
55
+
56
+ $ RACK_ENV=stage rackup --port 4004 -s unixrack -r unixrack lobster.ru
57
+
58
+ To run your own application, just create your own rackup (.ru) file with your
59
+ own Rack middleware.
60
+
61
+ ## Contributing
62
+
63
+ 1. Fork it
64
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
65
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
66
+ 4. Push to the branch (`git push origin my-new-feature`)
67
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,3 @@
1
+ module Unixrack
2
+ VERSION = "1.0.1"
3
+ end
data/lib/unixrack.rb ADDED
@@ -0,0 +1,498 @@
1
+ require "unixrack/version"
2
+
3
+ # unixrack - ruby webserver compatible with rack using old unix style
4
+ #
5
+ # license - see COPYING
6
+
7
+ require 'rack/content_length'
8
+ if not Rack.const_defined?("Handler")
9
+ require 'rack/handler'
10
+ end
11
+ require 'time'
12
+ require 'socket'
13
+ require 'stringio'
14
+
15
+ module UnixRack
16
+
17
+ module Alarm
18
+ # Thx - Logan Capaldo
19
+ case RUBY_VERSION.to_f
20
+ when 1.8
21
+ require 'dl/import'
22
+ extend DL::Importable
23
+ when 1.9
24
+ require 'dl/import'
25
+ extend DL::Importer
26
+ else # 2.0+
27
+ require 'fiddle/import'
28
+ extend Fiddle::Importer
29
+ end
30
+ if RUBY_PLATFORM =~ /darwin/
31
+ so_ext = 'dylib'
32
+ else
33
+ so_ext = 'so.6'
34
+ end
35
+ dlload "libc.#{so_ext}"
36
+ extern "unsigned int alarm(unsigned int)"
37
+ end
38
+
39
+ class Socket
40
+
41
+ attr_accessor :hdr_method, :headers
42
+ attr_accessor :sock
43
+
44
+ def initialize(sock)
45
+ TCPSocket.do_not_reverse_lookup = true
46
+ @sock = sock
47
+ @buff = ""
48
+ end
49
+
50
+ def peeraddr
51
+ @sock.peeraddr
52
+ end
53
+
54
+ def self.write_buff(io, buff)
55
+ len = buff.length
56
+ nwritten = 0
57
+
58
+ out_buff = buff
59
+
60
+ while true
61
+ nw = io.syswrite(out_buff)
62
+ nwritten = nwritten + nw
63
+ break if nw == out_buff.length
64
+ out_buff = out_buff.slice(nw..-1)
65
+ end
66
+ nwritten
67
+ end
68
+
69
+ def self.close(io)
70
+ io.close
71
+ end
72
+
73
+ def self.read_sock_num_bytes(sock, num, log = lambda { |x| x })
74
+ retval = [false, '']
75
+ buff = ""
76
+ num_left = num
77
+ while true
78
+ begin
79
+ #log("PRE Doing sysread")
80
+ numr = num_left < 32768 ? num_left : 32768
81
+ dat = sock.sysread(numr)
82
+ #log("Doing sysread #{dat.inspect}")
83
+ buff = buff + dat
84
+ if buff.length == num
85
+ retval = [true, buff]
86
+ break
87
+ else
88
+ num_left = num_left - dat.length
89
+ end
90
+ rescue Errno::EINTR # Ruby threading can cause an alarm/timer interrupt on a syscall
91
+ retry
92
+ rescue EOFError
93
+ retval = [false, "EOF", buff]
94
+ break
95
+ rescue EOFError, Errno::ECONNRESET, Errno::EPIPE, Errno::EINVAL, Errno::EBADF
96
+ retval = [false, "Exception occurred on socket read"]
97
+ #log("Got an #{$!} from socket read")
98
+ break
99
+ end
100
+ end
101
+ retval
102
+ end
103
+
104
+
105
+ def do_read
106
+ begin
107
+ dat = @sock.sysread(16384)
108
+ rescue Errno::EINTR # Ruby threading can cause an alarm/timer interrupt on a syscall
109
+ retry
110
+ rescue EOFError
111
+ puts "#{$$}: Got an EOF from socket read"
112
+ $stdout.flush
113
+ return nil
114
+ rescue EOFError, Errno::ECONNRESET, Errno::EPIPE, Errno::EINVAL, Errno::EBADF
115
+ puts "#{$$}: Got an #{$!} from socket read"
116
+ $stdout.flush
117
+ exit! 0
118
+ end
119
+ @buff = @buff + dat
120
+ return @buff
121
+ end
122
+
123
+ def read_content
124
+ cont_len = @headers['Content-Length'].to_i
125
+
126
+ #if cont_len < (8 * 1024 * 1024)
127
+ if cont_len < (1024 * 1024)
128
+ # Small file, just use mem
129
+ while true
130
+ if @buff.length == cont_len
131
+ f = StringIO.new(@buff)
132
+ @buff = ""
133
+ return f
134
+ end
135
+ return nil if not do_read
136
+ end
137
+ else
138
+ # Large file, use disk
139
+ f = nil
140
+ tmpname = "./.__tmp__unixrack_upload__#{$$}.tmp"
141
+ begin
142
+ tmpfd = IO.sysopen(tmpname, File::RDWR | File::CREAT | File::EXCL, 0600)
143
+ f = IO.new(tmpfd, "w+")
144
+ File.unlink(tmpname)
145
+ rescue
146
+ p $!
147
+ $stdout.flush
148
+ return nil
149
+ end
150
+
151
+ # Write what we already have
152
+ len = @buff.length
153
+ if len > 0
154
+ f.syswrite(@buff)
155
+ @buff = ""
156
+ end
157
+
158
+ while true
159
+ if len == cont_len
160
+ f.rewind
161
+ return f
162
+ end
163
+
164
+ return nil if not do_read
165
+
166
+ len += @buff.length
167
+ f.syswrite(@buff)
168
+ @buff = ""
169
+ end
170
+ end
171
+ end
172
+
173
+ def read_headers
174
+ while true
175
+ r = do_read
176
+ break if @buff.index "\r\n\r\n"
177
+ return false if r == nil
178
+ end
179
+
180
+ a, b = @buff.split("\r\n\r\n", 2)
181
+
182
+ @buff = b
183
+ @hdr_buff = a
184
+ @hdr_lines = @hdr_buff.split("\r\n")
185
+
186
+ @hdr_method_line = @hdr_lines[0]
187
+ @hdr_method = @hdr_method_line.split(" ")
188
+
189
+ @hdr_field_lines = @hdr_lines.slice(1..-1) # would prefer first, and rest
190
+ headers = @hdr_field_lines.inject({}) { |h, line| k, v = line.split(": "); h[k] = v; h }
191
+ @headers = canonicalize_headers(headers)
192
+ true
193
+ end
194
+
195
+ private
196
+
197
+ def canonicalize_headers(headers)
198
+ headers.keys.each do |key|
199
+ headers[key.split(/-/).map(&:capitalize).join('-')] = headers.delete(key)
200
+ end
201
+ headers
202
+ end
203
+ end
204
+ end
205
+
206
+ module Rack
207
+ module Handler
208
+ class UnixRack
209
+
210
+ @@chdir = ''
211
+ @@client_ip = nil
212
+ @@pid = nil
213
+ @@start_time = Time.now
214
+
215
+ # Set this in config.ru when in daemon mode
216
+ # Why? It appears that the behaviour of most servers
217
+ # is to expect to be in a certain dir when run
218
+ # Or, another way, rackup daemon mode is a bit strict
219
+ # and does the old-school chdir to '/' as a daemon.
220
+ # the fact is people probably don't use rackup often
221
+ def self.set_chdir(dir)
222
+ @@chdir = dir
223
+ end
224
+
225
+ def self.log(response_code, message='-', method='-', url='-', options={})
226
+ return
227
+ #TODO: Ignore the early return
228
+ #TODO: I'm going to make this a config debug option, use your other middleware logging for now
229
+ ip = @@client_ip || '-'
230
+ now = Time.now
231
+ duration = ((now.to_f - @@start_time.to_f) * 1000).to_i / 1000.0
232
+ puts "#{now.strftime('%Y-%m-%dT%H:%M:%S%z')},#{@@pid},#{ip},#{sprintf("%0.03f", duration)},#{response_code},\"#{message}\",#{method},\"#{url}\""
233
+ $stdout.flush
234
+ end
235
+
236
+ def self.send_error_response!(sock, num, txt, method, url)
237
+ log(num, txt, method, url)
238
+
239
+ bod = [
240
+ "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">",
241
+ "<html><head>",
242
+ "<title>#{num} #{txt}</title>",
243
+ "</head><body>",
244
+ "<h1>#{num} - #{txt}</h1>",
245
+ "<hr>",
246
+ "</body></html>"
247
+ ]
248
+
249
+ bod_txt = bod.join("\r\n")
250
+
251
+ hdr = [
252
+ "HTTP/1.1 #{num} #{txt}",
253
+ "Date: #{@@start_time.httpdate}",
254
+ "Server: UnixRack",
255
+ "Content-Length: #{bod_txt.length}",
256
+ "Connection: close",
257
+ "Content-Type: text/html; charset=iso-8859-1"
258
+ ]
259
+
260
+ hdr_txt = hdr.join("\r\n")
261
+
262
+ res = hdr_txt + "\r\n\r\n" + bod_txt
263
+
264
+ ::UnixRack::Socket.write_buff(sock.sock, res)
265
+ ::UnixRack::Socket.close(sock.sock)
266
+ exit! 0
267
+ end
268
+
269
+ def self.send_response!(sock, status, method, url, headers, body)
270
+ out = []
271
+
272
+ msg = Rack::Utils::HTTP_STATUS_CODES[status.to_i]
273
+
274
+ log(status, msg, method, url)
275
+
276
+ hdr_ary = ["HTTP/1.1 #{status} #{msg}"]
277
+
278
+ headers['Connection'] ||= 'close'
279
+
280
+ headers.each do
281
+ |k, vs|
282
+ vs.split("\n").each { |v| hdr_ary << ["#{k}: #{v}"] }
283
+ end
284
+
285
+ hdr = hdr_ary.join("\r\n")
286
+
287
+ out = [hdr, "\r\n\r\n"]
288
+
289
+ body.each { |part| out << part.to_s }
290
+
291
+ out_buff = out.join("")
292
+
293
+ ::UnixRack::Socket.write_buff(sock.sock, out_buff)
294
+ ::UnixRack::Socket.close(sock.sock)
295
+
296
+ # Conforming to SPEC - I was noticing that Sinatra logging wasn't working
297
+ body.close if body.respond_to? :close
298
+
299
+ exit! 0
300
+ end
301
+
302
+
303
+ def self.run(app, options={})
304
+
305
+ require 'socket'
306
+ port = options[:Port] || 8080
307
+ host = options[:Hostname] || 'localhost'
308
+ listen = options[:Host] || '127.0.0.1'
309
+ allowed_ips = options[:allowed_ips] || []
310
+ server = TCPServer.new(listen, port)
311
+
312
+ @@pid = $$
313
+ trap(:CHLD) do
314
+ begin
315
+ while true
316
+ pid, status = Process.waitpid2(-1, Process::WNOHANG)
317
+ if pid == nil
318
+ break
319
+ end
320
+ @@start_time = Time.now
321
+ log(-(status.exitstatus), 'child exited non-zero') if status.exitstatus != 0
322
+ #puts "#{pid}: exited - status #{status}"
323
+ #$stdout.flush
324
+ end
325
+ rescue Errno::ECHILD
326
+ end
327
+ end
328
+
329
+ trap(:TERM) { log(0, "Listener received TERM. Exiting."); exit! 0 }
330
+ trap("SIGINT") { log(0, "Listener received INT. Exiting."); exit! 0 }
331
+
332
+ if not @@chdir.empty?
333
+ Dir.chdir @@chdir
334
+ end
335
+ while true
336
+ begin
337
+ conn = server.accept
338
+ rescue Errno::EAGAIN, Errno::ECONNABORTED
339
+ p "Connection interrupted on accept"
340
+ $stdout.flush
341
+ next
342
+ rescue
343
+ p "DRU"
344
+ p $!
345
+ p $!.backtrace
346
+ $stdout.flush
347
+ exit
348
+ end
349
+
350
+ pid = fork
351
+
352
+ if pid != nil
353
+ # We are in parent
354
+ conn.close
355
+ else
356
+ # We are in child
357
+ @@pid = $$
358
+ server.close
359
+ @@start_time = Time.now
360
+
361
+ trap("ALRM") { log(0, "Child received ALARM during read_headers. Exiting."); exit! 2 }
362
+ trap(:TERM) { log(0, "Child received TERM. Exiting."); exit! 0 }
363
+
364
+ ::UnixRack::Alarm.alarm(5) # if no command received in 5 secs
365
+
366
+ sock = ::UnixRack::Socket.new(conn)
367
+ @@client_ip = sock.peeraddr.last
368
+
369
+ if not sock.read_headers()
370
+ send_error_response!(sock, 400, "Bad Request")
371
+ end
372
+
373
+ trap("ALRM") { log(0, "Child received ALARM during response. Exiting."); exit! 2 }
374
+ ::UnixRack::Alarm.alarm(120) # if command not handled in 120 seconds
375
+
376
+ if not allowed_ips.empty?
377
+ if not (allowed_ips.any? { |e| @@client_ip.include? e })
378
+ send_error_response!(sock, 403, "Forbidden", sock.hdr_method[0], sock.hdr_method[1])
379
+ end
380
+ end
381
+
382
+ if ["GET", "POST"].include?(sock.hdr_method[0])
383
+
384
+ env = {}
385
+
386
+ if sock.hdr_method[0] == "GET"
387
+ content = StringIO.new("")
388
+ content.set_encoding(Encoding::ASCII_8BIT) if content.respond_to?(:set_encoding)
389
+ elsif sock.hdr_method[0] == "POST"
390
+ if not sock.headers.include?('Content-Length')
391
+ send_error_response!(sock, 400, "Bad Request no content-length", sock.hdr_method[0], sock.hdr_method[1])
392
+ end
393
+ if not sock.headers.include?('Content-Type')
394
+ send_error_response!(sock, 400, "Bad Request no content-type", sock.hdr_method[0], sock.hdr_method[1])
395
+ end
396
+
397
+ env["CONTENT_LENGTH"] = sock.headers['Content-Length']
398
+ env["CONTENT_TYPE"] = sock.headers['Content-Type']
399
+
400
+ # F the 1.1
401
+ if sock.headers.include?('Expect')
402
+ if sock.headers['Expect'] == '100-continue'
403
+ ::UnixRack::Socket.write_buff(sock.sock, "HTTP/1.1 100 Continue\r\n\r\n")
404
+ else
405
+ send_error_response!(sock, 417, "Expectation Failed", sock.hdr_method[0], sock.hdr_method[1])
406
+ end
407
+ end
408
+
409
+ # It is required that we read all of the content prior to responding
410
+ content = sock.read_content
411
+ content.set_encoding(Encoding::ASCII_8BIT) if content.respond_to?(:set_encoding)
412
+
413
+ if content == nil
414
+ send_error_response!(sock, 400, "Bad Request not enough content", sock.hdr_method[0], sock.hdr_method[1])
415
+ end
416
+ end
417
+
418
+ app = ContentLength.new(app)
419
+
420
+
421
+ env["REQUEST_METHOD"] = sock.hdr_method[0]
422
+
423
+ uri_parts = sock.hdr_method[1].split("?", 2)
424
+ if uri_parts.length != 2
425
+ uri_parts << ""
426
+ end
427
+
428
+ # If somebody wants to send me the big absoluteURI...
429
+ # fine... what is wasting a few more cycles to chop it off
430
+ if uri_parts[0].index('http://') == 0
431
+ uri_parts[0] = uri_parts[0].sub(/http:\/\/[^\/]+/, '')
432
+ end
433
+
434
+ env["SCRIPT_NAME"] = ''
435
+ env["PATH_INFO"] = uri_parts[0]
436
+ env["QUERY_STRING"] = uri_parts[1]
437
+
438
+ env["SERVER_NAME"] = host
439
+ env["SERVER_PORT"] = port
440
+ env["REMOTE_ADDR"] = @@client_ip
441
+
442
+ if sock.headers['User-Agent']
443
+ env["HTTP_USER_AGENT"] = sock.headers['User-Agent']
444
+ end
445
+ if sock.headers['Cookie']
446
+ env["HTTP_COOKIE"] = sock.headers['Cookie']
447
+ end
448
+ if sock.headers['Authorization']
449
+ env["HTTP_AUTHORIZATION"] = sock.headers['Authorization']
450
+ end
451
+ if sock.headers['Range']
452
+ env["HTTP_RANGE"] = sock.headers['Range']
453
+ end
454
+ if sock.headers['X-Real-IP']
455
+ env["HTTP_X_REAL_IP"] = sock.headers['Real-IP']
456
+ end
457
+ if sock.headers['X-Forwarded-For']
458
+ env["HTTP_X_FORWARDED_FOR"] = sock.headers['X-Forwarded-For']
459
+ end
460
+ if sock.headers['Host']
461
+ env["HTTP_HOST"] = sock.headers['Host']
462
+ end
463
+
464
+ env["HTTP_VERSION"] = "HTTP/1.1"
465
+ if sock.headers['If-Modified-Since']
466
+ env["HTTP_IF_MODIFIED_SINCE"] = sock.headers['If-Modified-Since']
467
+ end
468
+
469
+ env.update({"rack.version" => [1, 1],
470
+ "rack.input" => content,
471
+ "rack.errors" => $stderr,
472
+ "rack.multithread" => false,
473
+ "rack.multiprocess" => true,
474
+ "rack.run_once" => true,
475
+ "rack.url_scheme" => "http"
476
+ })
477
+
478
+ # Reminder of how to do this for the future the '::' I always forget
479
+ #::File.open('/tmp/dru', 'a') do
480
+ # |f2|
481
+ # f2.syswrite(env.inspect + "\n")
482
+ #end
483
+ status, headers, body = app.call(env)
484
+
485
+ send_response!(sock, status, sock.hdr_method[0], sock.hdr_method[1], headers, body)
486
+
487
+ end
488
+
489
+ send_error_response!(sock, 500, "Server Error", sock.hdr_method[0], sock.hdr_method[1])
490
+ end
491
+ end
492
+ end
493
+
494
+ end
495
+ end
496
+ end
497
+
498
+ Rack::Handler.register 'unixrack', 'Rack::Handler::UnixRack'
data/sample/lobster.ru ADDED
@@ -0,0 +1,3 @@
1
+ require 'rack'
2
+ require 'rack/lobster'
3
+ run Rack::Lobster.new
data/unixrack.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'unixrack/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "unixrack"
8
+ spec.version = Unixrack::VERSION
9
+ spec.authors = ["Dru Nelson"]
10
+ spec.email = ["drudru@gmail.com"]
11
+ spec.description = %q{Simple Rack Compatible Web Server in Ruby}
12
+ spec.summary = %q{Old School Super Solid Forking Web Server for Ruby}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "rack", "~> 1.5"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unixrack
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Dru Nelson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Simple Rack Compatible Web Server in Ruby
56
+ email:
57
+ - drudru@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - lib/unixrack.rb
68
+ - lib/unixrack/version.rb
69
+ - sample/lobster.ru
70
+ - unixrack.gemspec
71
+ homepage: ''
72
+ licenses:
73
+ - MIT
74
+ metadata: {}
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ requirements: []
90
+ rubyforge_project:
91
+ rubygems_version: 2.0.3
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: Old School Super Solid Forking Web Server for Ruby
95
+ test_files: []