From 3b2fc62dadd3c90038c168849b33c4ca6df058da Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 8 Dec 2010 22:02:45 +0000 Subject: tee_input: fix accounting error on corked requests In case a request sends the header and buffer as one packet, TeeInput relying on accounting info from StreamInput is harmful as StreamInput will buffer in memory outside of TeeInput's control. This bug is triggered by calling env["rack.input"].size or env["rack.input"].rewind before to read. --- t/rack-input-tests.ru | 21 ++++++++ t/t0100-rack-input-tests.sh | 124 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 t/rack-input-tests.ru create mode 100755 t/t0100-rack-input-tests.sh (limited to 't') diff --git a/t/rack-input-tests.ru b/t/rack-input-tests.ru new file mode 100644 index 0000000..8c35630 --- /dev/null +++ b/t/rack-input-tests.ru @@ -0,0 +1,21 @@ +# SHA1 checksum generator +require 'digest/sha1' +use Rack::ContentLength +cap = 16384 +app = lambda do |env| + /\A100-continue\z/i =~ env['HTTP_EXPECT'] and + return [ 100, {}, [] ] + digest = Digest::SHA1.new + input = env['rack.input'] + input.size if env["PATH_INFO"] == "/size_first" + input.rewind if env["PATH_INFO"] == "/rewind_first" + if buf = input.read(rand(cap)) + begin + raise "#{buf.size} > #{cap}" if buf.size > cap + digest.update(buf) + end while input.read(rand(cap), buf) + end + + [ 200, {'Content-Type' => 'text/plain'}, [ digest.hexdigest << "\n" ] ] +end +run app diff --git a/t/t0100-rack-input-tests.sh b/t/t0100-rack-input-tests.sh new file mode 100755 index 0000000..1cd9279 --- /dev/null +++ b/t/t0100-rack-input-tests.sh @@ -0,0 +1,124 @@ +#!/bin/sh +. ./test-lib.sh +test -r random_blob || die "random_blob required, run with 'make $0'" + +t_plan 10 "rack.input read tests" + +t_begin "setup and startup" && { + rtmpfiles curl_out curl_err + unicorn_setup + unicorn -D rack-input-tests.ru -c $unicorn_config + blob_sha1=$(rsha1 < random_blob) + blob_size=$(wc -c < random_blob) + t_info "blob_sha1=$blob_sha1" + unicorn_wait_start +} + +t_begin "corked identity request" && { + rm -f $tmp + ( + cat $fifo > $tmp & + printf 'PUT / HTTP/1.0\r\n' + printf 'Content-Length: %d\r\n\r\n' $blob_size + cat random_blob + wait + echo ok > $ok + ) | ( sleep 1 && socat - TCP4:$listen > $fifo ) + test 1 -eq $(grep $blob_sha1 $tmp |wc -l) + test x"$(cat $ok)" = xok +} + +t_begin "corked chunked request" && { + rm -f $tmp + ( + cat $fifo > $tmp & + content-md5-put < random_blob + wait + echo ok > $ok + ) | ( sleep 1 && socat - TCP4:$listen > $fifo ) + test 1 -eq $(grep $blob_sha1 $tmp |wc -l) + test x"$(cat $ok)" = xok +} + +t_begin "corked identity request (input#size first)" && { + rm -f $tmp + ( + cat $fifo > $tmp & + printf 'PUT /size_first HTTP/1.0\r\n' + printf 'Content-Length: %d\r\n\r\n' $blob_size + cat random_blob + wait + echo ok > $ok + ) | ( sleep 1 && socat - TCP4:$listen > $fifo ) + test 1 -eq $(grep $blob_sha1 $tmp |wc -l) + test x"$(cat $ok)" = xok +} + +t_begin "corked identity request (input#rewind first)" && { + rm -f $tmp + ( + cat $fifo > $tmp & + printf 'PUT /rewind_first HTTP/1.0\r\n' + printf 'Content-Length: %d\r\n\r\n' $blob_size + cat random_blob + wait + echo ok > $ok + ) | ( sleep 1 && socat - TCP4:$listen > $fifo ) + test 1 -eq $(grep $blob_sha1 $tmp |wc -l) + test x"$(cat $ok)" = xok +} + +t_begin "corked chunked request (input#size first)" && { + rm -f $tmp + ( + cat $fifo > $tmp & + printf 'PUT /size_first HTTP/1.1\r\n' + printf 'Host: example.com\r\n' + printf 'Transfer-Encoding: chunked\r\n' + printf 'Trailer: Content-MD5\r\n' + printf '\r\n' + content-md5-put --no-headers < random_blob + wait + echo ok > $ok + ) | ( sleep 1 && socat - TCP4:$listen > $fifo ) + test 1 -eq $(grep $blob_sha1 $tmp |wc -l) + test 1 -eq $(grep $blob_sha1 $tmp |wc -l) + test x"$(cat $ok)" = xok +} + +t_begin "corked chunked request (input#rewind first)" && { + rm -f $tmp + ( + cat $fifo > $tmp & + printf 'PUT /rewind_first HTTP/1.1\r\n' + printf 'Host: example.com\r\n' + printf 'Transfer-Encoding: chunked\r\n' + printf 'Trailer: Content-MD5\r\n' + printf '\r\n' + content-md5-put --no-headers < random_blob + wait + echo ok > $ok + ) | ( sleep 1 && socat - TCP4:$listen > $fifo ) + test 1 -eq $(grep $blob_sha1 $tmp |wc -l) + test x"$(cat $ok)" = xok +} + +t_begin "regular request" && { + curl -sSf -T random_blob http://$listen/ > $curl_out 2> $curl_err + test x$blob_sha1 = x$(cat $curl_out) + test ! -s $curl_err +} + +t_begin "chunked request" && { + curl -sSf -T- < random_blob http://$listen/ > $curl_out 2> $curl_err + test x$blob_sha1 = x$(cat $curl_out) + test ! -s $curl_err +} + +dbgcat r_err + +t_begin "shutdown" && { + kill $unicorn_pid +} + +t_done -- cgit v1.2.3-24-ge0c7