Merge branch 'jn/gitweb-fastcgi'
authorJunio C Hamano <gitster@pobox.com>
Mon, 21 Jun 2010 13:02:42 +0000 (06:02 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 21 Jun 2010 13:02:42 +0000 (06:02 -0700)
* jn/gitweb-fastcgi:
  gitweb: Run in FastCGI mode if gitweb script has .fcgi extension
  gitweb: Add support for FastCGI, using CGI::Fast
  gitweb: Put all per-connection code in run() subroutine

1  2 
gitweb/gitweb.perl

@@@ -946,62 -952,134 +965,149 @@@ sub evaluate_and_validate_params 
  
  # path to the current git repository
  our $git_dir;
- $git_dir = "$projectroot/$project" if $project;
- # list of supported snapshot formats
- our @snapshot_fmts = gitweb_get_feature('snapshot');
- @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
- # check that the avatar feature is set to a known provider name,
- # and for each provider check if the dependencies are satisfied.
- # if the provider name is invalid or the dependencies are not met,
- # reset $git_avatar to the empty string.
- our ($git_avatar) = gitweb_get_feature('avatar');
- if ($git_avatar eq 'gravatar') {
-       $git_avatar = '' unless (eval { require Digest::MD5; 1; });
- } elsif ($git_avatar eq 'picon') {
-       # no dependencies
- } else {
-       $git_avatar = '';
+ sub evaluate_git_dir {
+       our $git_dir = "$projectroot/$project" if $project;
+ }
+ our (@snapshot_fmts, $git_avatar);
+ sub configure_gitweb_features {
+       # list of supported snapshot formats
+       our @snapshot_fmts = gitweb_get_feature('snapshot');
+       @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
+       # check that the avatar feature is set to a known provider name,
+       # and for each provider check if the dependencies are satisfied.
+       # if the provider name is invalid or the dependencies are not met,
+       # reset $git_avatar to the empty string.
+       our ($git_avatar) = gitweb_get_feature('avatar');
+       if ($git_avatar eq 'gravatar') {
+               $git_avatar = '' unless (eval { require Digest::MD5; 1; });
+       } elsif ($git_avatar eq 'picon') {
+               # no dependencies
+       } else {
+               $git_avatar = '';
+       }
  }
  
 +# custom error handler: 'die <message>' is Internal Server Error
 +sub handle_errors_html {
 +      my $msg = shift; # it is already HTML escaped
 +
 +      # to avoid infinite loop where error occurs in die_error,
 +      # change handler to default handler, disabling handle_errors_html
 +      set_message("Error occured when inside die_error:\n$msg");
 +
 +      # you cannot jump out of die_error when called as error handler;
 +      # the subroutine set via CGI::Carp::set_message is called _after_
 +      # HTTP headers are already written, so it cannot write them itself
 +      die_error(undef, undef, $msg, -error_handler => 1, -no_http_header => 1);
 +}
 +set_message(\&handle_errors_html);
 +
  # dispatch
- if (!defined $action) {
-       if (defined $hash) {
-               $action = git_get_type($hash);
-       } elsif (defined $hash_base && defined $file_name) {
-               $action = git_get_type("$hash_base:$file_name");
-       } elsif (defined $project) {
-               $action = 'summary';
-       } else {
-               $action = 'project_list';
+ sub dispatch {
+       if (!defined $action) {
+               if (defined $hash) {
+                       $action = git_get_type($hash);
+               } elsif (defined $hash_base && defined $file_name) {
+                       $action = git_get_type("$hash_base:$file_name");
+               } elsif (defined $project) {
+                       $action = 'summary';
+               } else {
+                       $action = 'project_list';
+               }
        }
+       if (!defined($actions{$action})) {
+               die_error(400, "Unknown action");
+       }
+       if ($action !~ m/^(?:opml|project_list|project_index)$/ &&
+           !$project) {
+               die_error(400, "Project needed");
+       }
+       $actions{$action}->();
  }
- if (!defined($actions{$action})) {
-       die_error(400, "Unknown action");
+ sub run_request {
+       our $t0 = [Time::HiRes::gettimeofday()]
+               if defined $t0;
+       evaluate_uri();
+       evaluate_gitweb_config();
+       evaluate_git_version();
+       check_loadavg();
+       # $projectroot and $projects_list might be set in gitweb config file
+       $projects_list ||= $projectroot;
+       evaluate_query_params();
+       evaluate_path_info();
+       evaluate_and_validate_params();
+       evaluate_git_dir();
+       configure_gitweb_features();
+       dispatch();
  }
- if ($action !~ m/^(?:opml|project_list|project_index)$/ &&
-     !$project) {
-       die_error(400, "Project needed");
+ our $is_last_request = sub { 1 };
+ our ($pre_dispatch_hook, $post_dispatch_hook, $pre_listen_hook);
+ our $CGI = 'CGI';
+ our $cgi;
+ sub configure_as_fcgi {
+       require CGI::Fast;
+       our $CGI = 'CGI::Fast';
+       my $request_number = 0;
+       # let each child service 100 requests
+       our $is_last_request = sub { ++$request_number > 100 };
  }
- $actions{$action}->();
- DONE_GITWEB:
- 1;
+ sub evaluate_argv {
+       my $script_name = $ENV{'SCRIPT_NAME'} || $ENV{'SCRIPT_FILENAME'} || __FILE__;
+       configure_as_fcgi()
+               if $script_name =~ /\.fcgi$/;
+       return unless (@ARGV);
+       require Getopt::Long;
+       Getopt::Long::GetOptions(
+               'fastcgi|fcgi|f' => \&configure_as_fcgi,
+               'nproc|n=i' => sub {
+                       my ($arg, $val) = @_;
+                       return unless eval { require FCGI::ProcManager; 1; };
+                       my $proc_manager = FCGI::ProcManager->new({
+                               n_processes => $val,
+                       });
+                       our $pre_listen_hook    = sub { $proc_manager->pm_manage()        };
+                       our $pre_dispatch_hook  = sub { $proc_manager->pm_pre_dispatch()  };
+                       our $post_dispatch_hook = sub { $proc_manager->pm_post_dispatch() };
+               },
+       );
+ }
+ sub run {
+       evaluate_argv();
+       $pre_listen_hook->()
+               if $pre_listen_hook;
+  REQUEST:
+       while ($cgi = $CGI->new()) {
+               $pre_dispatch_hook->()
+                       if $pre_dispatch_hook;
+               run_request();
+               $pre_dispatch_hook->()
+                       if $post_dispatch_hook;
+               last REQUEST if ($is_last_request->());
+       }
+  DONE_GITWEB:
+       1;
+ }
+ run();
  
  ## ======================================================================
  ## action links