This commit makes it possible to run gitweb as a PSGI application (see http://plackperl.org), using any of PSGI web servers. This includes ability to run gitweb from command line via Plack::Runner module; just run "./gitweb.cgi --psgi" and point web browser to http://0:5000 Gitweb uses CGI mode by default; to trigger PSGI mode one must either pass `--psgi` or `--plackup` option to gitweb script, or the script must be run with *.psgi filename (either copy/rename script, or make symlink ending with *.psgi e.g. gitweb.psgi). Besides running gitweb itself, converting its run_request() from CGI to PSGI via CGI::Emulate::PSGI, gitweb as PSGI app also serves static files: scripts, stylesheets and images via Plack::Middleware::Static. Currently it assumes that positions and URLs of those assets are left at their default values, namely that URLs end with "/static/<file>", and that files can be found in "static/" directory relative to the gitweb script itself. This assumption should be relaxed in future. Currently "git instaweb --httpd=plackup" doesn't (yet!) use this newly introduced feature: it is left for future commit. Signed-off-by: Jakub Narebski <jnareb@xxxxxxxxx> --- The major part of this series. "./gitweb.cgi --psgi" runs web server on http://0:5000/ Documentation/gitweb.txt | 16 +++++++++ gitweb/INSTALL | 3 ++ gitweb/README | 13 ++++++-- gitweb/gitweb.perl | 80 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 107 insertions(+), 5 deletions(-) diff --git a/Documentation/gitweb.txt b/Documentation/gitweb.txt index 605a085..157903b 100644 --- a/Documentation/gitweb.txt +++ b/Documentation/gitweb.txt @@ -473,6 +473,22 @@ With that configuration the full path to browse repositories would be: http://server/gitweb +As PSGI using plackup +~~~~~~~~~~~~~~~~~~~~~ +Gitweb can run as PSGI app (via emulation with *CGI::Emulate::PSGI*(3pm)). +First you need to rename, copy or symlink gitweb.cgi to gitweb.psgi. +You can run gitweb as a PSGI application from 'plackup' command line +utility, using any *PSGI*(3) web server (see http://plackperl.org for +a list), for example: + + $ plackup --server HTTP::Server::Simple --port 8080 \ + --host 127.0.0.1 gitweb.psgi + +With that configuration the full path to browse repositories would be: + + http://127.0.0.1:8080/ + +See *plackup*(1) manpage for more details. ADVANCED WEB SERVER SETUP ------------------------- diff --git a/gitweb/INSTALL b/gitweb/INSTALL index 6d45406..9f55ab4 100644 --- a/gitweb/INSTALL +++ b/gitweb/INSTALL @@ -36,6 +36,9 @@ Requirements The following optional Perl modules are required for extra features - Digest::MD5 - for gravatar support - CGI::Fast and FCGI - for running gitweb as FastCGI script + - CGI::Emulate::PSGI, Plack::Builder, Plack::Middleware::Static + for running gitweb as PSGI application, and Plack::Runner to + make it runnable as a standalone script - HTML::TagCloud - for fancy tag cloud in project list view - HTTP::Date or Time::ParseDate - to support If-Modified-Since for feeds diff --git a/gitweb/README b/gitweb/README index 6da4778..a435f9b 100644 --- a/gitweb/README +++ b/gitweb/README @@ -56,9 +56,16 @@ See also gitweb.conf(5) manpage. Web server configuration ------------------------ Gitweb can be run as CGI script, as legacy mod_perl application (using -ModPerl::Registry), and as FastCGI script. You can find some simple examples -in "Example web server configuration" section in INSTALL file for gitweb (in -gitweb/INSTALL). +ModPerl::Registry), as FastCGI script, and as PSGI application. You +can find some simple examples in "Example web server configuration" +section in INSTALL file for gitweb (in gitweb/INSTALL). + +Note that to run as FastCGI script gitweb must be run with *.fcgi +extension or with `--fastcgi` / `--fcgi` parameter on command line. +To run as PSGI application gitweb must be run with *.psgi extension, +or with `--psgi` / `--plackup` parameter on command line, or from +PSGI web server which sets 'PLACK_ENV' or 'PLACK_SERVER' environment +variable. See "Webserver configuration" and "Advanced web server setup" sections in gitweb(1) manpage. diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 22efec2..0dbdd15 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -1221,16 +1221,22 @@ sub configure_as_fcgi { # let each child service 100 requests our $is_last_request = sub { ++$request_number > 100 }; } +sub configure_as_psgi { + our $CGI = 'PSGI'; # fake +} sub evaluate_argv { my $script_name = $ENV{'SCRIPT_NAME'} || $ENV{'SCRIPT_FILENAME'} || __FILE__; configure_as_fcgi() if $script_name =~ /\.fcgi$/; + configure_as_psgi() + if $script_name =~ /\.psgi$/; return unless (@ARGV); require Getopt::Long; Getopt::Long::GetOptions( 'fastcgi|fcgi|f' => \&configure_as_fcgi, + 'psgi|plack' => \&configure_as_psgi, 'nproc|n=i' => sub { my ($arg, $val) = @_; return unless eval { require FCGI::ProcManager; 1; }; @@ -1244,8 +1250,78 @@ sub evaluate_argv { ); } +# it is very similar to run() subroutine, but it would be hard to +# extract common code; note that $*_hook variables can be set only +# for FastCGI, so they are absent here +sub to_psgi_app { + require CGI::Emulate::PSGI; + + our $CGI = 'CGI'; + our $first_request = 1; + + my $app = CGI::Emulate::PSGI->handler(sub { + CGI::initialize_globals(); + our $cgi = CGI->new(); + + run_request(); + + $first_request = 0; + }); + return $app; +} + +sub build_psgi_app { + require Plack::Builder; + require Plack::Middleware::Static; + + my $sigchld_mw = sub { + my $app = shift; + sub { + my $env = shift; + local $SIG{'CHLD'} = 'DEFAULT'; + local $SIG{'CLD'} = 'DEFAULT'; + $app->($env); + }; + }; + + # you're supposed to "add" middleware from outer to inner. + # note: Plack::Builder DSL (builder, enable_if, enable) won't work + # with "require Plack::Builder" outside BEGIN section. + my $app = to_psgi_app(); + $app = Plack::Middleware::Static->wrap($app, + path => qr{(?:^|/)static/.*\.(?:js|css|png)$}, + root => __DIR__, + encoding => 'utf-8', # encoding for 'text/plain' files + ); + $app = $sigchld_mw->($app) + if (defined $SIG{'CHLD'} && $SIG{'CHLD'} eq 'IGNORE'); + + return $app; +} + +sub run_psgi_app { + my $app = build_psgi_app(); + + # make it runnable as standalone app, + # like it would be run via 'plackup' utility. + # PLACK_ENV is set by plackup etc. + if ($ENV{'PLACK_ENV'} || $ENV{'PLACK_SERVER'}) { + return $app; + } else { + require Plack::Runner; + + my $runner = Plack::Runner->new(); + $runner->parse_options(qw(--env deployment), + qw(--host 127.0.0.1)); + $runner->run($app); + } +} + sub run { evaluate_argv(); + if ($CGI eq 'PSGI' || $ENV{'PLACK_ENV'} || $ENV{'PLACK_SERVER'}) { + return run_psgi_app(); + } $first_request = 1; $pre_listen_hook->() @@ -1266,12 +1342,12 @@ sub run { } } -run(); +our $app = run(); if (defined caller) { # wrapped in a subroutine processing requests, # e.g. mod_perl with ModPerl::Registry, or PSGI with Plack::App::WrapCGI - return; + return $app; } else { # pure CGI script, serving single request exit; -- 1.7.9 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html