From 36934f75dea2fbb5ba7ab62159f1d3bed3e8f35a Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 3 Mar 2017 04:09:34 +0000 Subject: repobrowse: avoid excessive buffering in raw endpoint Relying on qspawn allows us to serve arbitrarily large files without excessive buffering. We'll special-case small files in the future to avoid qspawn, as those small files should fit comfortably in socket buffers. --- lib/PublicInbox/RepoGitRaw.pm | 128 ++++++++++++++++++++++-------------------- 1 file changed, 66 insertions(+), 62 deletions(-) diff --git a/lib/PublicInbox/RepoGitRaw.pm b/lib/PublicInbox/RepoGitRaw.pm index b990c7e7..fabc237b 100644 --- a/lib/PublicInbox/RepoGitRaw.pm +++ b/lib/PublicInbox/RepoGitRaw.pm @@ -6,35 +6,47 @@ use warnings; use base qw(PublicInbox::RepoBase); use PublicInbox::Hval qw(utf8_html); use PublicInbox::Qspawn; +my $MAX_ASYNC = 65536; sub call_git_raw { my ($self, $req) = @_; my $repo = $req->{-repo}; my $git = $repo->{git}; my $tip = $req->{tip} || $repo->tip; - my $id = $tip . ':' . $req->{expath}; - my ($cat, $hex, $type, $size) = $git->cat_file_begin($id); - return unless defined $cat; - - my ($r, $buf); - my $left = $size; - if ($type eq 'blob') { - $type = git_blob_mime_type($self, $req, $cat, \$buf, \$left); - } elsif ($type eq 'commit' || $type eq 'tag') { - $type = 'text/plain; charset=UTF-8'; - } elsif ($type eq 'tree') { - $git->cat_file_finish($left); - return git_tree_raw($self, $req, $git, $hex); - } else { - $type = 'application/octet-stream'; + my $expath = $req->{expath}; + my $obj = $tip; + $obj .= ":$expath" if $expath ne ''; + my $env = $req->{env}; + sub { + my ($res) = @_; + $git->check_async($env, $obj, sub { + my ($info) = @_; + my ($hex, $type, $size) = @$info; + if (!defined $type || $type eq 'missing') { + return $res->($self->rt(404, 'plain', + 'Not Found')); + } + my $ct; + if ($type eq 'blob') { + my $base = $req->{extra}->[-1]; + $ct = $self->mime_type($base) if defined $base; + $ct ||= 'text/plain; charset=UTF-8' if !$size; + } elsif ($type eq 'commit' || $type eq 'tag') { + $ct = 'text/plain; charset=UTF-8'; + } elsif ($type eq 'tree') { + return git_tree_raw($self, $req, $res, $hex); + } else { + $ct = 'application/octet-stream'; + } + show_raw($self, $req, $res, $ct, $hex, $type, $size); + }); } - git_blob_stream_response($git, $cat, $size, $type, $buf, $left); } sub git_tree_sed ($) { my ($req) = @_; my $buf = ''; - my $end; + my $end = ''; my $pfx = $req->{tpfx}; sub { # $_[0] = buffer or undef my $dst = delete $req->{tstart} || ''; @@ -53,19 +65,18 @@ sub git_tree_sed ($) { $dst .= $n->as_html; $dst .= ''; } - $end ? $dst .= $end : $dst; + $dst .= $end; } } -# This should follow the cgit DOM structure in case anybody depends on it, -# not using
 here as we don't expect people to actually view it much
 sub git_tree_raw {
-	my ($self, $req, $git, $hex) = @_;
+	my ($self, $req, $res, $hex) = @_;
 
 	my @ex = @{$req->{extra}};
 	my $rel = $req->{relcmd};
 	my $title = utf8_html(join('/', '', @ex, ''));
-	my $pfx = 'raw/';
+	my $repo = $req->{-repo};
+	my $pfx = ($req->{tip} || $repo->tip) . '/';
 	my $t = "

$title