From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id E269720382 for ; Tue, 12 Mar 2019 04:00:46 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 09/13] cgit: support running cgit as a standalone CGI Date: Tue, 12 Mar 2019 04:00:42 +0000 Message-Id: <20190312040046.4619-10-e@80x24.org> In-Reply-To: <20190312040046.4619-1-e@80x24.org> References: <20190312040046.4619-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: 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 e6316ef..150e337 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 0000000..ca93f92 --- /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 0000000..3d1a0d5 --- /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; -- EW