unicorn 4.9.0 → 5.0.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.
- checksums.yaml +4 -4
- data/Application_Timeouts +3 -3
- data/DESIGN +2 -4
- data/Documentation/unicorn.1.txt +8 -5
- data/Documentation/unicorn_rails.1.txt +2 -2
- data/FAQ +17 -8
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +6 -1
- data/ISSUES +20 -28
- data/KNOWN_ISSUES +9 -9
- data/Links +14 -17
- data/PHILOSOPHY +0 -6
- data/README +22 -17
- data/SIGNALS +1 -1
- data/Sandbox +4 -4
- data/TUNING +11 -8
- data/bin/unicorn +1 -1
- data/bin/unicorn_rails +1 -1
- data/examples/nginx.conf +10 -11
- data/examples/unicorn.conf.rb +1 -4
- data/ext/unicorn_http/extconf.rb +1 -0
- data/ext/unicorn_http/httpdate.c +1 -1
- data/ext/unicorn_http/unicorn_http.rl +89 -156
- data/lib/unicorn.rb +10 -18
- data/lib/unicorn/configurator.rb +17 -31
- data/lib/unicorn/const.rb +2 -25
- data/lib/unicorn/http_request.rb +22 -33
- data/lib/unicorn/http_response.rb +14 -32
- data/lib/unicorn/http_server.rb +129 -122
- data/lib/unicorn/socket_helper.rb +36 -72
- data/lib/unicorn/stream_input.rb +3 -3
- data/lib/unicorn/tmpio.rb +0 -5
- data/lib/unicorn/util.rb +2 -1
- data/lib/unicorn/worker.rb +3 -15
- data/t/hijack.ru +2 -1
- data/t/t0200-rack-hijack.sh +5 -2
- data/test/exec/test_exec.rb +52 -0
- data/test/test_helper.rb +3 -2
- data/test/unit/test_http_parser_ng.rb +16 -114
- data/test/unit/test_response.rb +19 -16
- data/test/unit/test_socket_helper.rb +1 -1
- data/unicorn.gemspec +10 -1
- metadata +10 -23
- 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,25 +4,17 @@ require 'socket'
|
|
4
4
|
|
5
5
|
module Unicorn
|
6
6
|
module SocketHelper
|
7
|
-
# :stopdoc:
|
8
|
-
include Socket::Constants
|
9
7
|
|
10
|
-
#
|
11
|
-
# kill this when we drop 1.8 support
|
12
|
-
IO_PURGATORY = []
|
13
|
-
|
14
|
-
# internal interface, only used by Rainbows!/Zbatery
|
8
|
+
# internal interface
|
15
9
|
DEFAULTS = {
|
16
10
|
# The semantics for TCP_DEFER_ACCEPT changed in Linux 2.6.32+
|
17
11
|
# with commit d1b99ba41d6c5aa1ed2fc634323449dd656899e9
|
18
|
-
# This change shouldn't affect
|
19
|
-
# value of 1 remains an optimization)
|
20
|
-
# want to use a higher value on Linux 2.6.32+ to protect against
|
21
|
-
# denial-of-service attacks
|
12
|
+
# This change shouldn't affect unicorn users behind nginx (a
|
13
|
+
# value of 1 remains an optimization).
|
22
14
|
:tcp_defer_accept => 1,
|
23
15
|
|
24
16
|
# FreeBSD, we need to override this to 'dataready' if we
|
25
|
-
# eventually
|
17
|
+
# eventually support non-HTTP/1.x
|
26
18
|
:accept_filter => 'httpready',
|
27
19
|
|
28
20
|
# same default value as Mongrel
|
@@ -32,76 +24,47 @@ module Unicorn
|
|
32
24
|
:tcp_nopush => nil,
|
33
25
|
:tcp_nodelay => true,
|
34
26
|
}
|
35
|
-
#:startdoc:
|
36
27
|
|
37
28
|
# 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
|
29
|
+
def accf_arg(af_name)
|
30
|
+
[ af_name, nil ].pack('a16a240')
|
31
|
+
end if RUBY_PLATFORM =~ /freebsd/ && Socket.const_defined?(:SO_ACCEPTFILTER)
|
70
32
|
|
71
33
|
def set_tcp_sockopt(sock, opt)
|
72
34
|
# just in case, even LANs can break sometimes. Linux sysadmins
|
73
35
|
# can lower net.ipv4.tcp_keepalive_* sysctl knobs to very low values.
|
74
|
-
|
36
|
+
Socket.const_defined?(:SO_KEEPALIVE) and
|
37
|
+
sock.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 1)
|
75
38
|
|
76
|
-
if
|
39
|
+
if Socket.const_defined?(:TCP_NODELAY)
|
77
40
|
val = opt[:tcp_nodelay]
|
78
|
-
val = DEFAULTS[:tcp_nodelay] if nil
|
79
|
-
sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, val ? 1 : 0)
|
41
|
+
val = DEFAULTS[:tcp_nodelay] if val.nil?
|
42
|
+
sock.setsockopt(:IPPROTO_TCP, :TCP_NODELAY, val ? 1 : 0)
|
80
43
|
end
|
81
44
|
|
82
45
|
val = opt[:tcp_nopush]
|
83
46
|
unless val.nil?
|
84
|
-
if
|
85
|
-
sock.setsockopt(IPPROTO_TCP, TCP_CORK, val)
|
86
|
-
elsif
|
87
|
-
sock.setsockopt(IPPROTO_TCP, TCP_NOPUSH, val)
|
47
|
+
if Socket.const_defined?(:TCP_CORK) # Linux
|
48
|
+
sock.setsockopt(:IPPROTO_TCP, :TCP_CORK, val)
|
49
|
+
elsif Socket.const_defined?(:TCP_NOPUSH) # FreeBSD
|
50
|
+
sock.setsockopt(:IPPROTO_TCP, :TCP_NOPUSH, val)
|
88
51
|
end
|
89
52
|
end
|
90
53
|
|
91
|
-
# No good reason to ever have deferred accepts off
|
92
|
-
# (except maybe benchmarking)
|
93
|
-
if
|
54
|
+
# No good reason to ever have deferred accepts off in single-threaded
|
55
|
+
# servers (except maybe benchmarking)
|
56
|
+
if Socket.const_defined?(:TCP_DEFER_ACCEPT)
|
94
57
|
# this differs from nginx, since nginx doesn't allow us to
|
95
58
|
# configure the the timeout...
|
96
59
|
seconds = opt[:tcp_defer_accept]
|
97
60
|
seconds = DEFAULTS[:tcp_defer_accept] if [true,nil].include?(seconds)
|
98
61
|
seconds = 0 unless seconds # nil/false means disable this
|
99
|
-
sock.setsockopt(
|
62
|
+
sock.setsockopt(:IPPROTO_TCP, :TCP_DEFER_ACCEPT, seconds)
|
100
63
|
elsif respond_to?(:accf_arg)
|
101
64
|
name = opt[:accept_filter]
|
102
|
-
name = DEFAULTS[:accept_filter] if nil
|
65
|
+
name = DEFAULTS[:accept_filter] if name.nil?
|
103
66
|
begin
|
104
|
-
sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, accf_arg(name))
|
67
|
+
sock.setsockopt(:SOL_SOCKET, :SO_ACCEPTFILTER, accf_arg(name))
|
105
68
|
rescue => e
|
106
69
|
logger.error("#{sock_name(sock)} " \
|
107
70
|
"failed to set accept_filter=#{name} (#{e.inspect})")
|
@@ -114,10 +77,11 @@ module Unicorn
|
|
114
77
|
|
115
78
|
TCPSocket === sock and set_tcp_sockopt(sock, opt)
|
116
79
|
|
117
|
-
|
80
|
+
rcvbuf, sndbuf = opt.values_at(:rcvbuf, :sndbuf)
|
81
|
+
if rcvbuf || sndbuf
|
118
82
|
log_buffer_sizes(sock, "before: ")
|
119
|
-
sock.setsockopt(SOL_SOCKET, SO_RCVBUF,
|
120
|
-
sock.setsockopt(SOL_SOCKET, SO_SNDBUF,
|
83
|
+
sock.setsockopt(:SOL_SOCKET, :SO_RCVBUF, rcvbuf) if rcvbuf
|
84
|
+
sock.setsockopt(:SOL_SOCKET, :SO_SNDBUF, sndbuf) if sndbuf
|
121
85
|
log_buffer_sizes(sock, " after: ")
|
122
86
|
end
|
123
87
|
sock.listen(opt[:backlog])
|
@@ -126,8 +90,8 @@ module Unicorn
|
|
126
90
|
end
|
127
91
|
|
128
92
|
def log_buffer_sizes(sock, pfx = '')
|
129
|
-
rcvbuf = sock.getsockopt(SOL_SOCKET, SO_RCVBUF).
|
130
|
-
sndbuf = sock.getsockopt(SOL_SOCKET, SO_SNDBUF).
|
93
|
+
rcvbuf = sock.getsockopt(:SOL_SOCKET, :SO_RCVBUF).int
|
94
|
+
sndbuf = sock.getsockopt(:SOL_SOCKET, :SO_SNDBUF).int
|
131
95
|
logger.info "#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}"
|
132
96
|
end
|
133
97
|
|
@@ -172,25 +136,25 @@ module Unicorn
|
|
172
136
|
|
173
137
|
def new_tcp_server(addr, port, opt)
|
174
138
|
# n.b. we set FD_CLOEXEC in the workers
|
175
|
-
sock = Socket.new(opt[:ipv6] ? AF_INET6 : AF_INET, SOCK_STREAM
|
139
|
+
sock = Socket.new(opt[:ipv6] ? :AF_INET6 : :AF_INET, :SOCK_STREAM)
|
176
140
|
if opt.key?(:ipv6only)
|
177
|
-
|
141
|
+
Socket.const_defined?(:IPV6_V6ONLY) or
|
178
142
|
abort "Socket::IPV6_V6ONLY not defined, upgrade Ruby and/or your OS"
|
179
|
-
sock.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, opt[:ipv6only] ? 1 : 0)
|
143
|
+
sock.setsockopt(:IPPROTO_IPV6, :IPV6_V6ONLY, opt[:ipv6only] ? 1 : 0)
|
180
144
|
end
|
181
|
-
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
|
182
|
-
if
|
183
|
-
sock.setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
|
145
|
+
sock.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1)
|
146
|
+
if Socket.const_defined?(:SO_REUSEPORT) && opt[:reuseport]
|
147
|
+
sock.setsockopt(:SOL_SOCKET, :SO_REUSEPORT, 1)
|
184
148
|
end
|
185
149
|
sock.bind(Socket.pack_sockaddr_in(port, addr))
|
186
|
-
|
150
|
+
sock.autoclose = false
|
187
151
|
Kgio::TCPServer.for_fd(sock.fileno)
|
188
152
|
end
|
189
153
|
|
190
154
|
# returns rfc2732-style (e.g. "[::1]:666") addresses for IPv6
|
191
155
|
def tcp_name(sock)
|
192
156
|
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
193
|
-
|
157
|
+
addr.include?(':') ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
|
194
158
|
end
|
195
159
|
module_function :tcp_name
|
196
160
|
|
data/lib/unicorn/stream_input.rb
CHANGED
@@ -53,7 +53,7 @@ class Unicorn::StreamInput
|
|
53
53
|
rv << @rbuf
|
54
54
|
to_read -= @rbuf.size
|
55
55
|
end
|
56
|
-
@rbuf.
|
56
|
+
@rbuf.clear
|
57
57
|
end
|
58
58
|
rv = nil if rv.empty? && length != 0
|
59
59
|
else
|
@@ -130,8 +130,8 @@ private
|
|
130
130
|
filter_body(@rbuf, @buf)
|
131
131
|
dst << @rbuf
|
132
132
|
end
|
133
|
-
|
134
|
-
|
133
|
+
ensure
|
134
|
+
@rbuf.clear
|
135
135
|
end
|
136
136
|
|
137
137
|
def eof!
|
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
@@ -3,15 +3,14 @@ require "raindrops"
|
|
3
3
|
|
4
4
|
# This class and its members can be considered a stable interface
|
5
5
|
# and will not change in a backwards-incompatible fashion between
|
6
|
-
# releases of
|
7
|
-
# not needed for most users of
|
6
|
+
# releases of unicorn. Knowledge of this class is generally not
|
7
|
+
# not needed for most users of unicorn.
|
8
8
|
#
|
9
9
|
# Some users may want to access it in the before_fork/after_fork hooks.
|
10
10
|
# See the Unicorn::Configurator RDoc for examples.
|
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/exec/test_exec.rb
CHANGED
@@ -96,6 +96,58 @@ run lambda { |env|
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
+
def test_sd_listen_fds_emulation
|
100
|
+
File.open("config.ru", "wb") { |fp| fp.write(HI) }
|
101
|
+
sock = TCPServer.new(@addr, @port)
|
102
|
+
|
103
|
+
[ %W(-l #@addr:#@port), nil ].each do |l|
|
104
|
+
sock.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 0)
|
105
|
+
|
106
|
+
pid = xfork do
|
107
|
+
redirect_test_io do
|
108
|
+
# pretend to be systemd
|
109
|
+
ENV['LISTEN_PID'] = "#$$"
|
110
|
+
ENV['LISTEN_FDS'] = '1'
|
111
|
+
|
112
|
+
# 3 = SD_LISTEN_FDS_START
|
113
|
+
args = [ $unicorn_bin ]
|
114
|
+
args.concat(l) if l
|
115
|
+
args << { 3 => sock }
|
116
|
+
exec(*args)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
res = hit(["http://#@addr:#@port/"])
|
120
|
+
assert_equal [ "HI\n" ], res
|
121
|
+
assert_shutdown(pid)
|
122
|
+
assert_equal 1, sock.getsockopt(:SOL_SOCKET, :SO_KEEPALIVE).int,
|
123
|
+
'unicorn should always set SO_KEEPALIVE on inherited sockets'
|
124
|
+
end
|
125
|
+
ensure
|
126
|
+
sock.close if sock
|
127
|
+
# disabled test on old Rubies: https://bugs.ruby-lang.org/issues/11336
|
128
|
+
# [ruby-core:69895] [Bug #11336] fixed by r51576
|
129
|
+
end if RUBY_VERSION.to_f >= 2.3
|
130
|
+
|
131
|
+
def test_inherit_listener_unspecified
|
132
|
+
File.open("config.ru", "wb") { |fp| fp.write(HI) }
|
133
|
+
sock = TCPServer.new(@addr, @port)
|
134
|
+
sock.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 0)
|
135
|
+
|
136
|
+
pid = xfork do
|
137
|
+
redirect_test_io do
|
138
|
+
ENV['UNICORN_FD'] = sock.fileno.to_s
|
139
|
+
exec($unicorn_bin, sock.fileno => sock.fileno)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
res = hit(["http://#@addr:#@port/"])
|
143
|
+
assert_equal [ "HI\n" ], res
|
144
|
+
assert_shutdown(pid)
|
145
|
+
assert_equal 1, sock.getsockopt(:SOL_SOCKET, :SO_KEEPALIVE).int,
|
146
|
+
'unicorn should always set SO_KEEPALIVE on inherited sockets'
|
147
|
+
ensure
|
148
|
+
sock.close if sock
|
149
|
+
end
|
150
|
+
|
99
151
|
def test_working_directory_rel_path_config_file
|
100
152
|
other = Tempfile.new('unicorn.wd')
|
101
153
|
File.unlink(other.path)
|
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
|