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
         |