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.
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