threads / rfc / 29824

RFC patch, 5 partsgitweb: Prepare for splitting gitweb

Subject: [RFC/PATCH 2/5] gitweb: Prepare for splitting gitweb

## tl;dr

7 messages between Mar 3, 2012 and Mar 8, 2012. Diffs are folded; open one to read it.

replies: 6people: 1as markdown or json

Jakub Narebski· Mar 3, 2012, 17:56 UTC · lore

[RFC/PATCH 0/5] gitweb: Proof of concept of PSGI interface

The major goal of this series is to make it possible for gitweb to run as PSGI application, even if non-native (emulated).

PSGI[1][2] is an interface between Perl web applications and web servers. It is accompanied by Plack, a Perl module and toolkit that contains PSGI middleware (e.g. for serving static files), helpers (e.g. 'plackup' command line launcher) and adapters to web servers (e.g. to CGI, FastCGI, or mod_perl). PSGI and Plack are inspired by Python's WSGI and Ruby's Rack.

[1]: http://plackperl.org [2]: http://search.cpan.org/perldoc?PSGI [3]: http://advent.plackperl.org

First two patches are independent improvements, and make sense even outside this patch series. Last patch is making use of gitweb as PSGI in "git instaweb --httpd=plackup".

Jakub Narebski (5):
  gitweb: Jump do DONE_REQUEST not DONE_GITWEB on error
  gitweb: Prepare for splitting gitweb
  gitweb: Enable running gitweb as PSGI app, via CGI::Emulate::PSGI
  gitweb.psgi: Allow passing arguments to Plack::Runner
  git-instaweb: Use new PSGI interface mode of gitweb
 Documentation/gitweb.txt |   16 +++++++
 git-instaweb.sh          |   33 +++------------
 gitweb/INSTALL           |    3 +
 gitweb/Makefile          |   14 ++++++-
 gitweb/README            |   13 +++++-
 gitweb/gitweb.perl       |   98 +++++++++++++++++++++++++++++++++++++++++++---
 6 files changed, 141 insertions(+), 36 deletions(-)
-- 
1.7.9
Jakub Narebski· Mar 3, 2012, 17:56 UTC · re: Jakub Narebski · lore

[RFC/PATCH 1/5] gitweb: Jump do DONE_REQUEST not DONE_GITWEB on error

On error (die_error()), instead of ending gitweb process by non-local jump to DONE_GITWEB label at the end of requests loop in run() subroutine, just end current request by jumping to DONE_REQUESt at the end of run_request() subroutine.

This change should improve truly persistent deployment methods, currently only FastCGI but in the future also PSGI, as an error page would no longer require reloading whole persistent environment, just end request.

This would allow to create new deployment mechanisms (for example PSGI) based on run_request() and not on run().

Note that DONE_REQUEST label is removed because it is no longer used, and it wouldn't work correctly for PSGI anyway.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
Without this we could not write to_psgi_app() in patch 3/5.

This patch has its value independently on PSGI support, though if it is to be accepted standalone, the commit message would have to be slighlty edited, so that the last two paragraphs of it read:

 "This would allow to create new deployment mechanisms based on
  run_request() and not on run().
  DONE_REQUEST label is removed because it is no longer used."
 gitweb/gitweb.perl |    9 +++++----
 1 files changed, 5 insertions(+), 4 deletions(-)
Show changes to gitweb/gitweb.perl +5 −4
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index e2e6a73..2cca2cd 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1195,6 +1195,10 @@ sub run_request {
 	configure_gitweb_features();
 
 	dispatch();
+
+
+ DONE_REQUEST:
+	1;
 }
 
 our $is_last_request = sub { 1 };
@@ -1252,9 +1256,6 @@ sub run {
 
 		last REQUEST if ($is_last_request->());
 	}
-
- DONE_GITWEB:
-	1;
 }
 
 run();
@@ -4195,7 +4196,7 @@ EOF
 	print "</div>\n";
 
 	git_footer_html();
-	goto DONE_GITWEB
+	goto DONE_REQUEST
 		unless ($opts{'-error_handler'});
 }
 
-- 
1.7.9
Jakub Narebski· Mar 3, 2012, 17:57 UTC · re: Jakub Narebski · lore

Prepare gitweb for having been split into modules that are to be installed alongside gitweb in 'lib/' subdirectory, by adding

  use lib __DIR__.'/lib';

