From 7f9cf4cbf5920591a955035555c2969d7d29af66 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 10 Mar 2019 22:02:29 +0000 Subject: [PATCH] cgit: support running cgit as a standalone CGI We depend on git-http-backend for smart HTTP clone support, however; since cgit does not support smart clones natively. WWW.pm will be able to cascade down to this as a 404 handler in the future. --- MANIFEST | 2 + examples/cgit.psgi | 29 ++++++++++++++ lib/PublicInbox/Cgit.pm | 87 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 examples/cgit.psgi create mode 100644 lib/PublicInbox/Cgit.pm diff --git a/MANIFEST b/MANIFEST index e6316ef6..150e337f 100644 --- a/MANIFEST +++ b/MANIFEST @@ -38,6 +38,7 @@ examples/apache2_perl.conf examples/apache2_perl_old.conf examples/cgi-webrick.rb examples/cgit-commit-filter.lua +examples/cgit.psgi examples/highlight.psgi examples/logrotate.conf examples/newswww.psgi @@ -58,6 +59,7 @@ examples/varnish-4.vcl lib/PublicInbox/Address.pm lib/PublicInbox/Admin.pm lib/PublicInbox/AltId.pm +lib/PublicInbox/Cgit.pm lib/PublicInbox/Config.pm lib/PublicInbox/ContentId.pm lib/PublicInbox/Daemon.pm diff --git a/examples/cgit.psgi b/examples/cgit.psgi new file mode 100644 index 00000000..ca93f924 --- /dev/null +++ b/examples/cgit.psgi @@ -0,0 +1,29 @@ +#!/usr/bin/perl -w +# Copyright (C) 2019 all contributors +# License: GPL-3.0+ +# +# PublicInbox::Cgit may be used independently of WWW. +# +# Usage: +# plackup -I lib -o 127.0.0.1 -R lib -r examples/cgit.psgi +use strict; +use warnings; +use Plack::Builder; +use PublicInbox::Cgit; +use PublicInbox::Config; +my $pi_config = PublicInbox::Config->new; +my $cgit = PublicInbox::Cgit->new($pi_config); + +builder { + eval { + enable 'Deflater', + content_type => [ qw( + text/html + text/plain + application/atom+xml + )] + }; + eval { enable 'ReverseProxy' }; + enable 'Head'; + sub { $cgit->call($_[0]) } +} diff --git a/lib/PublicInbox/Cgit.pm b/lib/PublicInbox/Cgit.pm new file mode 100644 index 00000000..3d1a0d54 --- /dev/null +++ b/lib/PublicInbox/Cgit.pm @@ -0,0 +1,87 @@ +# Copyright (C) 2019 all contributors +# License: AGPL-3.0+ + +# wrapper for cgit(1) and git-http-backend(1) for browsing and +# serving git code repositories. Requires 'publicinbox.cgitrc' +# directive to be set in the public-inbox config file. + +package PublicInbox::Cgit; +use strict; +use PublicInbox::GitHTTPBackend; +# not bothering with Exporter for a one-off +*r = *PublicInbox::GitHTTPBackend::r; +*input_prepare = *PublicInbox::GitHTTPBackend::input_prepare; +*parse_cgi_headers = *PublicInbox::GitHTTPBackend::parse_cgi_headers; +*serve = *PublicInbox::GitHTTPBackend::serve; +use warnings; +use PublicInbox::Qspawn; + +sub new { + my ($class, $pi_config) = @_; + my $cgit_bin = $pi_config->{'publicinbox.cgitbin'} || + # Debian default location: + '/usr/lib/cgit/cgit.cgi'; + + my $self = bless { + cmd => [ $cgit_bin ], + pi_config => $pi_config, + }, $class; + + $pi_config->each_inbox(sub {}); # fill in -code_repos mapped to inboxes + + # some cgit repos may not be mapped to inboxes, so ensure those exist: + my $code_repos = $pi_config->{-code_repos}; + foreach my $k (keys %$pi_config) { + $k =~ /\Acoderepo\.(.+)\.dir\z/ or next; + my $dir = $pi_config->{$k}; + $code_repos->{$1} ||= PublicInbox::Git->new($dir); + } + while (my ($nick, $repo) = each %$code_repos) { + $self->{"\0$nick"} = $repo; + } + $self; +} + +# only what cgit cares about: +my @PASS_ENV = qw( + HTTP_HOST + QUERY_STRING + REQUEST_METHOD + SCRIPT_NAME + SERVER_NAME + SERVER_PORT + HTTP_COOKIE + HTTP_REFERER + CONTENT_LENGTH +); +# XXX: cgit filters may care about more variables... + +sub call { + my ($self, $env) = @_; + my $path_info = $env->{PATH_INFO}; + + # handle requests without spawning cgit iff possible: + if ($path_info =~ m!\A/(.+?)/($PublicInbox::GitHTTPBackend::ANY)\z!ox) { + my ($nick, $path) = ($1, $2); + if (my $git = $self->{"\0$nick"}) { + return serve($env, $git, $path); + } + } + + my $cgi_env = { PATH_INFO => $path_info }; + foreach (@PASS_ENV) { + defined(my $v = $env->{$_}) or next; + $cgi_env->{$_} = $v; + } + $cgi_env->{'HTTPS'} = 'on' if $env->{'psgi.url_scheme'} eq 'https'; + + my $rdr = input_prepare($env) or return r(500); + my $qsp = PublicInbox::Qspawn->new($self->{cmd}, $cgi_env, $rdr); + $qsp->psgi_return($env, undef, sub { + my ($r, $bref) = @_; + my $res = parse_cgi_headers($r, $bref) or return; # incomplete + $res; + }); +} + +1; -- 2.44.0