From 3495d59763e6159975debf32728dc53fc41c5ea1 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 27 Dec 2010 20:25:39 -0800 Subject: several response body#close fixes Some middlewares require the Rack env to be preserved all the way through to close, so we'll ensure all request models preserve it. We also need to better response body wrappers/proxies always get fired properly when returning. IO.copy_stream and "sendfile" gem users could hit cases where wrappers did not fire properly. --- t/close-has-env.ru | 65 ++++++++++++++++++++ t/t0050-response-body-close-has-env.sh | 109 +++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 t/close-has-env.ru create mode 100644 t/t0050-response-body-close-has-env.sh (limited to 't') diff --git a/t/close-has-env.ru b/t/close-has-env.ru new file mode 100644 index 0000000..471f605 --- /dev/null +++ b/t/close-has-env.ru @@ -0,0 +1,65 @@ +#\ -E none +use Rainbows::DevFdResponse +class ClosablePipe < ::IO + attr_accessor :env + + def self.new(env) + rv = popen "echo hello", "rb" + rv.env = env + rv + end + + def close + super + $stdout.syswrite "path_info=#{@env['PATH_INFO']}\n" + end +end + +class ClosableFile < ::File + attr_accessor :env + alias to_path path + def close + super + $stdout.syswrite "path_info=#{@env['PATH_INFO']}\n" + end +end + +class Blob + def initialize(env) + @env = env + end + + def each(&block) + yield "BLOB\n" + end + + def close + $stdout.syswrite "path_info=#{@env['PATH_INFO']}\n" + end +end + +run(lambda { |env| + case env["PATH_INFO"] + when %r{\A/pipe/} + [ 200, + [ %w(Content-Length 6), %w(Content-Type text/plain)], + ClosablePipe.new(env) + ] + when %r{\A/file/} + f = ClosableFile.open("env.ru", "rb") + f.env = env + [ 200, { + 'X-Req-Path' => env["PATH_INFO"], + 'Content-Length' => f.stat.size.to_s, + 'Content-Type' => 'text/plain' }, + f + ] + when %r{\A/blob/} + [ 200, + [%w(Content-Length 5), %w(Content-Type text/plain)], + Blob.new(env) + ] + else + [ 404, [%w(Content-Length 0), %w(Content-Type text/plain)], [] ] + end +}) diff --git a/t/t0050-response-body-close-has-env.sh b/t/t0050-response-body-close-has-env.sh new file mode 100644 index 0000000..4d0cd6f --- /dev/null +++ b/t/t0050-response-body-close-has-env.sh @@ -0,0 +1,109 @@ +#!/bin/sh +. ./test-lib.sh + +t_plan 29 "keepalive does not clear Rack env prematurely for $model" + +t_begin "setup and start" && { + rainbows_setup + rtmpfiles curl_out curl_err + echo "preload_app true" >> $unicorn_config + rainbows -D close-has-env.ru -c $unicorn_config + rainbows_wait_start +} + +req_pipelined () { + pfx=$1 + t_begin "make pipelined requests to trigger $pfx response body" && { + > $r_out + ( + cat $fifo > $tmp & + printf 'GET /%s/1 HTTP/1.1\r\n' $pfx + printf 'Host: example.com\r\n\r\n' + printf 'GET /%s/2 HTTP/1.1\r\n' $pfx + printf 'Host: example.com\r\n\r\n' + printf 'GET /%s/3 HTTP/1.1\r\n' $pfx + printf 'Host: example.com\r\n' + printf 'Connection: close\r\n\r\n' + wait + echo ok > $ok + ) | socat - TCP4:$listen > $fifo + test xok = x$(cat $ok) + } +} + +reload () { + t_begin 'reloading Rainbows! to ensure writeout' && { + # reload to ensure everything is flushed + kill -HUP $rainbows_pid + test xSTART = x"$(cat $fifo)" + } +} + +check_log () { + pfx="$1" + t_begin "check body close messages" && { + < $r_out awk ' +/^path_info=\/'$pfx'\/[1-3]$/ { next } +{ exit(2) } +END { exit(NR == 3 ? 0 : 1) } +' + } +} + +req_keepalive () { + pfx="$1" + t_begin "make keepalive requests to trigger $pfx response body" && { + > $r_out + rm -f $curl_err $curl_out + curl -vsSf http://$listen/$pfx/[1-3] 2> $curl_err > $curl_out + } +} + +req_keepalive file +reload +check_log file + +req_pipelined file +reload +check_log file + +req_keepalive blob +reload +check_log blob + +req_pipelined blob +reload +check_log blob + +req_keepalive pipe +reload +check_log pipe + +req_pipelined pipe +reload +check_log pipe + +t_begin "enable sendfile gem" && { + echo "require 'sendfile'" >> $unicorn_config + curl http://$listen/ >/dev/null # ensure worker is loaded before HUP +} + +reload + +req_keepalive file +reload +check_log file + +req_pipelined file +reload +check_log file + +t_begin "killing succeeds" && { + kill $rainbows_pid +} + +t_begin "check stderr" && { + check_stderr +} + +t_done -- cgit v1.2.3-24-ge0c7