unicorn 0.5.1 → 0.5.2
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/CHANGELOG +1 -0
- data/GNUmakefile +7 -7
- data/ext/unicorn/http11/http11.c +22 -24
- data/lib/unicorn.rb +27 -29
- data/lib/unicorn/const.rb +40 -40
- data/lib/unicorn/http_response.rb +7 -3
- data/test/exec/test_exec.rb +6 -6
- data/test/unit/test_response.rb +21 -0
- data/unicorn.gemspec +2 -2
- metadata +2 -2
data/CHANGELOG
CHANGED
data/GNUmakefile
CHANGED
@@ -32,23 +32,23 @@ inst_deps := $(wildcard bin/*) $(wildcard lib/*.rb) \
|
|
32
32
|
|
33
33
|
ext/unicorn/http11/http11_parser.c: ext/unicorn/http11/http11_parser.rl
|
34
34
|
cd $(@D) && ragel $(<F) -C -G2 -o $(@F)
|
35
|
-
ext/unicorn/http11/Makefile: ext/unicorn/http11/extconf.rb
|
35
|
+
ext/unicorn/http11/Makefile: ext/unicorn/http11/extconf.rb $(http11_deps)
|
36
36
|
cd $(@D) && $(ruby) $(<F)
|
37
|
-
ext/unicorn/http11/http11.$(DLEXT):
|
37
|
+
ext/unicorn/http11/http11.$(DLEXT): ext/unicorn/http11/Makefile
|
38
38
|
$(MAKE) -C $(@D)
|
39
39
|
lib/unicorn/http11.$(DLEXT): ext/unicorn/http11/http11.$(DLEXT)
|
40
40
|
@mkdir -p lib
|
41
41
|
install -m644 $< $@
|
42
42
|
http11: lib/unicorn/http11.$(DLEXT)
|
43
43
|
|
44
|
-
$(test_prefix)/.stamp:
|
45
|
-
$(MAKE) clean-http11
|
46
|
-
$(MAKE) install-test
|
44
|
+
$(test_prefix)/.stamp: install-test
|
47
45
|
> $@
|
48
46
|
|
49
|
-
install-test:
|
47
|
+
install-test: $(inst_deps)
|
48
|
+
test -n "$(test_prefix)"
|
50
49
|
mkdir -p $(test_prefix)/.ccache
|
51
50
|
tar c bin ext lib GNUmakefile | (cd $(test_prefix) && tar x)
|
51
|
+
$(MAKE) -C $(test_prefix) clean
|
52
52
|
$(MAKE) -C $(test_prefix) http11 shebang
|
53
53
|
|
54
54
|
# this is only intended to be run within $(test_prefix)
|
@@ -62,7 +62,7 @@ test: $(T) $(T_n)
|
|
62
62
|
|
63
63
|
test-exec: $(wildcard test/exec/test_*.rb)
|
64
64
|
test-unit: $(wildcard test/unit/test_*.rb)
|
65
|
-
$(slow_tests):
|
65
|
+
$(slow_tests): $(test_prefix)/.stamp
|
66
66
|
@$(MAKE) $(shell $(awk_slow) $@)
|
67
67
|
|
68
68
|
TEST_OPTS = -v
|
data/ext/unicorn/http11/http11.c
CHANGED
@@ -40,6 +40,7 @@ static VALUE global_server_protocol_value;
|
|
40
40
|
static VALUE global_http_host;
|
41
41
|
static VALUE global_http_x_forwarded_proto;
|
42
42
|
static VALUE global_port_80;
|
43
|
+
static VALUE global_port_443;
|
43
44
|
static VALUE global_localhost;
|
44
45
|
static VALUE global_http;
|
45
46
|
|
@@ -243,40 +244,36 @@ static void http_version(void *data, const char *at, size_t length)
|
|
243
244
|
rb_hash_aset(req, global_http_version, val);
|
244
245
|
}
|
245
246
|
|
246
|
-
/** Finalizes the request header to have a bunch of stuff that's
|
247
|
-
needed. */
|
248
|
-
|
247
|
+
/** Finalizes the request header to have a bunch of stuff that's needed. */
|
249
248
|
static void header_done(void *data, const char *at, size_t length)
|
250
249
|
{
|
251
250
|
VALUE req = (VALUE)data;
|
252
|
-
VALUE
|
253
|
-
|
251
|
+
VALUE server_name = global_localhost;
|
252
|
+
VALUE server_port = global_port_80;
|
253
|
+
VALUE temp;
|
254
254
|
|
255
255
|
/* set rack.url_scheme to "https" or "http", no others are allowed by Rack */
|
256
|
-
temp = rb_hash_aref(req, global_http_x_forwarded_proto)
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
256
|
+
if ((temp = rb_hash_aref(req, global_http_x_forwarded_proto)) != Qnil &&
|
257
|
+
RSTRING_LEN(temp) == 5 &&
|
258
|
+
!memcmp("https", RSTRING_PTR(temp), 5))
|
259
|
+
server_port = global_port_443;
|
260
|
+
else
|
261
|
+
temp = global_http;
|
262
262
|
rb_hash_aset(req, global_rack_url_scheme, temp);
|
263
263
|
|
264
|
-
/* set the SERVER_NAME and SERVER_PORT variables */
|
265
|
-
if((temp = rb_hash_aref(req, global_http_host)) != Qnil) {
|
266
|
-
colon = memchr(RSTRING_PTR(temp), ':', RSTRING_LEN(temp));
|
267
|
-
if(colon
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
RSTRING_LEN(temp)));
|
264
|
+
/* parse and set the SERVER_NAME and SERVER_PORT variables */
|
265
|
+
if ((temp = rb_hash_aref(req, global_http_host)) != Qnil) {
|
266
|
+
char *colon = memchr(RSTRING_PTR(temp), ':', RSTRING_LEN(temp));
|
267
|
+
if (colon) {
|
268
|
+
server_name = rb_str_substr(temp, 0, colon - RSTRING_PTR(temp));
|
269
|
+
server_port = rb_str_substr(temp, colon - RSTRING_PTR(temp)+1,
|
270
|
+
RSTRING_LEN(temp));
|
272
271
|
} else {
|
273
|
-
|
274
|
-
rb_hash_aset(req, global_server_port, global_port_80);
|
272
|
+
server_name = temp;
|
275
273
|
}
|
276
|
-
} else {
|
277
|
-
rb_hash_aset(req, global_server_name, global_localhost);
|
278
|
-
rb_hash_aset(req, global_server_port, global_port_80);
|
279
274
|
}
|
275
|
+
rb_hash_aset(req, global_server_name, server_name);
|
276
|
+
rb_hash_aset(req, global_server_port, server_port);
|
280
277
|
|
281
278
|
/* grab the initial body and stuff it into the hash */
|
282
279
|
rb_hash_aset(req, sym_http_body, rb_str_new(at, length));
|
@@ -402,6 +399,7 @@ void Init_http11()
|
|
402
399
|
DEF_GLOBAL(http_host, "HTTP_HOST");
|
403
400
|
DEF_GLOBAL(http_x_forwarded_proto, "HTTP_X_FORWARDED_PROTO");
|
404
401
|
DEF_GLOBAL(port_80, "80");
|
402
|
+
DEF_GLOBAL(port_443, "443");
|
405
403
|
DEF_GLOBAL(localhost, "localhost");
|
406
404
|
DEF_GLOBAL(http, "http");
|
407
405
|
|
data/lib/unicorn.rb
CHANGED
@@ -52,7 +52,6 @@ module Unicorn
|
|
52
52
|
@start_ctx = DEFAULT_START_CTX.dup
|
53
53
|
@start_ctx.merge!(start_ctx) if start_ctx
|
54
54
|
@app = app
|
55
|
-
@master_pid = $$
|
56
55
|
@workers = Hash.new
|
57
56
|
@io_purgatory = [] # prevents IO objects in here from being GC-ed
|
58
57
|
@request = @rd_sig = @wr_sig = nil
|
@@ -193,9 +192,9 @@ module Unicorn
|
|
193
192
|
stop(false)
|
194
193
|
break
|
195
194
|
when :USR1 # rotate logs
|
196
|
-
logger.info "master
|
195
|
+
logger.info "master reopening logs..."
|
197
196
|
Unicorn::Util.reopen_logs
|
198
|
-
logger.info "master done
|
197
|
+
logger.info "master done reopening logs"
|
199
198
|
kill_each_worker(:USR1)
|
200
199
|
when :USR2 # exec binary, stay alive in case something went wrong
|
201
200
|
reexec
|
@@ -353,7 +352,7 @@ module Unicorn
|
|
353
352
|
io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
354
353
|
end
|
355
354
|
logger.info "executing #{cmd.inspect} (in #{Dir.pwd})"
|
356
|
-
@before_exec.call(self)
|
355
|
+
@before_exec.call(self)
|
357
356
|
exec(*cmd)
|
358
357
|
end
|
359
358
|
proc_name 'master (old)'
|
@@ -438,7 +437,7 @@ module Unicorn
|
|
438
437
|
@start_ctx = @workers = @rd_sig = @wr_sig = nil
|
439
438
|
@listeners.each { |sock| sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
|
440
439
|
worker.tempfile.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
441
|
-
@after_fork.call(self, worker)
|
440
|
+
@after_fork.call(self, worker) # can drop perms
|
442
441
|
@request = HttpRequest.new(logger)
|
443
442
|
build_app! unless @preload_app
|
444
443
|
end
|
@@ -447,28 +446,27 @@ module Unicorn
|
|
447
446
|
# for connections and doesn't die until the parent dies (or is
|
448
447
|
# given a INT, QUIT, or TERM signal)
|
449
448
|
def worker_loop(worker)
|
449
|
+
master_pid = Process.ppid # slightly racy, but less memory usage
|
450
450
|
init_worker_process(worker)
|
451
|
-
nr = 0
|
451
|
+
nr = 0 # this becomes negative if we need to reopen logs
|
452
452
|
tempfile = worker.tempfile
|
453
|
-
alive = true
|
454
453
|
ready = @listeners
|
455
454
|
client = nil
|
456
|
-
|
457
|
-
alive = false # graceful shutdown
|
458
|
-
@listeners.each { |sock| sock.close rescue nil } # break IO.select
|
459
|
-
end
|
460
|
-
reopen_logs, (rd, wr) = false, IO.pipe
|
455
|
+
rd, wr = IO.pipe
|
461
456
|
rd.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
462
457
|
wr.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
463
|
-
|
458
|
+
|
459
|
+
# closing anything we IO.select on will raise EBADF
|
460
|
+
trap(:USR1) { nr = -65536; rd.close rescue nil }
|
461
|
+
trap(:QUIT) { @listeners.each { |sock| sock.close rescue nil } }
|
462
|
+
[:TERM, :INT].each { |sig| trap(sig) { exit(0) } } # instant shutdown
|
464
463
|
@logger.info "worker=#{worker.nr} ready"
|
465
464
|
|
466
|
-
while
|
467
|
-
if
|
468
|
-
|
469
|
-
@logger.info "worker=#{worker.nr} rotating logs..."
|
465
|
+
while master_pid == Process.ppid
|
466
|
+
if nr < 0
|
467
|
+
@logger.info "worker=#{worker.nr} reopening logs..."
|
470
468
|
Unicorn::Util.reopen_logs
|
471
|
-
@logger.info "worker=#{worker.nr} done
|
469
|
+
@logger.info "worker=#{worker.nr} done reopening logs"
|
472
470
|
wr.close rescue nil
|
473
471
|
rd, wr = IO.pipe
|
474
472
|
rd.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
@@ -480,11 +478,11 @@ module Unicorn
|
|
480
478
|
# prefer temporary files to be unlinked for security,
|
481
479
|
# performance and reliability reasons, so utime is out. No-op
|
482
480
|
# changes with chmod doesn't update ctime on all filesystems; so
|
483
|
-
# we
|
484
|
-
|
481
|
+
# we change our counter each and every time (after process_client
|
482
|
+
# and before IO.select).
|
483
|
+
tempfile.chmod(nr = 0)
|
485
484
|
|
486
485
|
begin
|
487
|
-
accepted = false
|
488
486
|
ready.each do |sock|
|
489
487
|
begin
|
490
488
|
client = begin
|
@@ -492,22 +490,22 @@ module Unicorn
|
|
492
490
|
rescue Errno::EAGAIN
|
493
491
|
next
|
494
492
|
end
|
495
|
-
accepted = true
|
496
493
|
process_client(client)
|
497
494
|
rescue Errno::ECONNABORTED
|
498
495
|
# client closed the socket even before accept
|
499
496
|
client.close rescue nil
|
497
|
+
ensure
|
498
|
+
tempfile.chmod(nr += 1)
|
499
|
+
break if nr < 0
|
500
500
|
end
|
501
|
-
tempfile.chmod(nr += 1)
|
502
|
-
break if reopen_logs
|
503
501
|
end
|
504
502
|
client = nil
|
505
503
|
|
506
504
|
# make the following bet: if we accepted clients this round,
|
507
|
-
# we're probably reasonably busy, so avoid calling select(
|
508
|
-
# and
|
509
|
-
# before we sleep again in select
|
510
|
-
if
|
505
|
+
# we're probably reasonably busy, so avoid calling select()
|
506
|
+
# and do a speculative accept_nonblock on every listener
|
507
|
+
# before we sleep again in select().
|
508
|
+
if nr != 0 # (nr < 0) => reopen logs
|
511
509
|
ready = @listeners
|
512
510
|
else
|
513
511
|
begin
|
@@ -518,7 +516,7 @@ module Unicorn
|
|
518
516
|
rescue Errno::EINTR
|
519
517
|
ready = @listeners
|
520
518
|
rescue Errno::EBADF => e
|
521
|
-
|
519
|
+
nr < 0 or exit(@listeners[0].closed? ? 0 : 1)
|
522
520
|
end
|
523
521
|
end
|
524
522
|
rescue SignalException, SystemExit => e
|
data/lib/unicorn/const.rb
CHANGED
@@ -4,43 +4,43 @@ module Unicorn
|
|
4
4
|
# Every standard HTTP code mapped to the appropriate message. These are
|
5
5
|
# used so frequently that they are placed directly in Unicorn for easy
|
6
6
|
# access rather than Unicorn::Const itself.
|
7
|
-
HTTP_STATUS_CODES = {
|
8
|
-
100 => 'Continue',
|
9
|
-
101 => 'Switching Protocols',
|
10
|
-
200 => 'OK',
|
11
|
-
201 => 'Created',
|
12
|
-
202 => 'Accepted',
|
13
|
-
203 => 'Non-Authoritative Information',
|
14
|
-
204 => 'No Content',
|
15
|
-
205 => 'Reset Content',
|
16
|
-
206 => 'Partial Content',
|
17
|
-
300 => 'Multiple Choices',
|
18
|
-
301 => 'Moved Permanently',
|
19
|
-
302 => 'Moved Temporarily',
|
20
|
-
303 => 'See Other',
|
21
|
-
304 => 'Not Modified',
|
22
|
-
305 => 'Use Proxy',
|
23
|
-
400 => 'Bad Request',
|
24
|
-
401 => 'Unauthorized',
|
25
|
-
402 => 'Payment Required',
|
26
|
-
403 => 'Forbidden',
|
27
|
-
404 => 'Not Found',
|
28
|
-
405 => 'Method Not Allowed',
|
29
|
-
406 => 'Not Acceptable',
|
30
|
-
407 => 'Proxy Authentication Required',
|
31
|
-
408 => 'Request Time-out',
|
32
|
-
409 => 'Conflict',
|
33
|
-
410 => 'Gone',
|
34
|
-
411 => 'Length Required',
|
35
|
-
412 => 'Precondition Failed',
|
36
|
-
413 => 'Request Entity Too Large',
|
37
|
-
414 => 'Request-URI Too Large',
|
38
|
-
415 => 'Unsupported Media Type',
|
39
|
-
500 => 'Internal Server Error',
|
40
|
-
501 => 'Not Implemented',
|
41
|
-
502 => 'Bad Gateway',
|
42
|
-
503 => 'Service Unavailable',
|
43
|
-
504 => 'Gateway Time-out',
|
7
|
+
HTTP_STATUS_CODES = {
|
8
|
+
100 => 'Continue',
|
9
|
+
101 => 'Switching Protocols',
|
10
|
+
200 => 'OK',
|
11
|
+
201 => 'Created',
|
12
|
+
202 => 'Accepted',
|
13
|
+
203 => 'Non-Authoritative Information',
|
14
|
+
204 => 'No Content',
|
15
|
+
205 => 'Reset Content',
|
16
|
+
206 => 'Partial Content',
|
17
|
+
300 => 'Multiple Choices',
|
18
|
+
301 => 'Moved Permanently',
|
19
|
+
302 => 'Moved Temporarily',
|
20
|
+
303 => 'See Other',
|
21
|
+
304 => 'Not Modified',
|
22
|
+
305 => 'Use Proxy',
|
23
|
+
400 => 'Bad Request',
|
24
|
+
401 => 'Unauthorized',
|
25
|
+
402 => 'Payment Required',
|
26
|
+
403 => 'Forbidden',
|
27
|
+
404 => 'Not Found',
|
28
|
+
405 => 'Method Not Allowed',
|
29
|
+
406 => 'Not Acceptable',
|
30
|
+
407 => 'Proxy Authentication Required',
|
31
|
+
408 => 'Request Time-out',
|
32
|
+
409 => 'Conflict',
|
33
|
+
410 => 'Gone',
|
34
|
+
411 => 'Length Required',
|
35
|
+
412 => 'Precondition Failed',
|
36
|
+
413 => 'Request Entity Too Large',
|
37
|
+
414 => 'Request-URI Too Large',
|
38
|
+
415 => 'Unsupported Media Type',
|
39
|
+
500 => 'Internal Server Error',
|
40
|
+
501 => 'Not Implemented',
|
41
|
+
502 => 'Bad Gateway',
|
42
|
+
503 => 'Service Unavailable',
|
43
|
+
504 => 'Gateway Time-out',
|
44
44
|
505 => 'HTTP Version not supported'
|
45
45
|
}
|
46
46
|
|
@@ -53,12 +53,12 @@ module Unicorn
|
|
53
53
|
|
54
54
|
# This is the part of the path after the SCRIPT_NAME.
|
55
55
|
PATH_INFO="PATH_INFO".freeze
|
56
|
-
|
56
|
+
|
57
57
|
# The original URI requested by the client.
|
58
58
|
REQUEST_URI='REQUEST_URI'.freeze
|
59
59
|
REQUEST_PATH='REQUEST_PATH'.freeze
|
60
|
-
|
61
|
-
UNICORN_VERSION="0.5.
|
60
|
+
|
61
|
+
UNICORN_VERSION="0.5.2".freeze
|
62
62
|
|
63
63
|
UNICORN_TMP_BASE="unicorn".freeze
|
64
64
|
|
@@ -24,12 +24,16 @@ module Unicorn
|
|
24
24
|
# Rack does not set/require a Date: header. We always override the
|
25
25
|
# Connection: and Date: headers no matter what (if anything) our
|
26
26
|
# Rack application sent us.
|
27
|
-
SKIP = { 'connection' => true, 'date' => true }.freeze
|
27
|
+
SKIP = { 'connection' => true, 'date' => true, 'status' => true }.freeze
|
28
28
|
|
29
29
|
# writes the rack_response to socket as an HTTP response
|
30
30
|
def self.write(socket, rack_response)
|
31
31
|
status, headers, body = rack_response
|
32
|
-
|
32
|
+
status = "#{status} #{HTTP_STATUS_CODES[status]}"
|
33
|
+
|
34
|
+
# Date is required by HTTP/1.1 as long as our clock can be trusted.
|
35
|
+
# Some broken clients require a "Status" header so we accomodate them
|
36
|
+
out = [ "Date: #{Time.now.httpdate}", "Status: #{status}" ]
|
33
37
|
|
34
38
|
# Don't bother enforcing duplicate supression, it's a Hash most of
|
35
39
|
# the time anyways so just hope our app knows what it's doing
|
@@ -45,7 +49,7 @@ module Unicorn
|
|
45
49
|
# Rack should enforce Content-Length or chunked transfer encoding,
|
46
50
|
# so don't worry or care about them.
|
47
51
|
socket_write(socket,
|
48
|
-
"HTTP/1.1 #{status}
|
52
|
+
"HTTP/1.1 #{status}\r\n" \
|
49
53
|
"Connection: close\r\n" \
|
50
54
|
"#{out.join("\r\n")}\r\n\r\n")
|
51
55
|
body.each { |chunk| socket_write(socket, chunk) }
|
data/test/exec/test_exec.rb
CHANGED
@@ -370,21 +370,21 @@ end
|
|
370
370
|
tries = DEFAULT_TRIES
|
371
371
|
log = File.readlines(rotate.path)
|
372
372
|
while (tries -= 1) > 0 &&
|
373
|
-
log.grep(/
|
373
|
+
log.grep(/reopening logs\.\.\./).size < 5
|
374
374
|
sleep DEFAULT_RES
|
375
375
|
log = File.readlines(rotate.path)
|
376
376
|
end
|
377
|
-
assert_equal 5, log.grep(/
|
378
|
-
assert_equal 0, log.grep(/done
|
377
|
+
assert_equal 5, log.grep(/reopening logs\.\.\./).size
|
378
|
+
assert_equal 0, log.grep(/done reopening logs/).size
|
379
379
|
|
380
380
|
tries = DEFAULT_TRIES
|
381
381
|
log = File.readlines(COMMON_TMP.path)
|
382
|
-
while (tries -= 1) > 0 && log.grep(/done
|
382
|
+
while (tries -= 1) > 0 && log.grep(/done reopening logs/).size < 5
|
383
383
|
sleep DEFAULT_RES
|
384
384
|
log = File.readlines(COMMON_TMP.path)
|
385
385
|
end
|
386
|
-
assert_equal 5, log.grep(/done
|
387
|
-
assert_equal 0, log.grep(/
|
386
|
+
assert_equal 5, log.grep(/done reopening logs/).size
|
387
|
+
assert_equal 0, log.grep(/reopening logs\.\.\./).size
|
388
388
|
assert_nothing_raised { Process.kill(:QUIT, pid) }
|
389
389
|
status = nil
|
390
390
|
assert_nothing_raised { pid, status = Process.waitpid2(pid) }
|
data/test/unit/test_response.rb
CHANGED
@@ -54,6 +54,27 @@ class ResponseTest < Test::Unit::TestCase
|
|
54
54
|
assert_match(/^X-Whatever: stuff\r\nX-Whatever: bleh\r\n/, out.string)
|
55
55
|
end
|
56
56
|
|
57
|
+
# Even though Rack explicitly forbids "Status" in the header hash,
|
58
|
+
# some broken clients still rely on it
|
59
|
+
def test_status_header_added
|
60
|
+
out = StringIO.new
|
61
|
+
HttpResponse.write(out,[200, {"X-Whatever" => "stuff"}, []])
|
62
|
+
assert out.closed?
|
63
|
+
assert_match(/^Status: 200 OK\r\nX-Whatever: stuff\r\n/, out.string)
|
64
|
+
end
|
65
|
+
|
66
|
+
# we always favor the code returned by the application, since "Status"
|
67
|
+
# in the header hash is not allowed by Rack (but not every app is
|
68
|
+
# fully Rack-compliant).
|
69
|
+
def test_status_header_ignores_app_hash
|
70
|
+
out = StringIO.new
|
71
|
+
header_hash = {"X-Whatever" => "stuff", 'StaTus' => "666" }
|
72
|
+
HttpResponse.write(out,[200, header_hash, []])
|
73
|
+
assert out.closed?
|
74
|
+
assert_match(/^Status: 200 OK\r\nX-Whatever: stuff\r\n/, out.string)
|
75
|
+
assert_equal 1, out.string.split(/\r\n/).grep(/^Status:/i).size
|
76
|
+
end
|
77
|
+
|
57
78
|
def test_body_closed
|
58
79
|
expect_body = %w(1 2 3 4).join("\n")
|
59
80
|
body = StringIO.new(expect_body)
|
data/unicorn.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{unicorn}
|
5
|
-
s.version = "0.5.
|
5
|
+
s.version = "0.5.2"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Eric Wong"]
|
9
|
-
s.date = %q{2009-04-
|
9
|
+
s.date = %q{2009-04-16}
|
10
10
|
s.description = %q{A small fast HTTP library and server for Rack applications.}
|
11
11
|
s.email = %q{normalperson@yhbt.net}
|
12
12
|
s.executables = ["unicorn", "unicorn_rails"]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unicorn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Wong
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-04-
|
12
|
+
date: 2009-04-16 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|