about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2016-04-29 03:32:20 +0000
committerEric Wong <e@80x24.org>2016-04-29 20:03:17 +0000
commitf947a355417328a96b1bbe43223cbae79fc10345 (patch)
tree416770c545d78c6bbdbd10e0ec17db3a60a3f833
parent0b0d9b7f6237593c69dc38ec64566343fd20191c (diff)
downloadpublic-inbox-f947a355417328a96b1bbe43223cbae79fc10345.tar.gz
We need to abort connections properly if a response is prematurely
truncated.  This includes problems with serving static files, since
a clumsy admin or broken FS could return truncated responses and
inadvertently leave a client waiting (since the client saw
"Content-Length" in the header and expected a certain length).
-rw-r--r--lib/PublicInbox/GitHTTPBackend.pm19
-rw-r--r--lib/PublicInbox/HTTP.pm9
2 files changed, 23 insertions, 5 deletions
diff --git a/lib/PublicInbox/GitHTTPBackend.pm b/lib/PublicInbox/GitHTTPBackend.pm
index 30efa839..4b396934 100644
--- a/lib/PublicInbox/GitHTTPBackend.pm
+++ b/lib/PublicInbox/GitHTTPBackend.pm
@@ -95,6 +95,7 @@ sub serve_dumb {
                         $len -= $r;
                         $fh->write($buf);
                 }
+                die "$f truncated with $len bytes remaining\n" if $len;
                 $fh->close;
         }
 }
@@ -191,14 +192,21 @@ sub serve_smart {
                         $fh = undef;
                 }
                 if ($rpipe) {
-                        $rpipe->close; # _may_ be Danga::Socket::close
+                        # _may_ be Danga::Socket::close via
+                        # PublicInbox::HTTPD::Async::close:
+                        $rpipe->close;
                         $rpipe = undef;
                         $nr_running--;
                 }
-                if (defined $pid && $pid != waitpid($pid, 0)) {
-                        $err->print("git http-backend ($git_dir): $?\n");
-                } else {
-                        $pid = undef;
+                if (defined $pid) {
+                        my $e = $pid == waitpid($pid, 0) ?
+                                $? : "PID:$pid still running?";
+                        if ($e) {
+                                $err->print("http-backend ($git_dir): $e\n");
+                                if (my $io = $env->{'psgix.io'}) {
+                                        $io->close;
+                                }
+                        }
                 }
                 return unless $res;
                 my $dumb = serve_dumb($cgi, $git, $path);
@@ -245,6 +253,7 @@ sub serve_smart {
                 } # else { keep reading ... }
         };
         if (my $async = $env->{'pi-httpd.async'}) {
+                # $async is PublicInbox::HTTPD::Async->new($rpipe, $cb)
                 $rpipe = $async->($rpipe, $cb);
                 sub { ($res) = @_ } # let Danga::Socket handle the rest.
         } else { # synchronous loop for other PSGI servers
diff --git a/lib/PublicInbox/HTTP.pm b/lib/PublicInbox/HTTP.pm
index c7fb954e..973da341 100644
--- a/lib/PublicInbox/HTTP.pm
+++ b/lib/PublicInbox/HTTP.pm
@@ -135,6 +135,9 @@ sub app_dispatch ($) {
         sysseek($env->{'psgi.input'}, 0, SEEK_SET) or
                         die "BUG: psgi.input seek failed: $!";
 
+        # note: NOT $self->{sock}, we want our close (+ Danga::Socket::close),
+        # to do proper cleanup:
+        $env->{'psgix.io'} = $self; # only for ->close
         my $res = Plack::Util::run_app($self->{httpd}->{app}, $env);
         eval {
                 if (ref($res) eq 'CODE') {
@@ -371,6 +374,12 @@ sub quit {
 sub event_hup { $_[0]->close }
 sub event_err { $_[0]->close }
 
+sub close {
+        my $self = shift;
+        $self->{env} = undef;
+        $self->SUPER::close(@_);
+}
+
 sub write ($$) : method {
         my PublicInbox::HTTP $self = $_[0];
         return 1 if (defined($_[1]) && ref($_[1]) eq '' && $_[1] eq '');