to gitweb.perl (to main gitweb script), and preparing for putting modules (relative path) in $(GITWEB_MODULES) in gitweb/Makefile.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
This patch is the beginning of splitting gitweb (with its 8000+
lines, and more than 240kB) into modules, and is useful independently
of being in this patch series.
Sidenote: it is needed for __DIR__ only here.
 gitweb/Makefile    |   14 +++++++++++++-
 gitweb/gitweb.perl |    8 ++++++++
 2 files changed, 21 insertions(+), 1 deletions(-)
Show changes to 2 files +21 −1

gitweb/Makefile, gitweb/gitweb.perl

diff --git a/gitweb/Makefile b/gitweb/Makefile
index cd194d0..549e7dc 100644
--- a/gitweb/Makefile
+++ b/gitweb/Makefile
@@ -59,6 +59,7 @@ PERL_PATH  ?= /usr/bin/perl
 bindir_SQ = $(subst ','\'',$(bindir))#'
 gitwebdir_SQ = $(subst ','\'',$(gitwebdir))#'
 gitwebstaticdir_SQ = $(subst ','\'',$(gitwebdir)/static)#'
+gitweblibdir_SQ = $(subst ','\'',$(gitwebdir)/lib)#'
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))#'
 PERL_PATH_SQ  = $(subst ','\'',$(PERL_PATH))#'
 DESTDIR_SQ    = $(subst ','\'',$(DESTDIR))#'
@@ -178,12 +179,23 @@ test-installed:
 
 ### Installation rules
 
-install: all
+install: all install-modules
 	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitwebdir_SQ)'
 	$(INSTALL) -m 755 $(GITWEB_PROGRAMS) '$(DESTDIR_SQ)$(gitwebdir_SQ)'
 	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitwebstaticdir_SQ)'
 	$(INSTALL) -m 644 $(GITWEB_FILES) '$(DESTDIR_SQ)$(gitwebstaticdir_SQ)'
 
+install-modules:
+	install_dirs="$(sort $(dir $(GITWEB_MODULES)))" && \
+	for dir in $$install_dirs; do \
+		test -d '$(DESTDIR_SQ)$(gitwebdir_SQ)/$$dir' || \
+		$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitwebdir_SQ)/$$dir'; \
+	done
+	gitweb_modules="$(GITWEB_MODULES)" && \
+	for mod in $$gitweb_modules; do \
+		$(INSTALL) -m 644 $$mod '$(DESTDIR_SQ)$(gitwebdir_SQ)/$$(dirname $$mod)'; \
+	done
+
 ### Cleaning rules
 
 clean:
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 2cca2cd..22efec2 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -10,6 +10,14 @@
 use 5.008;
 use strict;
 use warnings;
+
+use File::Spec;
+# __DIR__ is taken from Dir::Self __DIR__ fragment
+sub __DIR__ () {
+	File::Spec->rel2abs(join '', (File::Spec->splitpath(__FILE__))[0, 1]);
+}
+use lib __DIR__ . '/lib';
+
 use CGI qw(:standard :escapeHTML -nosticky);
 use CGI::Util qw(unescape);
 use CGI::Carp qw(fatalsToBrowser set_message);
-- 
1.7.9
Jakub Narebski· Mar 3, 2012, 17:57 UTC · re: Jakub Narebski · lore

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

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@gmail.com>
---
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(-)
Show changes to 4 files +107 −5

Documentation/gitweb.txt, gitweb/INSTALL, gitweb/README, gitweb/gitweb.perl

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
Jakub Narebski· Mar 3, 2012, 17:57 UTC · re: Jakub Narebski · lore

[RFC/PATCH 4/5] gitweb.psgi: Allow passing arguments to Plack::Runner

Command options after first '--' are passed literally to Plack::Runner as arguments, in addition to default ones used. This way you can select PSGI server, host, port, etc.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
Could be squashed with 4/5, now that I realized that it is that simple.
 gitweb/gitweb.perl |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)
Show changes to gitweb/gitweb.perl +2 −1
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 0dbdd15..6bd7b08 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1312,7 +1312,8 @@ sub run_psgi_app {
 
 		my $runner = Plack::Runner->new();
 		$runner->parse_options(qw(--env deployment),
-		                       qw(--host 127.0.0.1));
+		                       qw(--host 127.0.0.1),
+		                       @ARGV);
 		$runner->run($app);
 	}
 }
-- 
1.7.9
Jakub Narebski· Mar 3, 2012, 17:57 UTC · re: Jakub Narebski · lore

