[RFC/PATCH 3/5] gitweb: Enable running gitweb as PSGI app, via CGI::Emulate::PSGI

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]