1 # Copyright (c) 2009 Eric Wong
2 require 'test/test_helper'
6 class UploadTest < Test::Unit::TestCase
9 @addr = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
11 @hdr = {'Content-Type' => 'text/plain', 'Content-Length' => '0'}
16 # we want random binary data to test 1.9 encoding-aware IO craziness
17 @random = File.open('/dev/urandom','rb')
18 @sha1 = Digest::SHA1.new
19 @sha1_app = lambda do |env|
20 input = env['rack.input']
21 resp = { :pos => input.pos, :size => input.stat.size }
23 loop { @sha1.update(input.sysread(@bs)) }
26 resp[:sha1] = @sha1.hexdigest
27 [ 200, @hdr.merge({'X-Resp' => resp.inspect}), [] ]
32 redirect_test_io { @server.stop(true) } if @server
37 start_server(@sha1_app)
38 sock = TCPSocket.new(@addr, @port)
39 sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n")
41 buf = @random.sysread(@bs)
45 read = sock.read.split(/\r\n/)
46 assert_equal "HTTP/1.1 200 OK", read[0]
47 resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
48 assert_equal length, resp[:size]
49 assert_equal 0, resp[:pos]
50 assert_equal @sha1.hexdigest, resp[:sha1]
54 def test_put_keepalive_truncates_small_overwrite
55 start_server(@sha1_app)
56 sock = TCPSocket.new(@addr, @port)
57 to_upload = length + 1
58 sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{to_upload}\r\n\r\n")
60 buf = @random.sysread(@bs)
64 sock.syswrite('12345') # write 4 bytes more than we expected
67 read = sock.read.split(/\r\n/)
68 assert_equal "HTTP/1.1 200 OK", read[0]
69 resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
70 assert_equal to_upload, resp[:size]
71 assert_equal 0, resp[:pos]
72 assert_equal @sha1.hexdigest, resp[:sha1]
75 def test_put_excessive_overwrite_closed
76 start_server(lambda { |env| [ 200, @hdr, [] ] })
77 sock = TCPSocket.new(@addr, @port)
79 sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n")
80 @count.times { sock.syswrite(buf) }
81 assert_raise Errno::ECONNRESET do
82 ::Unicorn::Const::CHUNK_SIZE.times { sock.syswrite(buf) }
86 def test_put_handler_closed_file
88 start_server(lambda { |env|
89 env['rack.input'].close
90 resp = { :nr => nr.succ! }
91 [ 200, @hdr.merge({ 'X-Resp' => resp.inspect}), [] ]
93 sock = TCPSocket.new(@addr, @port)
95 sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n")
96 @count.times { sock.syswrite(buf) }
97 read = sock.read.split(/\r\n/)
98 assert_equal "HTTP/1.1 200 OK", read[0]
99 resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
100 assert_equal '1', resp[:nr]
102 # server still alive?
103 sock = TCPSocket.new(@addr, @port)
104 sock.syswrite("GET / HTTP/1.0\r\n\r\n")
105 read = sock.read.split(/\r\n/)
106 assert_equal "HTTP/1.1 200 OK", read[0]
107 resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
108 assert_equal '2', resp[:nr]
111 def test_renamed_file_not_closed
112 start_server(lambda { |env|
113 new_tmp = Tempfile.new('unicorn_test')
114 input = env['rack.input']
115 File.rename(input.path, new_tmp.path)
117 :inode => input.stat.ino,
118 :size => input.stat.size,
119 :new_tmp => new_tmp.path,
120 :old_tmp => input.path,
122 [ 200, @hdr.merge({ 'X-Resp' => resp.inspect}), [] ]
124 sock = TCPSocket.new(@addr, @port)
126 sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n")
127 @count.times { sock.syswrite(buf) }
128 read = sock.read.split(/\r\n/)
129 assert_equal "HTTP/1.1 200 OK", read[0]
130 resp = eval(read.grep(/^X-Resp: /).first.sub!(/X-Resp: /, ''))
131 new_tmp = File.open(resp[:new_tmp])
132 assert_equal resp[:inode], new_tmp.stat.ino
133 assert_equal length, resp[:size]
134 assert ! File.exist?(resp[:old_tmp])
135 assert_equal resp[:size], new_tmp.stat.size
144 def start_server(app)
146 @server = HttpServer.new(app, :listeners => [ "#{@addr}:#{@port}" ] )