[RFC/PATCH 5/5] git-instaweb: Use new PSGI interface mode of gitweb

Now that gitweb supports PSGI interface itself, use it instead of Plack::App::WrapCGI.

Note that gitweb as PSGI serves static files and ensures correct handling of SIGCHLD, but the wrapper is still needed for logging, mimetype handling (probably unnecessary), and selecting host and port. It is now named gitweb-wrapper.psgi and not gitweb.psgi, though.

The advantage of previous version is that it uses persistent mode (similar to FastCGI); important if $per_request_config is false or coderef.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
Should make git-instaweb plackup mode better.
 git-instaweb.sh |   33 +++++++--------------------------
 1 files changed, 7 insertions(+), 26 deletions(-)
Show changes to git-instaweb.sh +7 −26
diff --git a/git-instaweb.sh b/git-instaweb.sh
index 01a1b05..58e142a 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -53,8 +53,8 @@ resolve_full_httpd () {
 		fi
 		;;
 	*plackup*)
-		# server is started by running via generated gitweb.psgi in $fqgitdir/gitweb
-		full_httpd="$fqgitdir/gitweb/gitweb.psgi"
+		# server is started by running via generated gitweb-wrapper.psgi in $fqgitdir/gitweb
+		full_httpd="$fqgitdir/gitweb/gitweb-wrapper.psgi"
 		httpd_only="${httpd%% *}" # cut on first space
 		return
 		;;
@@ -434,7 +434,7 @@ EOF
 plackup_conf () {
 	# generate a standalone 'plackup' server script in $fqgitdir/gitweb
 	# with embedded configuration; it does not use "$conf" file
-	cat > "$fqgitdir/gitweb/gitweb.psgi" <<EOF
+	cat > "$fqgitdir/gitweb/gitweb-wrapper.psgi" <<EOF
 #!$PERL
 
 # gitweb - simple web interface to track changes in git repositories
@@ -445,8 +445,7 @@ use strict;
 use IO::Handle;
 use Plack::MIME;
 use Plack::Builder;
-use Plack::App::WrapCGI;
-use CGI::Emulate::PSGI 0.07; # minimum version required to work with gitweb
+use Plack::Util;
 
 # mimetype mapping (from lighttpd_conf)
 Plack::MIME->add_type(
@@ -538,25 +537,7 @@ my \$app = builder {
 			\$app->(\$env);
 		}
 	};
-	# gitweb currently doesn't work with $SIG{CHLD} set to 'IGNORE',
-	# because it uses 'close $fd or die...' on piped filehandle $fh
-	# (which causes the parent process to wait for child to finish).
-	enable_if { \$SIG{'CHLD'} eq 'IGNORE' } sub {
-		my \$app = shift;
-		sub {
-			my \$env = shift;
-			local \$SIG{'CHLD'} = 'DEFAULT';
-			local \$SIG{'CLD'}  = 'DEFAULT';
-			\$app->(\$env);
-		}
-	};
-	# serve static files, i.e. stylesheet, images, script
-	enable 'Static',
-		path => sub { m!\.(js|css|png)\$! && s!^/gitweb/!! },
-		root => "$root/",
-		encoding => 'utf-8'; # encoding for 'text/plain' files
-	# convert CGI application to PSGI app
-	Plack::App::WrapCGI->new(script => "$root/gitweb.cgi")->to_app;
+	Plack::Util::load_psgi("$root/gitweb.cgi");
 };
 
 # make it runnable as standalone app,
@@ -574,8 +555,8 @@ if (caller) {
 __END__
 EOF
 
-	chmod a+x "$fqgitdir/gitweb/gitweb.psgi"
-	# configuration is embedded in server script file, gitweb.psgi
+	chmod a+x "$fqgitdir/gitweb/gitweb-wrapper.psgi"
+	# configuration is embedded in server script file, gitweb-wrapper.psgi
 	rm -f "$conf"
 }
 
-- 
1.7.9
Jakub Narebski· Mar 8, 2012, 14:06 UTC · re: Jakub Narebski · lore

[RFC/PATCH 6/5] gitweb.psgi: Use installed static files, if they are available

When gitweb is running as PSGI app, it must itself take care of serving static files: stylesheets, script, images that are required to render gitweb output.

This commit makes gitweb (in PSGI mode) use installed static files from $(gitwebstaticdir) if such directory exists. Before this commit gitweb served static files from 'static/' directory relative to position of gitweb script itself (to __DIR__).

