]> Sergey Matveev's repositories - public-inbox.git/commitdiff
http: improve error handling for aborted responses
authorEric Wong <e@80x24.org>
Fri, 29 Apr 2016 03:32:20 +0000 (03:32 +0000)
committerEric Wong <e@80x24.org>
Fri, 29 Apr 2016 20:03:17 +0000 (20:03 +0000)
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).

lib/PublicInbox/GitHTTPBackend.pm
lib/PublicInbox/HTTP.pm

index 30efa839ac3abb2cd0b8692478c29c8839df13dd..4b3969346acefda27f49ffd47c5f0b26db49a1c6 100644 (file)
@@ -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
index c7fb954ebd6c0f0b472dce57bf1a07601a01866c..973da3418c76282e3d38989c0e04f1130c4349f1 100644 (file)
@@ -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 '');