diff options
author | Eric Wong <e@80x24.org> | 2017-01-21 02:29:52 +0000 |
---|---|---|
committer | Eric Wong <e@80x24.org> | 2017-01-21 02:32:41 +0000 |
commit | a2bfef2dd80cdbd434f01fe5f7182a9a36c1b905 (patch) | |
tree | e9ecfe5ef1a4c055acb147625e47789e96c15e1e /lib | |
parent | b7df48261532049b640ad55548363381423ebabd (diff) | |
download | public-inbox-a2bfef2dd80cdbd434f01fe5f7182a9a36c1b905.tar.gz |
repobrowse: git Atom feed uses Qspawn->psgi_return
This allows us to wait on "git log" output in a non-blocking manner while being able to throttle on backpressure from slow clients when used with pi-httpd.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/PublicInbox/RepobrowseGitAtom.pm | 215 |
1 files changed, 127 insertions, 88 deletions
diff --git a/lib/PublicInbox/RepobrowseGitAtom.pm b/lib/PublicInbox/RepobrowseGitAtom.pm index 3031417b..99d79869 100644 --- a/lib/PublicInbox/RepobrowseGitAtom.pm +++ b/lib/PublicInbox/RepobrowseGitAtom.pm @@ -8,52 +8,14 @@ use warnings; use PublicInbox::Hval qw(utf8_html); use base qw(PublicInbox::RepobrowseBase); use PublicInbox::Qspawn; -my $ATOM_FMT = '--pretty=tformat:'. - join('%x00', qw(%s %ct %an %ae %at %h %H %b), '', ''); use constant DATEFMT => '%Y-%m-%dT%H:%M:%SZ'; +use constant STATES => qw(H ct an ae at s b); +use constant STATE_BODY => (scalar(STATES) - 1); +my $ATOM_FMT = '--pretty=tformat:'. + join('%n', map { "%$_" } STATES).'%x00'; use POSIX qw(strftime); -sub call_git_atom { - my ($self, $req) = @_; - my $repo_info = $req->{repo_info}; - my $max = $repo_info->{max_commit_count} || 10; - $max = int($max); - $max = 50 if $max == 0; - - my $git = $repo_info->{git}; - my $env = $req->{env}; - my $q = PublicInbox::RepobrowseGitQuery->new($env); - my $h = $q->{h}; - my $res; - - my $read_log = sub { - my @cmd = (qw(log --no-notes --no-color --abbrev-commit), - $git->abbrev, $ATOM_FMT, "-$max", $h, '--'); - my $expath = $req->{expath}; - push @cmd, $expath if $expath ne ''; - my $log = $git->popen(@cmd); - my @h = ( 'Content-Type' => 'application/atom+xml' ); - my $fh = $res->([200, \@h]); - $self->git_atom_stream($req, $q, $log, $fh, $h); - $fh->close; - }; - - sub { - $res = $_[0]; - return $read_log->() if $h ne ''; - - my $cmd = [ 'git', "--git-dir=$git->{git_dir}", - qw(symbolic-ref --short HEAD) ]; - my $rdr = { 2 => $git->err_begin }; - my $qsp = PublicInbox::Qspawn->new($cmd, undef, undef, $rdr); - $qsp->psgi_qx($env, undef, sub { - chomp($h = ${$_[0]}); - $read_log->(); - }) - } -} - sub repo_root_url { my ($self, $req) = @_; my $env = $req->{env}; @@ -70,63 +32,140 @@ sub repo_root_url { PublicInbox::Repobrowse::base_url($env) . join('/', @uri); } -sub git_atom_stream { - my ($self, $req, $q, $log, $fh, $h) = @_; +sub flush_hdr ($$) { + my ($dst, $hdr) = @_; + $$dst .= '<entry><title>'; + $$dst .= utf8_html($hdr->{'s'}); # commit subject + $$dst .= '</title><updated>'; + $$dst .= strftime(DATEFMT, gmtime($hdr->{ct})); + $$dst .= '</updated><author><name>'; + $$dst .= utf8_html($hdr->{an}); + $$dst .= '</name><email>'; + $$dst .= utf8_html($hdr->{ae}); + $$dst .= '</email></author><published>'; + $$dst .= strftime(DATEFMT, gmtime($hdr->{at})); + $$dst .= '</published>'; + $$dst .= qq(<link\nrel="alternate"\ntype="text/html"\nhref="); + $$dst .= $hdr->{url}; + $$dst .= '/commit?id='; + my $H = $hdr->{H}; + $$dst .= $H; + $$dst .= qq("\n/><id>); + $$dst .= $H; + $$dst .= qq(</id>); + + $$dst .= qq(<content\ntype="xhtml"><div\nxmlns="); + $$dst .= qq(http://www.w3.org/1999/xhtml">); + $$dst .= qq(<pre\nstyle="white-space:pre-wrap">); + undef +} + +sub git_atom_sed_end ($$) { + my ($req, $buf) = @_; +} + +sub git_atom_sed ($$) { + my ($self, $req) = @_; + my $buf = ''; + my $state = 0; + my $rel = $req->{relcmd}; my $repo_info = $req->{repo_info}; my $title = join('/', $repo_info->{repo}, @{$req->{extra}}); - $title = utf8_html("$title, branch $h"); - - my $url = $self->repo_root_url($req); - $fh->write(qq(<?xml version="1.0"?>\n) . + $title = utf8_html("$title, branch $req->{q}->{h}"); + my $url = repo_root_url($self, $req); + my $hdr = $req->{hdr} = { url => $url }; + $req->{axml} = qq(<?xml version="1.0"?>\n) . qq(<feed\nxmlns="http://www.w3.org/2005/Atom">) . qq(<title>$title</title>) . qq(<subtitle>$repo_info->{desc_html}</subtitle>) . - qq(<link\nrel="alternate"\ntype="text/html"\nhref="$url"\n/>)); - my $rel = $req->{relcmd}; - my %acache; - local $/ = "\0"; - while (defined(my $s = <$log>)) { - chomp $s; - my $entry = '<entry><title>'; - $entry .= utf8_html($s); # commit subject - $entry .= '</title><updated>'; - - chomp($s = <$log>); # commit time - $entry .= strftime(DATEFMT, gmtime($s)); - $entry .= '</updated><author><name>'; + qq(<link\nrel="alternate"\ntype="text/html"\nhref="$url"\n/>); + my ($plinks, $id, $ai); + my $end = ''; + sub { + my $dst; + # $_[0] == scalar buffer, undef means EOF from "git log" + $dst = delete $req->{axml} || ''; + my @tmp; + if (defined $_[0]) { + $buf .= $_[0]; + @tmp = split(/\n/, $buf, -1); + $buf = @tmp ? pop(@tmp) : ''; + } else { + @tmp = split(/\n/, $buf, -1); + $buf = ''; + $end = '</feed>'; + } + + foreach my $l (@tmp) { + if ($state != STATE_BODY) { + $hdr->{((STATES)[$state])} = $l; + if (++$state == STATE_BODY) { + flush_hdr(\$dst, $hdr); + %$hdr = (url => $url); + } + next; + } + if ($l eq "\0") { + $dst .= qq(</pre></div></content></entry>); + $state = 0; + } else { + $dst .= utf8_html($l); + } + } + $dst .= $end; + } +} - chomp($s = <$log>); # author name - $entry .= $acache{$s} ||= utf8_html($s); - $entry .= '</name><email>'; +sub git_atom_cb { + my ($self, $req) = @_; + sub { + my ($r) = @_; + my $env = $req->{env}; + if (!defined $r) { + my $git = $req->{repo_info}->{git}; + return [ 400, [ 'Content-Type', 'text/plain' ], + [ $git->err ] ]; + } + $env->{'qspawn.filter'} = git_atom_sed($self, $req); + [ 200, [ 'Content-Type', 'application/atom+xml' ] ]; + } +} - chomp($s = <$log>); # author email - $entry .= $acache{$s} ||= utf8_html($s); - $entry .= '</email></author><published>'; +sub call_git_atom { + my ($self, $req) = @_; + my $repo_info = $req->{repo_info}; + my $max = $repo_info->{max_commit_count} || 10; + $max = int($max); + $max = 50 if $max == 0; - chomp($s = <$log>); # author time - $entry .= strftime(DATEFMT, gmtime($s)); - $entry .= '</published>'; + my $git = $repo_info->{git}; + my $env = $req->{env}; + my $q =$req->{'q'} = PublicInbox::RepobrowseGitQuery->new($env); + my $h = $q->{h}; + my $git_dir = "--git-dir=$git->{git_dir}"; + my $read_log = sub { + my $cmd = ['git', $git_dir, + qw(log --no-notes --no-color --abbrev-commit), + $git->abbrev, $ATOM_FMT, "-$max", $h, '--' ]; + my $expath = $req->{expath}; + push @$cmd, $expath if $expath ne ''; + my $rdr = { 2 => $git->err_begin }; + my $qsp = PublicInbox::Qspawn->new($cmd, undef, undef, $rdr); + $qsp->psgi_return($env, undef, git_atom_cb($self, $req)); + }; - $entry .= qq(<link\nrel="alternate"\ntype="text/html"\nhref="); - $entry .= $url; - chomp($s = <$log>); # abbreviated commit hash for URL - $entry .= qq(/commit?id=$s"\n/><id>); - chomp($s = <$log>); # unabbreviated commit hash - $entry .= $s; - $entry .= qq(</id>); + sub { + $env->{'qspawn.response'} = $_[0]; + return $read_log->() if $h ne ''; - $entry .= qq(<content\ntype="xhtml"><div\nxmlns="); - $entry .= qq(http://www.w3.org/1999/xhtml">); - $entry .= qq(<pre\nstyle="white-space:pre-wrap">\n); - chomp($s = <$log>); - $entry .= utf8_html($s); # body - $fh->write($entry .= qq(</pre></div></content></entry>)); - eval { - local $/ = "\0\n"; - $s = <$log>; - }; + my $cmd = [ 'git', $git_dir, qw(symbolic-ref --short HEAD) ]; + my $rdr = { 2 => $git->err_begin }; + my $qsp = PublicInbox::Qspawn->new($cmd, undef, undef, $rdr); + $qsp->psgi_qx($env, undef, sub { + chomp($h = ${$_[0]}); + $read_log->(); + }) } - $fh->write('</feed>'); } 1; |