This allows to use gitweb in PSGI mode even if static files are installed to non-standard place.

Note that mechanism of serving is slightly different: the one with __DIR__ uses Plack::Middleware::Static, while the installdir one uses URLMap-ped set of Plack::App::File.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
This probably would have to be reworked, so that using Plack::Middleware::Static
is preferred over set of Plack::App::File (which I think is slightly slower).
 gitweb/Makefile    |    7 +++++--
 gitweb/gitweb.perl |   38 ++++++++++++++++++++++++++++++--------
 2 files changed, 35 insertions(+), 10 deletions(-)
Show changes to 2 files +35 −10

gitweb/Makefile, gitweb/gitweb.perl

diff --git a/gitweb/Makefile b/gitweb/Makefile
index 549e7dc..6f673ff 100644
--- a/gitweb/Makefile
+++ b/gitweb/Makefile
@@ -13,6 +13,8 @@ all::
 prefix ?= $(HOME)
 bindir ?= $(prefix)/bin
 gitwebdir ?= /var/www/cgi-bin
+gitwebstaticdir ?= $(gitwebdir)/static
+gitweblibdir ?= $(gitwebdir)/lib
 
 RM ?= rm -f
 INSTALL ?= install
@@ -58,8 +60,8 @@ PERL_PATH  ?= /usr/bin/perl
 # Shell quote;
 bindir_SQ = $(subst ','\'',$(bindir))#'
 gitwebdir_SQ = $(subst ','\'',$(gitwebdir))#'
-gitwebstaticdir_SQ = $(subst ','\'',$(gitwebdir)/static)#'
-gitweblibdir_SQ = $(subst ','\'',$(gitwebdir)/lib)#'
+gitwebstaticdir_SQ = $(subst ','\'',$(gitwebstaticdir))#'
+gitweblibdir_SQ = $(subst ','\'',$(gitweblibdir))#'
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))#'
 PERL_PATH_SQ  = $(subst ','\'',$(PERL_PATH))#'
 DESTDIR_SQ    = $(subst ','\'',$(DESTDIR))#'
@@ -130,6 +132,7 @@ GITWEB_JSLIB_FILES += static/js/blame_incremental.js
 GITWEB_REPLACE = \
 	-e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
 	-e 's|++GIT_BINDIR++|$(bindir)|g' \
+	-e 's|++GITWEBSTATICDIR++|$(gitwebstaticdir)|g' \
 	-e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \
 	-e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \
 	-e 's|++GITWEB_CONFIG_COMMON++|$(GITWEB_CONFIG_COMMON)|g' \
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 6bd7b08..f871090 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1271,9 +1271,10 @@ sub to_psgi_app {
 }
 
 sub build_psgi_app {
-	require Plack::Builder;
-	require Plack::Middleware::Static;
-
+	# gitweb currently doesn't work with $SIG{CHLD} set to 'IGNORE',
+	# because it uses 'close $fd or die...' on piped filehandle $fh
+	# (which causes the parent process to wait for child to finish).
+	# this middleware is enabled only if $SIG{CHLD} is 'IGNORE'.
 	my $sigchld_mw = sub {
 		my $app = shift;
 		sub {
@@ -1288,14 +1289,35 @@ sub build_psgi_app {
 	# 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');
 
+	if (-d "++GITWEBSTATICDIR++") {
+		require Plack::App::URLMap;
+		require Plack::App::File;
+
+		my $urlmap = Plack::App::URLMap->new();
+		$urlmap->map("/" => $app);
+		foreach my $static_url (@stylesheets, $stylesheet, $logo, $favicon, $javascript) {
+			next if (!defined $static_url || $static_url eq "");
+
+			(my $static_file = $static_url) =~ s!^.*/!!; # basename
+			$static_file = "++GITWEBSTATICDIR++/$static_file";
+			$urlmap->map($static_url => Plack::App::File->new(file => $static_file));
+		}
+		$app = $urlmap->to_app();
+
+	} else {
+		require Plack::Middleware::Static;
+
+		$app = Plack::Middleware::Static->wrap($app,
+			path => qr{(?:^|/)static/.*\.(?:js|css|png)$},
+			root => __DIR__,
+			encoding => 'utf-8', # encoding for 'text/plain' files
+		);
+
+	}
+
 	return $app;
 }
 
-- 
1.7.9

← back to recent threads