From 9c8372c3a4972cacf0eb15ad85d34cb33293672e Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 19 Jul 2010 10:10:08 +0000 Subject: ensure client aborted file/stream response bodies are closed We need to remember to close response bodies even if a client aborts the connection, since body.close can trigger interesting things like logging and such... --- lib/rainbows/event_machine.rb | 11 ++++++----- t/close-pipe-response.ru | 5 ++--- t/t0021-sendfile-wrap-to_path.sh | 22 ++++++++++++++++++++-- t/t0031-close-pipe-response.sh | 35 +++++++++++++++++++++++++++++++---- 4 files changed, 59 insertions(+), 14 deletions(-) mode change 100644 => 100755 t/t0021-sendfile-wrap-to_path.sh mode change 100644 => 100755 t/t0031-close-pipe-response.sh diff --git a/lib/rainbows/event_machine.rb b/lib/rainbows/event_machine.rb index 3094284..8de02ea 100644 --- a/lib/rainbows/event_machine.rb +++ b/lib/rainbows/event_machine.rb @@ -119,14 +119,15 @@ module Rainbows st = io.stat if st.file? - write(response_header(status, headers)) if headers - io.close - @body = stream = stream_file_data(body.to_path) - stream.callback do + cb = lambda do body.close if body.respond_to?(:close) quit unless alive end - return + write(response_header(status, headers)) if headers + io.close + @body = stream = stream_file_data(body.to_path) + stream.errback(&cb) + return stream.callback(&cb) elsif st.socket? || st.pipe? chunk = stream_response_headers(status, headers) if headers m = chunk ? ResponseChunkPipe : ResponsePipe diff --git a/t/close-pipe-response.ru b/t/close-pipe-response.ru index 96116d4..4e34766 100644 --- a/t/close-pipe-response.ru +++ b/t/close-pipe-response.ru @@ -15,11 +15,10 @@ class CloseWrapper < Struct.new(:to_io) end use Rainbows::DevFdResponse run(lambda { |env| - body = 'hello world' - io = IO.popen("echo '#{body}'", 'rb') + io = IO.popen('cat random_blob', 'rb') [ 200, { - 'Content-Length' => (body.size + 1).to_s, + 'Content-Length' => ::File.stat('random_blob').size.to_s, 'Content-Type' => 'application/octet-stream', }, CloseWrapper[io] ] diff --git a/t/t0021-sendfile-wrap-to_path.sh b/t/t0021-sendfile-wrap-to_path.sh old mode 100644 new mode 100755 index 4ae5929..6baa890 --- a/t/t0021-sendfile-wrap-to_path.sh +++ b/t/t0021-sendfile-wrap-to_path.sh @@ -9,10 +9,10 @@ ruby) ;; ;; esac -t_plan 7 "sendfile wrap body response for $model" +t_plan 10 "sendfile wrap body response for $model" t_begin "setup and startup" && { - rtmpfiles out err + rtmpfiles out err http_fifo sub_ok rainbows_setup $model echo 'require "sendfile"' >> $unicorn_config echo 'def (::IO).copy_stream(*x); abort "NO"; end' >> $unicorn_config @@ -42,6 +42,24 @@ t_begin "body.close called" && { grep CLOSING $out || die "body.close not logged" } +t_begin "start FIFO reader for abortive request" && { + cat $fifo > $out & +} + +t_begin "send abortive request" && { + ( + printf 'GET /random_blob\r\n' + dd bs=4096 count=1 < $http_fifo >/dev/null + echo ok > $ok + ) | socat - TCP:$listen > $http_fifo || : + test xok = x$(cat $ok) +} + +t_begin "body.close called for aborted request" && { + wait # for cat $fifo + grep CLOSING $out || die "body.close not logged" +} + t_begin "shutdown server" && { kill -QUIT $rainbows_pid } diff --git a/t/t0031-close-pipe-response.sh b/t/t0031-close-pipe-response.sh old mode 100644 new mode 100755 index 7439b5f..58b6346 --- a/t/t0031-close-pipe-response.sh +++ b/t/t0031-close-pipe-response.sh @@ -1,19 +1,28 @@ #!/bin/sh . ./test-lib.sh -t_plan 5 "close pipe response for $model" +t_plan 10 "close pipe response for $model" t_begin "setup and startup" && { - rtmpfiles err out + rtmpfiles err out http_fifo sub_ok rainbows_setup $model export fifo rainbows -E none -D close-pipe-response.ru -c $unicorn_config rainbows_wait_start } -t_begin "single request matches" && { +t_begin "read random blob sha1" && { + random_blob_sha1=$(rsha1 < random_blob) +} + +t_begin "start FIFO reader" && { cat $fifo > $out & - test x'hello world' = x"$(curl -sSfv 2> $err http://$listen/)" +} + +t_begin "single request matches" && { + sha1=$(curl -sSfv 2> $err http://$listen/ | rsha1) + test -n "$sha1" + test x"$sha1" = x"$random_blob_sha1" } t_begin "body.close called" && { @@ -21,6 +30,24 @@ t_begin "body.close called" && { grep CLOSING $out || die "body.close not logged" } +t_begin "start FIFO reader for abortive request" && { + cat $fifo > $out & +} + +t_begin "send abortive request" && { + ( + printf 'GET /random_blob\r\n' + dd bs=4096 count=1 < $http_fifo >/dev/null + echo ok > $ok + ) | socat - TCP:$listen > $http_fifo || : + test xok = x$(cat $ok) +} + +t_begin "body.close called for aborted request" && { + wait # for cat $fifo + grep CLOSING $out || die "body.close not logged" +} + t_begin "shutdown server" && { kill -QUIT $rainbows_pid } -- cgit v1.2.3-24-ge0c7