unicorn.git  about / heads / tags
Rack HTTP server for Unix and fast clients
blob bedce0173ed039d3aef75a5eaa5c813a160b62f0 3048 bytes (raw)
$ git show v0.5.2:test/unit/test_signals.rb	# shows this blob on the CLI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
 
# Copyright (c) 2009 Eric Wong
# You can redistribute it and/or modify it under the same terms as Ruby.
#
# Ensure we stay sane in the face of signals being sent to us

require 'test/test_helper'

include Unicorn

class Dd
  def initialize(bs, count)
    @count = count
    @buf = ' ' * bs
  end

  def each(&block)
    @count.times { yield @buf }
  end
end

class SignalsTest < Test::Unit::TestCase

  def setup
    @bs = 1 * 1024 * 1024
    @count = 100
    @port = unused_port
    tmp = @tmp = Tempfile.new('unicorn.sock')
    File.unlink(@tmp.path)
    n = 0
    tmp.chmod(0)
    @server_opts = {
      :listeners => [ "127.0.0.1:#@port", @tmp.path ],
      :after_fork => lambda { |server,worker|
        trap(:HUP) { tmp.chmod(n += 1) }
      },
    }
    @server = nil
  end

  def test_response_write
    app = lambda { |env|
      [ 200, { 'Content-Type' => 'text/plain', 'X-Pid' => Process.pid.to_s },
        Dd.new(@bs, @count) ]
    }
    redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
    sock = nil
    assert_nothing_raised do
      sock = TCPSocket.new('127.0.0.1', @port)
      sock.syswrite("GET / HTTP/1.0\r\n\r\n")
    end
    buf = ''
    header_len = pid = nil
    assert_nothing_raised do
      buf = sock.sysread(16384, buf)
      pid = buf[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
      header_len = buf[/\A(.+?\r\n\r\n)/m, 1].size
    end
    read = buf.size
    mode_before = @tmp.stat.mode
    assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
                  Errno::EBADF) do
      loop do
        3.times { Process.kill(:HUP, pid) }
        sock.sysread(16384, buf)
        read += buf.size
        3.times { Process.kill(:HUP, pid) }
      end
    end

    redirect_test_io { @server.stop(true) }
    # can't check for == since pending signals get merged
    assert mode_before < @tmp.stat.mode
    assert_equal(read - header_len, @bs * @count)
    assert_nothing_raised { sock.close }
  end

  def test_request_read
    app = lambda { |env|
      [ 200, {'Content-Type'=>'text/plain', 'X-Pid'=>Process.pid.to_s}, [] ]
    }
    redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
    pid = nil

    assert_nothing_raised do
      sock = TCPSocket.new('127.0.0.1', @port)
      sock.syswrite("GET / HTTP/1.0\r\n\r\n")
      pid = sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
      sock.close
    end

    sock = TCPSocket.new('127.0.0.1', @port)
    sock.syswrite("PUT / HTTP/1.0\r\n")
    sock.syswrite("Content-Length: #{@bs * @count}\r\n\r\n")
    1000.times { Process.kill(:HUP, pid) }
    mode_before = @tmp.stat.mode
    killer = fork { loop { Process.kill(:HUP, pid); sleep(0.0001) } }
    buf = ' ' * @bs
    @count.times { sock.syswrite(buf) }
    Process.kill(:TERM, killer)
    Process.waitpid2(killer)
    redirect_test_io { @server.stop(true) }
    # can't check for == since pending signals get merged
    assert mode_before < @tmp.stat.mode
    assert_equal pid, sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
    sock.close
  end

end

git clone https://yhbt.net/unicorn.git