unicorn 3.2.1 → 3.3.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/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +1 -1
- data/Rakefile +5 -2
- data/ext/unicorn_http/extconf.rb +1 -2
- data/ext/unicorn_http/httpdate.c +82 -0
- data/ext/unicorn_http/unicorn_http.rl +29 -4
- data/lib/unicorn/const.rb +2 -2
- data/lib/unicorn/http_request.rb +1 -1
- data/lib/unicorn/http_response.rb +4 -7
- data/lib/unicorn/http_server.rb +5 -7
- data/lib/unicorn/socket_helper.rb +7 -7
- data/test/unit/test_http_parser.rb +6 -6
- data/test/unit/test_http_parser_ng.rb +25 -3
- data/test/unit/test_response.rb +20 -21
- data/unicorn.gemspec +1 -1
- metadata +9 -9
data/GIT-VERSION-GEN
CHANGED
data/GNUmakefile
CHANGED
@@ -45,7 +45,7 @@ T_r_log := $(subst .r,$(log_suffix),$(T_r))
|
|
45
45
|
test_prefix = $(CURDIR)/test/$(RUBY_ENGINE)-$(RUBY_VERSION)
|
46
46
|
|
47
47
|
ext := ext/unicorn_http
|
48
|
-
c_files := $(ext)/unicorn_http.c $(wildcard $(ext)/*.h)
|
48
|
+
c_files := $(ext)/unicorn_http.c $(ext)/httpdate.c $(wildcard $(ext)/*.h)
|
49
49
|
rl_files := $(wildcard $(ext)/*.rl)
|
50
50
|
base_bins := unicorn unicorn_rails
|
51
51
|
bins := $(addprefix bin/, $(base_bins))
|
data/Rakefile
CHANGED
@@ -51,9 +51,12 @@ task :fm_update do
|
|
51
51
|
uri = URI.parse('http://freshmeat.net/projects/unicorn/releases.json')
|
52
52
|
rc = Net::Netrc.locate('unicorn-fm') or abort "~/.netrc not found"
|
53
53
|
api_token = rc.password
|
54
|
-
|
54
|
+
_, subject, body = `git cat-file tag v#{version}`.split(/\n\n/, 3)
|
55
55
|
tmp = Tempfile.new('fm-changelog')
|
56
|
-
tmp.
|
56
|
+
tmp.puts subject
|
57
|
+
tmp.puts
|
58
|
+
tmp.puts body
|
59
|
+
tmp.flush
|
57
60
|
system(ENV["VISUAL"], tmp.path) or abort "#{ENV["VISUAL"]} failed: #$?"
|
58
61
|
changelog = File.read(tmp.path).strip
|
59
62
|
|
data/ext/unicorn_http/extconf.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
require 'mkmf'
|
3
3
|
|
4
|
-
dir_config("unicorn_http")
|
5
|
-
|
6
4
|
have_macro("SIZEOF_OFF_T", "ruby.h") or check_sizeof("off_t", "sys/types.h")
|
7
5
|
have_macro("SIZEOF_LONG", "ruby.h") or check_sizeof("long", "sys/types.h")
|
8
6
|
have_func("rb_str_set_len", "ruby.h")
|
7
|
+
have_func("gmtime_r", "time.h")
|
9
8
|
|
10
9
|
create_makefile("unicorn_http")
|
@@ -0,0 +1,82 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <time.h>
|
3
|
+
#include <stdio.h>
|
4
|
+
|
5
|
+
static const size_t buf_capa = sizeof("Thu, 01 Jan 1970 00:00:00 GMT");
|
6
|
+
static VALUE buf;
|
7
|
+
static char *buf_ptr;
|
8
|
+
static const char *const week[] = {
|
9
|
+
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
10
|
+
};
|
11
|
+
static const char *const months[] = {
|
12
|
+
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
13
|
+
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
14
|
+
};
|
15
|
+
|
16
|
+
/* for people on wonky systems only */
|
17
|
+
#ifndef HAVE_GMTIME_R
|
18
|
+
static struct tm * my_gmtime_r(time_t *now, struct tm *tm)
|
19
|
+
{
|
20
|
+
struct tm *global = gmtime(now);
|
21
|
+
if (global)
|
22
|
+
*tm = *global;
|
23
|
+
return tm;
|
24
|
+
}
|
25
|
+
# define gmtime_r my_gmtime_r
|
26
|
+
#endif
|
27
|
+
|
28
|
+
|
29
|
+
/*
|
30
|
+
* Returns a string which represents the time as rfc1123-date of HTTP-date
|
31
|
+
* defined by RFC 2616:
|
32
|
+
*
|
33
|
+
* day-of-week, DD month-name CCYY hh:mm:ss GMT
|
34
|
+
*
|
35
|
+
* Note that the result is always GMT.
|
36
|
+
*
|
37
|
+
* This method is identical to Time#httpdate in the Ruby standard library,
|
38
|
+
* except it is implemented in C for performance. We always saw
|
39
|
+
* Time#httpdate at or near the top of the profiler output so we
|
40
|
+
* decided to rewrite this in C.
|
41
|
+
*
|
42
|
+
* Caveats: it relies on a Ruby implementation with the global VM lock,
|
43
|
+
* a thread-safe version will be provided when a Unix-only, GVL-free Ruby
|
44
|
+
* implementation becomes viable.
|
45
|
+
*/
|
46
|
+
static VALUE httpdate(VALUE self)
|
47
|
+
{
|
48
|
+
static time_t last;
|
49
|
+
time_t now = time(NULL); /* not a syscall on modern 64-bit systems */
|
50
|
+
struct tm tm;
|
51
|
+
|
52
|
+
if (last == now)
|
53
|
+
return buf;
|
54
|
+
last = now;
|
55
|
+
gmtime_r(&now, &tm);
|
56
|
+
|
57
|
+
/* we can make this thread-safe later if our Ruby loses the GVL */
|
58
|
+
snprintf(buf_ptr, buf_capa,
|
59
|
+
"%s, %02d %s %4d %02d:%02d:%02d GMT",
|
60
|
+
week[tm.tm_wday],
|
61
|
+
tm.tm_mday,
|
62
|
+
months[tm.tm_mon],
|
63
|
+
tm.tm_year + 1900,
|
64
|
+
tm.tm_hour,
|
65
|
+
tm.tm_min,
|
66
|
+
tm.tm_sec);
|
67
|
+
|
68
|
+
return buf;
|
69
|
+
}
|
70
|
+
|
71
|
+
void init_unicorn_httpdate(void)
|
72
|
+
{
|
73
|
+
VALUE mod = rb_const_get(rb_cObject, rb_intern("Unicorn"));
|
74
|
+
mod = rb_define_module_under(mod, "HttpResponse");
|
75
|
+
|
76
|
+
buf = rb_str_new(0, buf_capa - 1);
|
77
|
+
rb_global_variable(&buf);
|
78
|
+
buf_ptr = RSTRING_PTR(buf);
|
79
|
+
httpdate(Qnil);
|
80
|
+
|
81
|
+
rb_define_method(mod, "httpdate", httpdate, 0);
|
82
|
+
}
|
@@ -12,6 +12,8 @@
|
|
12
12
|
#include "global_variables.h"
|
13
13
|
#include "c_util.h"
|
14
14
|
|
15
|
+
void init_unicorn_httpdate(void);
|
16
|
+
|
15
17
|
#define UH_FL_CHUNKED 0x1
|
16
18
|
#define UH_FL_HASBODY 0x2
|
17
19
|
#define UH_FL_INBODY 0x4
|
@@ -570,18 +572,39 @@ static VALUE HttpParser_init(VALUE self)
|
|
570
572
|
|
571
573
|
/**
|
572
574
|
* call-seq:
|
573
|
-
* parser.
|
575
|
+
* parser.clear => parser
|
574
576
|
*
|
575
577
|
* Resets the parser to it's initial state so that you can reuse it
|
576
578
|
* rather than making new ones.
|
577
579
|
*/
|
578
|
-
static VALUE
|
580
|
+
static VALUE HttpParser_clear(VALUE self)
|
579
581
|
{
|
580
582
|
struct http_parser *hp = data_get(self);
|
581
583
|
|
582
584
|
http_parser_init(hp);
|
583
585
|
rb_funcall(hp->env, id_clear, 0);
|
584
586
|
|
587
|
+
return self;
|
588
|
+
}
|
589
|
+
|
590
|
+
/**
|
591
|
+
* call-seq:
|
592
|
+
* parser.reset => nil
|
593
|
+
*
|
594
|
+
* Resets the parser to it's initial state so that you can reuse it
|
595
|
+
* rather than making new ones.
|
596
|
+
*
|
597
|
+
* This method is deprecated and to be removed in Unicorn 4.x
|
598
|
+
*/
|
599
|
+
static VALUE HttpParser_reset(VALUE self)
|
600
|
+
{
|
601
|
+
static int warned;
|
602
|
+
|
603
|
+
if (!warned) {
|
604
|
+
rb_warn("Unicorn::HttpParser#reset is deprecated; "
|
605
|
+
"use Unicorn::HttpParser#clear instead");
|
606
|
+
}
|
607
|
+
HttpParser_clear(self);
|
585
608
|
return Qnil;
|
586
609
|
}
|
587
610
|
|
@@ -852,8 +875,9 @@ void Init_unicorn_http(void)
|
|
852
875
|
|
853
876
|
init_globals();
|
854
877
|
rb_define_alloc_func(cHttpParser, HttpParser_alloc);
|
855
|
-
rb_define_method(cHttpParser, "initialize", HttpParser_init,0);
|
856
|
-
rb_define_method(cHttpParser, "
|
878
|
+
rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
|
879
|
+
rb_define_method(cHttpParser, "clear", HttpParser_clear, 0);
|
880
|
+
rb_define_method(cHttpParser, "reset", HttpParser_reset, 0);
|
857
881
|
rb_define_method(cHttpParser, "parse", HttpParser_parse, 0);
|
858
882
|
rb_define_method(cHttpParser, "headers", HttpParser_headers, 2);
|
859
883
|
rb_define_method(cHttpParser, "trailers", HttpParser_headers, 2);
|
@@ -897,5 +921,6 @@ void Init_unicorn_http(void)
|
|
897
921
|
SET_GLOBAL(g_content_length, "CONTENT_LENGTH");
|
898
922
|
SET_GLOBAL(g_http_connection, "CONNECTION");
|
899
923
|
id_clear = rb_intern("clear");
|
924
|
+
init_unicorn_httpdate();
|
900
925
|
}
|
901
926
|
#undef SET_GLOBAL
|
data/lib/unicorn/const.rb
CHANGED
@@ -7,8 +7,8 @@
|
|
7
7
|
# improve things much compared to constants.
|
8
8
|
module Unicorn::Const
|
9
9
|
|
10
|
-
# The current version of Unicorn, currently 3.
|
11
|
-
UNICORN_VERSION = "3.
|
10
|
+
# The current version of Unicorn, currently 3.3.0
|
11
|
+
UNICORN_VERSION = "3.3.0"
|
12
12
|
|
13
13
|
# default TCP listen host address (0.0.0.0, all interfaces)
|
14
14
|
DEFAULT_HOST = "0.0.0.0"
|
data/lib/unicorn/http_request.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
-
require 'time'
|
3
|
-
|
4
2
|
# Writes a Rack response to your client using the HTTP/1.1 specification.
|
5
3
|
# You use it by simply doing:
|
6
4
|
#
|
7
5
|
# status, headers, body = rack_app.call(env)
|
8
|
-
# http_response_write(socket,
|
6
|
+
# http_response_write(socket, status, headers, body)
|
9
7
|
#
|
10
8
|
# Most header correctness (including Content-Length and Content-Type)
|
11
9
|
# is the job of Rack, with the exception of the "Date" and "Status" header.
|
@@ -19,20 +17,19 @@ module Unicorn::HttpResponse
|
|
19
17
|
CRLF = "\r\n"
|
20
18
|
|
21
19
|
# writes the rack_response to socket as an HTTP response
|
22
|
-
def http_response_write(socket,
|
23
|
-
status, headers, body = rack_response
|
20
|
+
def http_response_write(socket, status, headers, body)
|
24
21
|
status = CODES[status.to_i] || status
|
25
22
|
|
26
23
|
if headers
|
27
24
|
buf = "HTTP/1.1 #{status}\r\n" \
|
28
|
-
"Date: #{
|
25
|
+
"Date: #{httpdate}\r\n" \
|
29
26
|
"Status: #{status}\r\n" \
|
30
27
|
"Connection: close\r\n"
|
31
28
|
headers.each do |key, value|
|
32
29
|
next if %r{\A(?:Date\z|Status\z|Connection\z)}i =~ key
|
33
30
|
if value =~ /\n/
|
34
31
|
# avoiding blank, key-only cookies with /\n+/
|
35
|
-
buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
|
32
|
+
buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
|
36
33
|
else
|
37
34
|
buf << "#{key}: #{value}\r\n"
|
38
35
|
end
|
data/lib/unicorn/http_server.rb
CHANGED
@@ -524,22 +524,20 @@ class Unicorn::HttpServer
|
|
524
524
|
client.kgio_trywrite(msg)
|
525
525
|
client.close
|
526
526
|
rescue
|
527
|
-
nil
|
528
527
|
end
|
529
528
|
|
530
529
|
# once a client is accepted, it is processed in its entirety here
|
531
530
|
# in 3 easy steps: read request, call app, write app response
|
532
531
|
def process_client(client)
|
533
|
-
|
532
|
+
status, headers, body = @app.call(env = @request.read(client))
|
534
533
|
|
535
|
-
if 100 ==
|
534
|
+
if 100 == status.to_i
|
536
535
|
client.write(Unicorn::Const::EXPECT_100_RESPONSE)
|
537
536
|
env.delete(Unicorn::Const::HTTP_EXPECT)
|
538
|
-
|
537
|
+
status, headers, body = @app.call(env)
|
539
538
|
end
|
540
|
-
|
541
|
-
|
542
|
-
http_response_write(client, r)
|
539
|
+
@request.headers? or headers = nil
|
540
|
+
http_response_write(client, status, headers, body)
|
543
541
|
rescue => e
|
544
542
|
handle_error(client, e)
|
545
543
|
end
|
@@ -20,6 +20,9 @@ module Unicorn
|
|
20
20
|
# FreeBSD, we need to override this to 'dataready' when we
|
21
21
|
# eventually get HTTPS support
|
22
22
|
:accept_filter => 'httpready',
|
23
|
+
|
24
|
+
# same default value as Mongrel
|
25
|
+
:backlog => 1024,
|
23
26
|
}
|
24
27
|
#:startdoc:
|
25
28
|
|
@@ -41,7 +44,6 @@ module Unicorn
|
|
41
44
|
end
|
42
45
|
|
43
46
|
def set_tcp_sockopt(sock, opt)
|
44
|
-
|
45
47
|
# highly portable, but off by default because we don't do keepalive
|
46
48
|
if defined?(TCP_NODELAY) && ! (val = opt[:tcp_nodelay]).nil?
|
47
49
|
sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, val ? 1 : 0)
|
@@ -61,14 +63,12 @@ module Unicorn
|
|
61
63
|
if defined?(TCP_DEFER_ACCEPT)
|
62
64
|
# this differs from nginx, since nginx doesn't allow us to
|
63
65
|
# configure the the timeout...
|
64
|
-
|
65
|
-
seconds = tmp[:tcp_defer_accept]
|
66
|
+
seconds = opt[:tcp_defer_accept]
|
66
67
|
seconds = DEFAULTS[:tcp_defer_accept] if seconds == true
|
67
68
|
seconds = 0 unless seconds # nil/false means disable this
|
68
69
|
sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, seconds)
|
69
70
|
elsif respond_to?(:accf_arg)
|
70
|
-
|
71
|
-
if name = tmp[:accept_filter]
|
71
|
+
if name = opt[:accept_filter]
|
72
72
|
begin
|
73
73
|
sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, accf_arg(name))
|
74
74
|
rescue => e
|
@@ -80,7 +80,7 @@ module Unicorn
|
|
80
80
|
end
|
81
81
|
|
82
82
|
def set_server_sockopt(sock, opt)
|
83
|
-
opt
|
83
|
+
opt = DEFAULTS.merge(opt || {})
|
84
84
|
|
85
85
|
TCPSocket === sock and set_tcp_sockopt(sock, opt)
|
86
86
|
|
@@ -90,7 +90,7 @@ module Unicorn
|
|
90
90
|
sock.setsockopt(SOL_SOCKET, SO_SNDBUF, opt[:sndbuf]) if opt[:sndbuf]
|
91
91
|
log_buffer_sizes(sock, " after: ")
|
92
92
|
end
|
93
|
-
sock.listen(opt[:backlog]
|
93
|
+
sock.listen(opt[:backlog])
|
94
94
|
rescue => e
|
95
95
|
logger.error "error setting socket options: #{e.inspect}"
|
96
96
|
logger.error e.backtrace.join("\n")
|
@@ -28,7 +28,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
28
28
|
assert_equal '', req['QUERY_STRING']
|
29
29
|
|
30
30
|
assert parser.keepalive?
|
31
|
-
parser.
|
31
|
+
parser.clear
|
32
32
|
req.clear
|
33
33
|
|
34
34
|
http = "G"
|
@@ -326,7 +326,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
326
326
|
assert_raises(HttpParserError) { parser.headers(req, bad_http) }
|
327
327
|
|
328
328
|
# make sure we can recover
|
329
|
-
parser.
|
329
|
+
parser.clear
|
330
330
|
req.clear
|
331
331
|
assert_equal req, parser.headers(req, "GET / HTTP/1.0\r\n\r\n")
|
332
332
|
assert ! parser.keepalive?
|
@@ -569,7 +569,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
569
569
|
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
|
570
570
|
assert_raises Unicorn::HttpParserError do
|
571
571
|
parser.headers({}, get)
|
572
|
-
parser.
|
572
|
+
parser.clear
|
573
573
|
end
|
574
574
|
end
|
575
575
|
|
@@ -578,7 +578,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
578
578
|
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
579
579
|
assert_raises Unicorn::HttpParserError do
|
580
580
|
parser.headers({}, get)
|
581
|
-
parser.
|
581
|
+
parser.clear
|
582
582
|
end
|
583
583
|
end
|
584
584
|
|
@@ -587,7 +587,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
587
587
|
get << "X-Test: test\r\n" * (80 * 1024)
|
588
588
|
assert_raises Unicorn::HttpParserError do
|
589
589
|
parser.headers({}, get)
|
590
|
-
parser.
|
590
|
+
parser.clear
|
591
591
|
end
|
592
592
|
|
593
593
|
# finally just that random garbage gets blocked all the time
|
@@ -595,7 +595,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
595
595
|
get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
596
596
|
assert_raises Unicorn::HttpParserError do
|
597
597
|
parser.headers({}, get)
|
598
|
-
parser.
|
598
|
+
parser.clear
|
599
599
|
end
|
600
600
|
end
|
601
601
|
|
@@ -91,7 +91,7 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
91
91
|
@parser.parse
|
92
92
|
end
|
93
93
|
assert @parser.keepalive?
|
94
|
-
@parser.
|
94
|
+
@parser.clear
|
95
95
|
assert ! @parser.keepalive?
|
96
96
|
assert ! @parser.next?
|
97
97
|
end
|
@@ -491,7 +491,7 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
491
491
|
}.each do |uri,expect|
|
492
492
|
assert_equal req, @parser.headers(req.clear, str % [ uri ])
|
493
493
|
req = req.dup
|
494
|
-
@parser.
|
494
|
+
@parser.clear
|
495
495
|
assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
|
496
496
|
assert_equal expect[qs], req[qs], "#{qs} mismatch"
|
497
497
|
assert_equal expect[pi], req[pi], "#{pi} mismatch"
|
@@ -516,7 +516,7 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
516
516
|
}.each do |uri,expect|
|
517
517
|
assert_equal req, @parser.headers(req.clear, str % [ uri ])
|
518
518
|
req = req.dup
|
519
|
-
@parser.
|
519
|
+
@parser.clear
|
520
520
|
assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
|
521
521
|
assert_equal "example.com", req["HTTP_HOST"], "Host: mismatch"
|
522
522
|
assert_equal expect[qs], req[qs], "#{qs} mismatch"
|
@@ -608,4 +608,26 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
608
608
|
assert_equal expect, env2
|
609
609
|
assert_equal "", @parser.buf
|
610
610
|
end
|
611
|
+
|
612
|
+
def test_keepalive_requests_disabled
|
613
|
+
req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
|
614
|
+
expect = {
|
615
|
+
"SERVER_NAME" => "example.com",
|
616
|
+
"HTTP_HOST" => "example.com",
|
617
|
+
"rack.url_scheme" => "http",
|
618
|
+
"REQUEST_PATH" => "/",
|
619
|
+
"SERVER_PROTOCOL" => "HTTP/1.1",
|
620
|
+
"PATH_INFO" => "/",
|
621
|
+
"HTTP_VERSION" => "HTTP/1.1",
|
622
|
+
"REQUEST_URI" => "/",
|
623
|
+
"SERVER_PORT" => "80",
|
624
|
+
"REQUEST_METHOD" => "GET",
|
625
|
+
"QUERY_STRING" => ""
|
626
|
+
}.freeze
|
627
|
+
HttpParser.keepalive_requests = 0
|
628
|
+
@parser = HttpParser.new
|
629
|
+
@parser.buf << req
|
630
|
+
assert_equal expect, @parser.parse
|
631
|
+
assert ! @parser.next?
|
632
|
+
end
|
611
633
|
end
|
data/test/unit/test_response.rb
CHANGED
@@ -7,15 +7,26 @@
|
|
7
7
|
# for more information.
|
8
8
|
|
9
9
|
require 'test/test_helper'
|
10
|
+
require 'time'
|
10
11
|
|
11
12
|
include Unicorn
|
12
13
|
|
13
14
|
class ResponseTest < Test::Unit::TestCase
|
14
15
|
include Unicorn::HttpResponse
|
15
16
|
|
17
|
+
def test_httpdate
|
18
|
+
before = Time.now.to_i
|
19
|
+
str = httpdate
|
20
|
+
assert_kind_of(String, str)
|
21
|
+
middle = Time.parse(str).to_i
|
22
|
+
after = Time.now.to_i
|
23
|
+
assert before <= middle
|
24
|
+
assert middle <= after
|
25
|
+
end
|
26
|
+
|
16
27
|
def test_response_headers
|
17
28
|
out = StringIO.new
|
18
|
-
http_response_write(out,
|
29
|
+
http_response_write(out, 200, {"X-Whatever" => "stuff"}, ["cool"])
|
19
30
|
assert out.closed?
|
20
31
|
|
21
32
|
assert out.length > 0, "output didn't have data"
|
@@ -23,27 +34,15 @@ class ResponseTest < Test::Unit::TestCase
|
|
23
34
|
|
24
35
|
def test_response_string_status
|
25
36
|
out = StringIO.new
|
26
|
-
http_response_write(out,
|
37
|
+
http_response_write(out,'200', {}, [])
|
27
38
|
assert out.closed?
|
28
39
|
assert out.length > 0, "output didn't have data"
|
29
40
|
assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/).size
|
30
41
|
end
|
31
42
|
|
32
|
-
def test_response_OFS_set
|
33
|
-
old_ofs = $,
|
34
|
-
$, = "\f\v"
|
35
|
-
out = StringIO.new
|
36
|
-
http_response_write(out,[200, {"X-k" => "cd","X-y" => "z"}, ["cool"]])
|
37
|
-
assert out.closed?
|
38
|
-
resp = out.string
|
39
|
-
assert ! resp.include?("\f\v"), "output didn't use $, ($OFS)"
|
40
|
-
ensure
|
41
|
-
$, = old_ofs
|
42
|
-
end
|
43
|
-
|
44
43
|
def test_response_200
|
45
44
|
io = StringIO.new
|
46
|
-
http_response_write(io,
|
45
|
+
http_response_write(io, 200, {}, [])
|
47
46
|
assert io.closed?
|
48
47
|
assert io.length > 0, "output didn't have data"
|
49
48
|
end
|
@@ -51,7 +50,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
51
50
|
def test_response_with_default_reason
|
52
51
|
code = 400
|
53
52
|
io = StringIO.new
|
54
|
-
http_response_write(io,
|
53
|
+
http_response_write(io, code, {}, [])
|
55
54
|
assert io.closed?
|
56
55
|
lines = io.string.split(/\r\n/)
|
57
56
|
assert_match(/.* Bad Request$/, lines.first,
|
@@ -60,7 +59,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
60
59
|
|
61
60
|
def test_rack_multivalue_headers
|
62
61
|
out = StringIO.new
|
63
|
-
http_response_write(out,
|
62
|
+
http_response_write(out,200, {"X-Whatever" => "stuff\nbleh"}, [])
|
64
63
|
assert out.closed?
|
65
64
|
assert_match(/^X-Whatever: stuff\r\nX-Whatever: bleh\r\n/, out.string)
|
66
65
|
end
|
@@ -69,7 +68,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
69
68
|
# some broken clients still rely on it
|
70
69
|
def test_status_header_added
|
71
70
|
out = StringIO.new
|
72
|
-
http_response_write(out,
|
71
|
+
http_response_write(out,200, {"X-Whatever" => "stuff"}, [])
|
73
72
|
assert out.closed?
|
74
73
|
assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/i).size
|
75
74
|
end
|
@@ -80,7 +79,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
80
79
|
def test_status_header_ignores_app_hash
|
81
80
|
out = StringIO.new
|
82
81
|
header_hash = {"X-Whatever" => "stuff", 'StaTus' => "666" }
|
83
|
-
http_response_write(out,
|
82
|
+
http_response_write(out,200, header_hash, [])
|
84
83
|
assert out.closed?
|
85
84
|
assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/i).size
|
86
85
|
assert_equal 1, out.string.split(/\r\n/).grep(/^Status:/i).size
|
@@ -91,7 +90,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
91
90
|
body = StringIO.new(expect_body)
|
92
91
|
body.rewind
|
93
92
|
out = StringIO.new
|
94
|
-
http_response_write(out,
|
93
|
+
http_response_write(out,200, {}, body)
|
95
94
|
assert out.closed?
|
96
95
|
assert body.closed?
|
97
96
|
assert_match(expect_body, out.string.split(/\r\n/).last)
|
@@ -99,7 +98,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
99
98
|
|
100
99
|
def test_unknown_status_pass_through
|
101
100
|
out = StringIO.new
|
102
|
-
http_response_write(out,
|
101
|
+
http_response_write(out,"666 I AM THE BEAST", {}, [] )
|
103
102
|
assert out.closed?
|
104
103
|
headers = out.string.split(/\r\n\r\n/).first.split(/\r\n/)
|
105
104
|
assert %r{\AHTTP/\d\.\d 666 I AM THE BEAST\z}.match(headers[0])
|
data/unicorn.gemspec
CHANGED
@@ -38,7 +38,7 @@ Gem::Specification.new do |s|
|
|
38
38
|
s.add_dependency(%q<kgio>, '~> 2.1')
|
39
39
|
|
40
40
|
s.add_development_dependency('isolate', '~> 3.0.0')
|
41
|
-
s.add_development_dependency('wrongdoc', '~> 1.
|
41
|
+
s.add_development_dependency('wrongdoc', '~> 1.3')
|
42
42
|
|
43
43
|
# s.licenses = %w(GPLv2 Ruby) # licenses= method is not in older RubyGems
|
44
44
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unicorn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 3
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 3.
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 3.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Unicorn hackers
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-01-05 00:00:00 +00:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -71,12 +71,11 @@ dependencies:
|
|
71
71
|
requirements:
|
72
72
|
- - ~>
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
hash:
|
74
|
+
hash: 9
|
75
75
|
segments:
|
76
76
|
- 1
|
77
|
-
-
|
78
|
-
|
79
|
-
version: 1.0.1
|
77
|
+
- 3
|
78
|
+
version: "1.3"
|
80
79
|
type: :development
|
81
80
|
version_requirements: *id004
|
82
81
|
description: |-
|
@@ -177,6 +176,7 @@ files:
|
|
177
176
|
- ext/unicorn_http/ext_help.h
|
178
177
|
- ext/unicorn_http/extconf.rb
|
179
178
|
- ext/unicorn_http/global_variables.h
|
179
|
+
- ext/unicorn_http/httpdate.c
|
180
180
|
- ext/unicorn_http/unicorn_http.c
|
181
181
|
- ext/unicorn_http/unicorn_http.rl
|
182
182
|
- ext/unicorn_http/unicorn_http_common.rl
|