unicorn 4.9.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/Application_Timeouts +3 -3
  3. data/DESIGN +2 -4
  4. data/Documentation/unicorn.1.txt +8 -5
  5. data/Documentation/unicorn_rails.1.txt +2 -2
  6. data/FAQ +17 -8
  7. data/GIT-VERSION-GEN +1 -1
  8. data/GNUmakefile +6 -1
  9. data/ISSUES +20 -28
  10. data/KNOWN_ISSUES +9 -9
  11. data/Links +14 -17
  12. data/PHILOSOPHY +0 -6
  13. data/README +22 -17
  14. data/SIGNALS +1 -1
  15. data/Sandbox +4 -4
  16. data/TUNING +11 -8
  17. data/bin/unicorn +1 -1
  18. data/bin/unicorn_rails +1 -1
  19. data/examples/nginx.conf +10 -11
  20. data/examples/unicorn.conf.rb +1 -4
  21. data/ext/unicorn_http/extconf.rb +1 -0
  22. data/ext/unicorn_http/httpdate.c +1 -1
  23. data/ext/unicorn_http/unicorn_http.rl +89 -156
  24. data/lib/unicorn.rb +10 -18
  25. data/lib/unicorn/configurator.rb +17 -31
  26. data/lib/unicorn/const.rb +2 -25
  27. data/lib/unicorn/http_request.rb +22 -33
  28. data/lib/unicorn/http_response.rb +14 -32
  29. data/lib/unicorn/http_server.rb +129 -122
  30. data/lib/unicorn/socket_helper.rb +36 -72
  31. data/lib/unicorn/stream_input.rb +3 -3
  32. data/lib/unicorn/tmpio.rb +0 -5
  33. data/lib/unicorn/util.rb +2 -1
  34. data/lib/unicorn/worker.rb +3 -15
  35. data/t/hijack.ru +2 -1
  36. data/t/t0200-rack-hijack.sh +5 -2
  37. data/test/exec/test_exec.rb +52 -0
  38. data/test/test_helper.rb +3 -2
  39. data/test/unit/test_http_parser_ng.rb +16 -114
  40. data/test/unit/test_response.rb +19 -16
  41. data/test/unit/test_socket_helper.rb +1 -1
  42. data/unicorn.gemspec +10 -1
  43. metadata +10 -23
  44. data/examples/git.ru +0 -13
  45. data/lib/unicorn/app/exec_cgi.rb +0 -154
  46. data/lib/unicorn/app/inetd.rb +0 -109
  47. data/lib/unicorn/ssl_client.rb +0 -11
  48. data/lib/unicorn/ssl_configurator.rb +0 -104
  49. data/lib/unicorn/ssl_server.rb +0 -42
  50. data/t/t0016-trust-x-forwarded-false.sh +0 -30
  51. data/t/t0017-trust-x-forwarded-true.sh +0 -30
  52. data/test/unit/test_http_parser_xftrust.rb +0 -38
  53. 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
- # prevents IO objects in here from being GC-ed
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 Unicorn users behind nginx (a
19
- # value of 1 remains an optimization), but Rainbows! users may
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 get HTTPS support
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
- case RUBY_PLATFORM
39
- when /linux/
40
- # from /usr/include/linux/tcp.h
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
- sock.setsockopt(SOL_SOCKET, SO_KEEPALIVE, 1) if defined?(SO_KEEPALIVE)
36
+ Socket.const_defined?(:SO_KEEPALIVE) and
37
+ sock.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 1)
75
38
 
76
- if defined?(TCP_NODELAY)
39
+ if Socket.const_defined?(:TCP_NODELAY)
77
40
  val = opt[:tcp_nodelay]
78
- val = DEFAULTS[:tcp_nodelay] if nil == val
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 defined?(TCP_CORK) # Linux
85
- sock.setsockopt(IPPROTO_TCP, TCP_CORK, val)
86
- elsif defined?(TCP_NOPUSH) # TCP_NOPUSH is lightly tested (FreeBSD)
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 defined?(TCP_DEFER_ACCEPT)
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(SOL_TCP, TCP_DEFER_ACCEPT, seconds)
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 == name
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
- if opt[:rcvbuf] || opt[:sndbuf]
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, opt[:rcvbuf]) if opt[:rcvbuf]
120
- sock.setsockopt(SOL_SOCKET, SO_SNDBUF, opt[:sndbuf]) if opt[: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).unpack('i')
130
- sndbuf = sock.getsockopt(SOL_SOCKET, SO_SNDBUF).unpack('i')
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, 0)
139
+ sock = Socket.new(opt[:ipv6] ? :AF_INET6 : :AF_INET, :SOCK_STREAM)
176
140
  if opt.key?(:ipv6only)
177
- defined?(IPV6_V6ONLY) or
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 defined?(SO_REUSEPORT) && opt[:reuseport]
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
- prevent_autoclose(sock)
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
- /:/ =~ addr ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
157
+ addr.include?(':') ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
194
158
  end
195
159
  module_function :tcp_name
196
160
 
@@ -53,7 +53,7 @@ class Unicorn::StreamInput
53
53
  rv << @rbuf
54
54
  to_read -= @rbuf.size
55
55
  end
56
- @rbuf.replace('')
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
- ensure
134
- @rbuf.replace('')
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
@@ -1,6 +1,7 @@
1
1
  # -*- encoding: binary -*-
2
2
 
3
- module Unicorn::Util
3
+ require 'fcntl'
4
+ module Unicorn::Util # :nodoc:
4
5
 
5
6
  # :stopdoc:
6
7
  def self.is_log?(fp)
@@ -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 \Unicorn. Knowledge of this class is generally not
7
- # not needed for most users of \Unicorn.
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
- @tmp = @switched = false
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
- abort "body.close called after response hijack\n"
11
+ warn "closed DieIfUsed #{@@n += 1}\n"
11
12
  end
12
13
  end
13
14
  run lambda { |env|
@@ -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
@@ -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
- sigs = %w(CHLD).concat(Unicorn::HttpServer::QUEUE_SIGS)
296
- sigs.each { |sig| trap(sig, "DEFAULT") }
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 test_keepalive_requests_default_constant
33
- assert_kind_of Integer, HttpParser::KEEPALIVE_REQUESTS_DEFAULT
34
- assert HttpParser::KEEPALIVE_REQUESTS_DEFAULT >= 0
35
- end
36
-
37
- def test_keepalive_requests_setting
38
- HttpParser.keepalive_requests = 0
39
- assert_equal 0, HttpParser.keepalive_requests
40
- HttpParser.keepalive_requests = nil
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
- HttpParser::KEEPALIVE_REQUESTS_DEFAULT.times do |nr|
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