From a7949988f7f8690c868d2150fe3000fcf6a6d5f4 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 25 Dec 2019 07:50:47 +0000 Subject: qspawn: psgi_return: allow non-anon parse_hdr callback Callers can supply an arg to parse_hdr, now, eliminating the need for closures to capture local variables. --- lib/PublicInbox/Cgit.pm | 12 ++++++---- lib/PublicInbox/GitHTTPBackend.pm | 12 ++++++---- lib/PublicInbox/Qspawn.pm | 7 +++--- lib/PublicInbox/ViewVCS.pm | 48 +++++++++++++++++++++------------------ 4 files changed, 44 insertions(+), 35 deletions(-) diff --git a/lib/PublicInbox/Cgit.pm b/lib/PublicInbox/Cgit.pm index 353f4162..094f146e 100644 --- a/lib/PublicInbox/Cgit.pm +++ b/lib/PublicInbox/Cgit.pm @@ -95,6 +95,12 @@ my @PASS_ENV = qw( ); # XXX: cgit filters may care about more variables... +sub cgit_parse_hdr { # {parse_hdr} for Qspawn + my ($r, $bref) = @_; + my $res = parse_cgi_headers($r, $bref) or return; # incomplete + $res; +} + sub call { my ($self, $env) = @_; my $path_info = $env->{PATH_INFO}; @@ -123,11 +129,7 @@ sub call { my $rdr = input_prepare($env) or return r(500); my $qsp = PublicInbox::Qspawn->new($self->{cmd}, $cgi_env, $rdr); my $limiter = $self->{pi_config}->limiter('-cgit'); - $qsp->psgi_return($env, $limiter, sub { - my ($r, $bref) = @_; - my $res = parse_cgi_headers($r, $bref) or return; # incomplete - $res; - }); + $qsp->psgi_return($env, $limiter, \&cgit_parse_hdr); } 1; diff --git a/lib/PublicInbox/GitHTTPBackend.pm b/lib/PublicInbox/GitHTTPBackend.pm index ec8e6516..537a1947 100644 --- a/lib/PublicInbox/GitHTTPBackend.pm +++ b/lib/PublicInbox/GitHTTPBackend.pm @@ -184,6 +184,12 @@ sub prepare_range { ($code, $len); } +sub git_parse_hdr { # {parse_hdr} for Qspawn + my ($r, $bref, $dumb_args) = @_; + my $res = parse_cgi_headers($r, $bref) or return; # incomplete + $res->[0] == 403 ? serve_dumb(@$dumb_args) : $res; +} + # returns undef if 403 so it falls back to dumb HTTP sub serve_smart { my ($env, $git, $path) = @_; @@ -204,11 +210,7 @@ sub serve_smart { $env{PATH_TRANSLATED} = "$git->{git_dir}/$path"; my $rdr = input_prepare($env) or return r(500); my $qsp = PublicInbox::Qspawn->new([qw(git http-backend)], \%env, $rdr); - $qsp->psgi_return($env, $limiter, sub { # parse_hdr - my ($r, $bref) = @_; - my $res = parse_cgi_headers($r, $bref) or return; # incomplete - $res->[0] == 403 ? serve_dumb($env, $git, $path) : $res; - }); + $qsp->psgi_return($env, $limiter, \&git_parse_hdr, [$env, $git, $path]); } sub input_prepare { diff --git a/lib/PublicInbox/Qspawn.pm b/lib/PublicInbox/Qspawn.pm index d1a34bea..1a2b70e7 100644 --- a/lib/PublicInbox/Qspawn.pm +++ b/lib/PublicInbox/Qspawn.pm @@ -215,12 +215,13 @@ sub rd_hdr ($) { my $ret; my $total_rd = 0; my $hdr_buf = $self->{hdr_buf}; + my ($ph_cb, $ph_arg) = @{$self->{parse_hdr}}; do { my $r = sysread($self->{rpipe}, $$hdr_buf, 4096, length($$hdr_buf)); if (defined($r)) { $total_rd += $r; - $ret = $self->{parse_hdr}->($total_rd, $hdr_buf); + $ret = $ph_cb->($total_rd, $hdr_buf, $ph_arg); } else { # caller should notify us when it's ready: return if $! == EAGAIN; @@ -298,10 +299,10 @@ sub psgi_return_start { # may run later, much later... # psgix.io. 3-element arrays means the body is available # immediately (or streamed via ->getline (pull-based)). sub psgi_return { - my ($self, $env, $limiter, $parse_hdr) = @_; + my ($self, $env, $limiter, $parse_hdr, $hdr_arg) = @_; $self->{psgi_env} = $env; $self->{hdr_buf} = \(my $hdr_buf = ''); - $self->{parse_hdr} = $parse_hdr; + $self->{parse_hdr} = [ $parse_hdr, $hdr_arg ]; $limiter ||= $def_limiter ||= PublicInbox::Qspawn::Limiter->new(32); # the caller already captured the PSGI write callback from diff --git a/lib/PublicInbox/ViewVCS.pm b/lib/PublicInbox/ViewVCS.pm index 886e10cb..7618b198 100644 --- a/lib/PublicInbox/ViewVCS.pm +++ b/lib/PublicInbox/ViewVCS.pm @@ -42,35 +42,39 @@ sub html_page ($$$) { $wcb ? $wcb->($res) : $res; } +sub stream_blob_parse_hdr { # {parse_hdr} for Qspawn + my ($r, $bref, $ctx) = @_; + my ($res, $logref) = delete @$ctx{qw(-res -logref)}; + my ($git, $oid, $type, $size, $di) = @$res; + my @cl = ('Content-Length', $size); + if (!defined $r) { # error + html_page($ctx, 500, $logref); + } elsif (index($$bref, "\0") >= 0) { + [200, [qw(Content-Type application/octet-stream), @cl] ]; + } else { + my $n = bytes::length($$bref); + if ($n >= $BIN_DETECT || $n == $size) { + return [200, [ 'Content-Type', + 'text/plain; charset=UTF-8', @cl ] ]; + } + if ($r == 0) { + warn "premature EOF on $oid $$logref\n"; + return html_page($ctx, 500, $logref); + } + undef; # bref keeps growing + } +} + sub stream_large_blob ($$$$) { my ($ctx, $res, $logref, $fn) = @_; + $ctx->{-logref} = $logref; + $ctx->{-res} = $res; my ($git, $oid, $type, $size, $di) = @$res; my $cmd = ['git', "--git-dir=$git->{git_dir}", 'cat-file', $type, $oid]; my $qsp = PublicInbox::Qspawn->new($cmd); - my @cl = ('Content-Length', $size); my $env = $ctx->{env}; - $env->{'public-inbox.tmpgit'} = $git; # for {-tmp}/File::Temp::Dir $env->{'qspawn.wcb'} = delete $ctx->{-wcb}; - $qsp->psgi_return($env, undef, sub { - my ($r, $bref) = @_; - if (!defined $r) { # error - html_page($ctx, 500, $logref); - } elsif (index($$bref, "\0") >= 0) { - my $ct = 'application/octet-stream'; - [200, ['Content-Type', $ct, @cl ] ]; - } else { - my $n = bytes::length($$bref); - if ($n >= $BIN_DETECT || $n == $size) { - my $ct = 'text/plain; charset=UTF-8'; - return [200, ['Content-Type', $ct, @cl] ]; - } - if ($r == 0) { - warn "premature EOF on $oid $$logref\n"; - return html_page($ctx, 500, $logref); - } - undef; # bref keeps growing - } - }); + $qsp->psgi_return($env, undef, \&stream_blob_parse_hdr, $ctx); } sub show_other_result ($$) { -- cgit v1.2.3-24-ge0c7