unicorn 4.9.0 → 5.0.0.pre1
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.
- checksums.yaml +4 -4
- data/FAQ +17 -8
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +6 -1
- data/ISSUES +2 -1
- data/TUNING +6 -3
- data/bin/unicorn +1 -1
- data/bin/unicorn_rails +1 -1
- data/ext/unicorn_http/extconf.rb +1 -0
- data/ext/unicorn_http/httpdate.c +1 -1
- data/ext/unicorn_http/unicorn_http.rl +88 -155
- data/lib/unicorn.rb +7 -15
- data/lib/unicorn/configurator.rb +2 -17
- data/lib/unicorn/const.rb +2 -25
- data/lib/unicorn/http_request.rb +22 -28
- data/lib/unicorn/http_response.rb +5 -20
- data/lib/unicorn/http_server.rb +112 -117
- data/lib/unicorn/socket_helper.rb +33 -67
- data/lib/unicorn/tmpio.rb +0 -5
- data/lib/unicorn/util.rb +1 -0
- data/lib/unicorn/worker.rb +1 -13
- data/t/hijack.ru +2 -1
- data/t/t0200-rack-hijack.sh +5 -2
- data/test/test_helper.rb +3 -2
- data/test/unit/test_http_parser_ng.rb +16 -114
- data/test/unit/test_response.rb +0 -17
- data/test/unit/test_socket_helper.rb +1 -1
- metadata +4 -16
- data/examples/git.ru +0 -13
- data/lib/unicorn/app/exec_cgi.rb +0 -154
- data/lib/unicorn/app/inetd.rb +0 -109
- data/lib/unicorn/ssl_client.rb +0 -11
- data/lib/unicorn/ssl_configurator.rb +0 -104
- data/lib/unicorn/ssl_server.rb +0 -42
- data/t/t0016-trust-x-forwarded-false.sh +0 -30
- data/t/t0017-trust-x-forwarded-true.sh +0 -30
- data/test/unit/test_http_parser_xftrust.rb +0 -38
- data/test/unit/test_sni_hostnames.rb +0 -47
@@ -4,12 +4,6 @@ require 'socket'
|
|
4
4
|
|
5
5
|
module Unicorn
|
6
6
|
module SocketHelper
|
7
|
-
# :stopdoc:
|
8
|
-
include Socket::Constants
|
9
|
-
|
10
|
-
# prevents IO objects in here from being GC-ed
|
11
|
-
# kill this when we drop 1.8 support
|
12
|
-
IO_PURGATORY = []
|
13
7
|
|
14
8
|
# internal interface, only used by Rainbows!/Zbatery
|
15
9
|
DEFAULTS = {
|
@@ -22,7 +16,7 @@ module Unicorn
|
|
22
16
|
:tcp_defer_accept => 1,
|
23
17
|
|
24
18
|
# FreeBSD, we need to override this to 'dataready' if we
|
25
|
-
# eventually
|
19
|
+
# eventually support non-HTTP/1.x
|
26
20
|
:accept_filter => 'httpready',
|
27
21
|
|
28
22
|
# same default value as Mongrel
|
@@ -32,76 +26,47 @@ module Unicorn
|
|
32
26
|
:tcp_nopush => nil,
|
33
27
|
:tcp_nodelay => true,
|
34
28
|
}
|
35
|
-
#:startdoc:
|
36
29
|
|
37
30
|
# configure platform-specific options (only tested on Linux 2.6 so far)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
TCP_DEFER_ACCEPT = 9 unless defined?(TCP_DEFER_ACCEPT)
|
42
|
-
|
43
|
-
# do not send out partial frames (Linux)
|
44
|
-
TCP_CORK = 3 unless defined?(TCP_CORK)
|
45
|
-
|
46
|
-
# Linux got SO_REUSEPORT in 3.9, BSDs have had it for ages
|
47
|
-
unless defined?(SO_REUSEPORT)
|
48
|
-
if RUBY_PLATFORM =~ /(?:alpha|mips|parisc|sparc)/
|
49
|
-
SO_REUSEPORT = 0x0200 # untested
|
50
|
-
else
|
51
|
-
SO_REUSEPORT = 15 # only tested on x86_64 and i686
|
52
|
-
end
|
53
|
-
end
|
54
|
-
when /freebsd/
|
55
|
-
# do not send out partial frames (FreeBSD)
|
56
|
-
TCP_NOPUSH = 4 unless defined?(TCP_NOPUSH)
|
57
|
-
|
58
|
-
def accf_arg(af_name)
|
59
|
-
[ af_name, nil ].pack('a16a240')
|
60
|
-
end if defined?(SO_ACCEPTFILTER)
|
61
|
-
end
|
62
|
-
|
63
|
-
def prevent_autoclose(io)
|
64
|
-
if io.respond_to?(:autoclose=)
|
65
|
-
io.autoclose = false
|
66
|
-
else
|
67
|
-
IO_PURGATORY << io
|
68
|
-
end
|
69
|
-
end
|
31
|
+
def accf_arg(af_name)
|
32
|
+
[ af_name, nil ].pack('a16a240')
|
33
|
+
end if RUBY_PLATFORM =~ /freebsd/ && Socket.const_defined?(:SO_ACCEPTFILTER)
|
70
34
|
|
71
35
|
def set_tcp_sockopt(sock, opt)
|
72
36
|
# just in case, even LANs can break sometimes. Linux sysadmins
|
73
37
|
# can lower net.ipv4.tcp_keepalive_* sysctl knobs to very low values.
|
74
|
-
|
38
|
+
Socket.const_defined?(:SO_KEEPALIVE) and
|
39
|
+
sock.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 1)
|
75
40
|
|
76
|
-
if
|
41
|
+
if Socket.const_defined?(:TCP_NODELAY)
|
77
42
|
val = opt[:tcp_nodelay]
|
78
|
-
val = DEFAULTS[:tcp_nodelay] if nil
|
79
|
-
sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, val ? 1 : 0)
|
43
|
+
val = DEFAULTS[:tcp_nodelay] if val.nil?
|
44
|
+
sock.setsockopt(:IPPROTO_TCP, :TCP_NODELAY, val ? 1 : 0)
|
80
45
|
end
|
81
46
|
|
82
47
|
val = opt[:tcp_nopush]
|
83
48
|
unless val.nil?
|
84
|
-
if
|
85
|
-
sock.setsockopt(IPPROTO_TCP, TCP_CORK, val)
|
86
|
-
elsif
|
87
|
-
sock.setsockopt(IPPROTO_TCP, TCP_NOPUSH, val)
|
49
|
+
if Socket.const_defined?(:TCP_CORK) # Linux
|
50
|
+
sock.setsockopt(:IPPROTO_TCP, :TCP_CORK, val)
|
51
|
+
elsif Socket.const_defined?(:TCP_NOPUSH) # FreeBSD
|
52
|
+
sock.setsockopt(:IPPROTO_TCP, :TCP_NOPUSH, val)
|
88
53
|
end
|
89
54
|
end
|
90
55
|
|
91
|
-
# No good reason to ever have deferred accepts off
|
92
|
-
# (except maybe benchmarking)
|
93
|
-
if
|
56
|
+
# No good reason to ever have deferred accepts off in single-threaded
|
57
|
+
# servers (except maybe benchmarking)
|
58
|
+
if Socket.const_defined?(:TCP_DEFER_ACCEPT)
|
94
59
|
# this differs from nginx, since nginx doesn't allow us to
|
95
60
|
# configure the the timeout...
|
96
61
|
seconds = opt[:tcp_defer_accept]
|
97
62
|
seconds = DEFAULTS[:tcp_defer_accept] if [true,nil].include?(seconds)
|
98
63
|
seconds = 0 unless seconds # nil/false means disable this
|
99
|
-
sock.setsockopt(
|
64
|
+
sock.setsockopt(:IPPROTO_TCP, :TCP_DEFER_ACCEPT, seconds)
|
100
65
|
elsif respond_to?(:accf_arg)
|
101
66
|
name = opt[:accept_filter]
|
102
|
-
name = DEFAULTS[:accept_filter] if nil
|
67
|
+
name = DEFAULTS[:accept_filter] if name.nil?
|
103
68
|
begin
|
104
|
-
sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, accf_arg(name))
|
69
|
+
sock.setsockopt(:SOL_SOCKET, :SO_ACCEPTFILTER, accf_arg(name))
|
105
70
|
rescue => e
|
106
71
|
logger.error("#{sock_name(sock)} " \
|
107
72
|
"failed to set accept_filter=#{name} (#{e.inspect})")
|
@@ -114,10 +79,11 @@ module Unicorn
|
|
114
79
|
|
115
80
|
TCPSocket === sock and set_tcp_sockopt(sock, opt)
|
116
81
|
|
117
|
-
|
82
|
+
rcvbuf, sndbuf = opt.values_at(:rcvbuf, :sndbuf)
|
83
|
+
if rcvbuf || sndbuf
|
118
84
|
log_buffer_sizes(sock, "before: ")
|
119
|
-
sock.setsockopt(SOL_SOCKET, SO_RCVBUF,
|
120
|
-
sock.setsockopt(SOL_SOCKET, SO_SNDBUF,
|
85
|
+
sock.setsockopt(:SOL_SOCKET, :SO_RCVBUF, rcvbuf) if rcvbuf
|
86
|
+
sock.setsockopt(:SOL_SOCKET, :SO_SNDBUF, sndbuf) if sndbuf
|
121
87
|
log_buffer_sizes(sock, " after: ")
|
122
88
|
end
|
123
89
|
sock.listen(opt[:backlog])
|
@@ -126,8 +92,8 @@ module Unicorn
|
|
126
92
|
end
|
127
93
|
|
128
94
|
def log_buffer_sizes(sock, pfx = '')
|
129
|
-
rcvbuf = sock.getsockopt(SOL_SOCKET, SO_RCVBUF).
|
130
|
-
sndbuf = sock.getsockopt(SOL_SOCKET, SO_SNDBUF).
|
95
|
+
rcvbuf = sock.getsockopt(:SOL_SOCKET, :SO_RCVBUF).int
|
96
|
+
sndbuf = sock.getsockopt(:SOL_SOCKET, :SO_SNDBUF).int
|
131
97
|
logger.info "#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}"
|
132
98
|
end
|
133
99
|
|
@@ -172,25 +138,25 @@ module Unicorn
|
|
172
138
|
|
173
139
|
def new_tcp_server(addr, port, opt)
|
174
140
|
# n.b. we set FD_CLOEXEC in the workers
|
175
|
-
sock = Socket.new(opt[:ipv6] ? AF_INET6 : AF_INET, SOCK_STREAM
|
141
|
+
sock = Socket.new(opt[:ipv6] ? :AF_INET6 : :AF_INET, :SOCK_STREAM)
|
176
142
|
if opt.key?(:ipv6only)
|
177
|
-
|
143
|
+
Socket.const_defined?(:IPV6_V6ONLY) or
|
178
144
|
abort "Socket::IPV6_V6ONLY not defined, upgrade Ruby and/or your OS"
|
179
|
-
sock.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, opt[:ipv6only] ? 1 : 0)
|
145
|
+
sock.setsockopt(:IPPROTO_IPV6, :IPV6_V6ONLY, opt[:ipv6only] ? 1 : 0)
|
180
146
|
end
|
181
|
-
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
|
182
|
-
if
|
183
|
-
sock.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
|
147
|
+
sock.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1)
|
148
|
+
if Socket.const_defined?(:SO_REUSEPORT) && opt[:reuseport]
|
149
|
+
sock.setsockopt(:SOL_SOCKET, :SO_REUSEPORT, 1)
|
184
150
|
end
|
185
151
|
sock.bind(Socket.pack_sockaddr_in(port, addr))
|
186
|
-
|
152
|
+
sock.autoclose = false
|
187
153
|
Kgio::TCPServer.for_fd(sock.fileno)
|
188
154
|
end
|
189
155
|
|
190
156
|
# returns rfc2732-style (e.g. "[::1]:666") addresses for IPv6
|
191
157
|
def tcp_name(sock)
|
192
158
|
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
193
|
-
|
159
|
+
addr.include?(':') ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
|
194
160
|
end
|
195
161
|
module_function :tcp_name
|
196
162
|
|
data/lib/unicorn/tmpio.rb
CHANGED
@@ -22,11 +22,6 @@ class Unicorn::TmpIO < File
|
|
22
22
|
fp
|
23
23
|
end
|
24
24
|
|
25
|
-
# for easier env["rack.input"] compatibility with Rack <= 1.1
|
26
|
-
def size
|
27
|
-
stat.size
|
28
|
-
end unless File.method_defined?(:size)
|
29
|
-
|
30
25
|
# pretend we're Tempfile for Rack::TempfileReaper
|
31
26
|
alias close! close
|
32
27
|
end
|
data/lib/unicorn/util.rb
CHANGED
data/lib/unicorn/worker.rb
CHANGED
@@ -11,7 +11,6 @@ require "raindrops"
|
|
11
11
|
class Unicorn::Worker
|
12
12
|
# :stopdoc:
|
13
13
|
attr_accessor :nr, :switched
|
14
|
-
attr_writer :tmp
|
15
14
|
attr_reader :to_io # IO.select-compatible
|
16
15
|
|
17
16
|
PER_DROP = Raindrops::PAGE_SIZE / Raindrops::SIZE
|
@@ -23,7 +22,7 @@ class Unicorn::Worker
|
|
23
22
|
@offset = nr % PER_DROP
|
24
23
|
@raindrop[@offset] = 0
|
25
24
|
@nr = nr
|
26
|
-
@
|
25
|
+
@switched = false
|
27
26
|
@to_io, @master = Unicorn.pipe
|
28
27
|
end
|
29
28
|
|
@@ -101,18 +100,8 @@ class Unicorn::Worker
|
|
101
100
|
@raindrop[@offset]
|
102
101
|
end
|
103
102
|
|
104
|
-
# only exists for compatibility
|
105
|
-
def tmp # :nodoc:
|
106
|
-
@tmp ||= begin
|
107
|
-
tmp = Unicorn::TmpIO.new
|
108
|
-
tmp.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
109
|
-
tmp
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
103
|
# called in both the master (reaping worker) and worker (SIGQUIT handler)
|
114
104
|
def close # :nodoc:
|
115
|
-
@tmp.close if @tmp
|
116
105
|
@master.close if @master
|
117
106
|
@to_io.close if @to_io
|
118
107
|
end
|
@@ -141,7 +130,6 @@ class Unicorn::Worker
|
|
141
130
|
uid = Etc.getpwnam(user).uid
|
142
131
|
gid = Etc.getgrnam(group).gid if group
|
143
132
|
Unicorn::Util.chown_logs(uid, gid)
|
144
|
-
@tmp.chown(uid, gid) if @tmp
|
145
133
|
if gid && Process.egid != gid
|
146
134
|
Process.initgroups(user, gid)
|
147
135
|
Process::GID.change_privilege(gid)
|
data/t/hijack.ru
CHANGED
@@ -2,12 +2,13 @@ use Rack::Lint
|
|
2
2
|
use Rack::ContentLength
|
3
3
|
use Rack::ContentType, "text/plain"
|
4
4
|
class DieIfUsed
|
5
|
+
@@n = 0
|
5
6
|
def each
|
6
7
|
abort "body.each called after response hijack\n"
|
7
8
|
end
|
8
9
|
|
9
10
|
def close
|
10
|
-
|
11
|
+
warn "closed DieIfUsed #{@@n += 1}\n"
|
11
12
|
end
|
12
13
|
end
|
13
14
|
run lambda { |env|
|
data/t/t0200-rack-hijack.sh
CHANGED
@@ -16,12 +16,15 @@ t_begin "check response hijack" && {
|
|
16
16
|
test "xresponse.hijacked" = x"$(curl -sSfv http://$listen/hijack_res)"
|
17
17
|
}
|
18
18
|
|
19
|
-
t_begin "killing succeeds" && {
|
19
|
+
t_begin "killing succeeds after hijack" && {
|
20
20
|
kill $unicorn_pid
|
21
21
|
}
|
22
22
|
|
23
|
-
t_begin "check stderr" && {
|
23
|
+
t_begin "check stderr for hijacked body close" && {
|
24
24
|
check_stderr
|
25
|
+
grep 'closed DieIfUsed 1\>' $r_err
|
26
|
+
grep 'closed DieIfUsed 2\>' $r_err
|
27
|
+
! grep 'closed DieIfUsed 3\>' $r_err
|
25
28
|
}
|
26
29
|
|
27
30
|
t_done
|
data/test/test_helper.rb
CHANGED
@@ -292,6 +292,7 @@ def chunked_spawn(stdout, *cmd)
|
|
292
292
|
end
|
293
293
|
|
294
294
|
def reset_sig_handlers
|
295
|
-
|
296
|
-
|
295
|
+
%w(WINCH QUIT INT TERM USR1 USR2 HUP TTIN TTOU CHLD).each do |sig|
|
296
|
+
trap(sig, "DEFAULT")
|
297
|
+
end
|
297
298
|
end
|
@@ -8,10 +8,15 @@ include Unicorn
|
|
8
8
|
class HttpParserNgTest < Test::Unit::TestCase
|
9
9
|
|
10
10
|
def setup
|
11
|
-
HttpParser.keepalive_requests = HttpParser::KEEPALIVE_REQUESTS_DEFAULT
|
12
11
|
@parser = HttpParser.new
|
13
12
|
end
|
14
13
|
|
14
|
+
def test_parser_max_len
|
15
|
+
assert_raises(RangeError) do
|
16
|
+
HttpParser.max_header_len = 0xffffffff + 1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
15
20
|
def test_next_clear
|
16
21
|
r = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
|
17
22
|
@parser.buf << r
|
@@ -29,23 +34,15 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
29
34
|
assert_equal false, @parser.response_start_sent
|
30
35
|
end
|
31
36
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
assert HttpParser.keepalive_requests >= 0xffffffff
|
42
|
-
HttpParser.keepalive_requests = 1
|
43
|
-
assert_equal 1, HttpParser.keepalive_requests
|
44
|
-
HttpParser.keepalive_requests = 666
|
45
|
-
assert_equal 666, HttpParser.keepalive_requests
|
46
|
-
|
47
|
-
assert_raises(TypeError) { HttpParser.keepalive_requests = "666" }
|
48
|
-
assert_raises(TypeError) { HttpParser.keepalive_requests = [] }
|
37
|
+
def test_response_start_sent
|
38
|
+
assert_equal false, @parser.response_start_sent, "default is false"
|
39
|
+
@parser.response_start_sent = true
|
40
|
+
assert_equal true, @parser.response_start_sent
|
41
|
+
@parser.response_start_sent = false
|
42
|
+
assert_equal false, @parser.response_start_sent
|
43
|
+
@parser.response_start_sent = true
|
44
|
+
@parser.clear
|
45
|
+
assert_equal false, @parser.response_start_sent
|
49
46
|
end
|
50
47
|
|
51
48
|
def test_connection_TE
|
@@ -71,41 +68,11 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
71
68
|
"REQUEST_METHOD" => "GET",
|
72
69
|
"QUERY_STRING" => ""
|
73
70
|
}.freeze
|
74
|
-
|
71
|
+
100.times do |nr|
|
75
72
|
@parser.buf << req
|
76
73
|
assert_equal expect, @parser.parse
|
77
74
|
assert @parser.next?
|
78
75
|
end
|
79
|
-
@parser.buf << req
|
80
|
-
assert_equal expect, @parser.parse
|
81
|
-
assert ! @parser.next?
|
82
|
-
end
|
83
|
-
|
84
|
-
def test_fewer_keepalive_requests_with_next?
|
85
|
-
HttpParser.keepalive_requests = 5
|
86
|
-
@parser = HttpParser.new
|
87
|
-
req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
|
88
|
-
expect = {
|
89
|
-
"SERVER_NAME" => "example.com",
|
90
|
-
"HTTP_HOST" => "example.com",
|
91
|
-
"rack.url_scheme" => "http",
|
92
|
-
"REQUEST_PATH" => "/",
|
93
|
-
"SERVER_PROTOCOL" => "HTTP/1.1",
|
94
|
-
"PATH_INFO" => "/",
|
95
|
-
"HTTP_VERSION" => "HTTP/1.1",
|
96
|
-
"REQUEST_URI" => "/",
|
97
|
-
"SERVER_PORT" => "80",
|
98
|
-
"REQUEST_METHOD" => "GET",
|
99
|
-
"QUERY_STRING" => ""
|
100
|
-
}.freeze
|
101
|
-
5.times do |nr|
|
102
|
-
@parser.buf << req
|
103
|
-
assert_equal expect, @parser.parse
|
104
|
-
assert @parser.next?
|
105
|
-
end
|
106
|
-
@parser.buf << req
|
107
|
-
assert_equal expect, @parser.parse
|
108
|
-
assert ! @parser.next?
|
109
76
|
end
|
110
77
|
|
111
78
|
def test_default_keepalive_is_off
|
@@ -663,69 +630,4 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
663
630
|
assert_equal expect, env2
|
664
631
|
assert_equal "", @parser.buf
|
665
632
|
end
|
666
|
-
|
667
|
-
def test_keepalive_requests_disabled
|
668
|
-
req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
|
669
|
-
expect = {
|
670
|
-
"SERVER_NAME" => "example.com",
|
671
|
-
"HTTP_HOST" => "example.com",
|
672
|
-
"rack.url_scheme" => "http",
|
673
|
-
"REQUEST_PATH" => "/",
|
674
|
-
"SERVER_PROTOCOL" => "HTTP/1.1",
|
675
|
-
"PATH_INFO" => "/",
|
676
|
-
"HTTP_VERSION" => "HTTP/1.1",
|
677
|
-
"REQUEST_URI" => "/",
|
678
|
-
"SERVER_PORT" => "80",
|
679
|
-
"REQUEST_METHOD" => "GET",
|
680
|
-
"QUERY_STRING" => ""
|
681
|
-
}.freeze
|
682
|
-
HttpParser.keepalive_requests = 0
|
683
|
-
@parser = HttpParser.new
|
684
|
-
@parser.buf << req
|
685
|
-
assert_equal expect, @parser.parse
|
686
|
-
assert ! @parser.next?
|
687
|
-
end
|
688
|
-
|
689
|
-
def test_chunk_only
|
690
|
-
tmp = ""
|
691
|
-
assert_equal @parser, @parser.dechunk!
|
692
|
-
assert_nil @parser.filter_body(tmp, "6\r\n")
|
693
|
-
assert_equal "", tmp
|
694
|
-
assert_nil @parser.filter_body(tmp, "abcdef")
|
695
|
-
assert_equal "abcdef", tmp
|
696
|
-
assert_nil @parser.filter_body(tmp, "\r\n")
|
697
|
-
assert_equal "", tmp
|
698
|
-
src = "0\r\n\r\n"
|
699
|
-
assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
|
700
|
-
assert_equal "", tmp
|
701
|
-
end
|
702
|
-
|
703
|
-
def test_chunk_only_bad_align
|
704
|
-
tmp = ""
|
705
|
-
assert_equal @parser, @parser.dechunk!
|
706
|
-
assert_nil @parser.filter_body(tmp, "6\r\na")
|
707
|
-
assert_equal "a", tmp
|
708
|
-
assert_nil @parser.filter_body(tmp, "bcde")
|
709
|
-
assert_equal "bcde", tmp
|
710
|
-
assert_nil @parser.filter_body(tmp, "f\r")
|
711
|
-
assert_equal "f", tmp
|
712
|
-
src = "\n0\r\n\r\n"
|
713
|
-
assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
|
714
|
-
assert_equal "", tmp
|
715
|
-
end
|
716
|
-
|
717
|
-
def test_chunk_only_reset_ok
|
718
|
-
tmp = ""
|
719
|
-
assert_equal @parser, @parser.dechunk!
|
720
|
-
src = "1\r\na\r\n0\r\n\r\n"
|
721
|
-
assert_nil @parser.filter_body(tmp, src)
|
722
|
-
assert_equal "a", tmp
|
723
|
-
assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
|
724
|
-
|
725
|
-
assert_equal @parser, @parser.dechunk!
|
726
|
-
src = "0\r\n\r\n"
|
727
|
-
assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
|
728
|
-
assert_equal "", tmp
|
729
|
-
assert_equal src, @parser.filter_body(tmp, src)
|
730
|
-
end
|
731
633
|
end
|
data/test/unit/test_response.rb
CHANGED
@@ -38,7 +38,6 @@ class ResponseTest < Test::Unit::TestCase
|
|
38
38
|
http_response_write(out,'200', {}, [])
|
39
39
|
assert ! out.closed?
|
40
40
|
assert out.length > 0, "output didn't have data"
|
41
|
-
assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/).size
|
42
41
|
end
|
43
42
|
|
44
43
|
def test_response_200
|
@@ -71,18 +70,6 @@ class ResponseTest < Test::Unit::TestCase
|
|
71
70
|
out = StringIO.new
|
72
71
|
http_response_write(out,200, {"X-Whatever" => "stuff"}, [])
|
73
72
|
assert ! out.closed?
|
74
|
-
assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/i).size
|
75
|
-
end
|
76
|
-
|
77
|
-
def test_body_closed
|
78
|
-
expect_body = %w(1 2 3 4).join("\n")
|
79
|
-
body = StringIO.new(expect_body)
|
80
|
-
body.rewind
|
81
|
-
out = StringIO.new
|
82
|
-
http_response_write(out,200, {}, body)
|
83
|
-
assert ! out.closed?
|
84
|
-
assert body.closed?
|
85
|
-
assert_match(expect_body, out.string.split(/\r\n/).last)
|
86
73
|
end
|
87
74
|
|
88
75
|
def test_unknown_status_pass_through
|
@@ -91,9 +78,5 @@ class ResponseTest < Test::Unit::TestCase
|
|
91
78
|
assert ! out.closed?
|
92
79
|
headers = out.string.split(/\r\n\r\n/).first.split(/\r\n/)
|
93
80
|
assert %r{\AHTTP/\d\.\d 666 I AM THE BEAST\z}.match(headers[0])
|
94
|
-
status = headers.grep(/\AStatus:/i).first
|
95
|
-
assert status
|
96
|
-
assert_equal "Status: 666 I AM THE BEAST", status
|
97
81
|
end
|
98
|
-
|
99
82
|
end
|