From b44af5d8f2fb106bee9b4720bcf92aed7851bfa8 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 25 Feb 2016 04:02:36 +0000 Subject: git-http-backend: start refactoring to use callback Designing for asynchronous, non-blocking operations makes adapting for synchronous, blocking operation easy. Going the other way around is not easy, so do it now and allow us to be more easily adapted for non-blocking use in the next commit... --- lib/PublicInbox/GitHTTPBackend.pm | 73 +++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/lib/PublicInbox/GitHTTPBackend.pm b/lib/PublicInbox/GitHTTPBackend.pm index cba025e7..3cf78579 100644 --- a/lib/PublicInbox/GitHTTPBackend.pm +++ b/lib/PublicInbox/GitHTTPBackend.pm @@ -178,34 +178,55 @@ sub serve_smart { my @cmd = qw(git http-backend); exec(@cmd) or die 'exec `' . join(' ', @cmd). "' failed: $!\n"; } - $wpipe = undef; - $in = undef; - my @h; - my $code = 200; - { - local $/ = "\r\n"; - while (defined(my $line = <$rpipe>)) { - if ($line =~ /\AStatus:\s*(\d+)/) { - $code = $1; - } else { - chomp $line; - last if $line eq ''; - push @h, split(/:\s*/, $line, 2); - } + $wpipe = $in = undef; + $rpipe->blocking(0); + $buf = ''; + my $vin; + vec($vin, fileno($rpipe), 1) = 1; + my ($fh, $res); + my $fail = sub { + my ($e) = @_; + if ($e eq 'EAGAIN') { + select($vin, undef, undef, undef); + } else { + $rpipe = undef; + $fh->close if $fh; + $err->print('git http-backend error: ', $e, "\n"); } - } - return if $code == 403; - sub { - my ($cb) = @_; - my $fh = $cb->([ $code, \@h ]); - while (1) { - my $r = sysread($rpipe, $buf, 8192); - die "$!\n" unless defined $r; - last if ($r == 0); - $fh->write($buf); + }; + my $cb = sub { + my $r = sysread($rpipe, $buf, 8192, length($buf)); + return $fail->($!{EAGAIN} ? 'EAGAIN' : $!) unless defined $r; + if ($r == 0) { # EOF + $rpipe = undef; + $fh->close if $fh; + return; } - $fh->close; - } + if ($fh) { # stream body from git-http-backend to HTTP client + $fh->write($buf); + $buf = ''; + } elsif ($buf =~ s/\A(.*?)\r?\n\r?\n//s) { # parse headers + my $h = $1; + my $code = 200; + my @h; + foreach my $l (split(/\r?\n/, $h)) { + my ($k, $v) = split(/:\s*/, $l, 2); + if ($k =~ /\AStatus\z/i) { + $code = int($v); + } else { + push @h, $k, $v; + } + } + # write response header: + $fh = $res->([ $code, \@h ]); + $fh->write($buf); + $buf = ''; + } # else { keep reading ... } + }; + sub { + ($res) = @_; + while ($rpipe) { $cb->() } + }; } 1; -- cgit v1.2.3-24-ge0c7