Bug 122560 - Structurize code, add preload support

git-svn-id: svn://svn.office.custis.ru/3rdparty/bugzilla.org/trunk@1785 6955db30-a419-402b-8a0d-67ecbb4d7f56
master
vfilippov 2013-09-06 13:24:13 +00:00
parent 7c46dd1d44
commit 66a84b1ca7
1 changed files with 149 additions and 59 deletions

View File

@ -80,6 +80,22 @@ sub new
splice @{$self->{_config}}, $i, 2;
$i -= 2;
}
elsif ($self->{_config}->[$i] eq 'preload')
{
# Preload a script
my $script = $self->{_config}->[$i+1];
for (glob $script)
{
eval
{
$self->load_script($_);
};
if ($@ && ref $@ eq 'Bugzilla::HTTPServerSimple::FakeExit')
{
print STDERR "Preloading $_ failed: $@->{error}->[2]\n";
}
}
}
}
return $self;
}
@ -96,17 +112,19 @@ sub port
return $self->{_config_hash}->{port};
}
# Returns Net::Server subclass to run under
sub net_server
{
my $self = shift;
return $self->{_config_hash}->{class};
}
# Format an error in HTML
sub print_error
{
my $self = shift;
my ($status_code, $status_line, $error_text) = @_;
warn $error_text;
print STDERR strftime("[%Y-%m-%d %H:%M:%S] ", localtime) . $error_text . "\n";
print $self->{_cgi}->header(-status => $status_code).
"<html><head><title>$status_line</title></head>".
"<body><h1>$status_line</h1><p>".html_quote($error_text).
@ -114,6 +132,119 @@ sub print_error
return $status_code;
}
# Abort request with error
sub throw
{
my $self = shift;
die bless { error => [ @_ ] }, 'Bugzilla::HTTPServerSimple::FakeExit';
}
# Print a "Not found" error
sub not_found
{
my $self = shift;
my ($script) = @_;
$self->throw(404, 'Not Found', "The requested URL $script was not found on this server.");
}
# Print an "Internal Server Error"
sub internal_error
{
my $self = shift;
my ($text) = @_;
$self->throw(500, 'Internal Server Error', $text);
}
# Load CGI script from file
sub get_script
{
my $self = shift;
my ($script, $for_require) = @_;
my $fd;
if (!open $fd, "<$script")
{
$self->not_found($script);
}
# Reload Bugzilla.pm for old versions on each request
my $preload = Bugzilla->can('request_cache') ? '' : "delete \$INC{'Bugzilla.pm'}; require 'Bugzilla.pm';";
my $content;
local $/ = undef;
$content = <$fd>;
close $fd;
# untaint
($content) = $content =~ /^(.*)$/s;
$content =~ s/\n__END__.*/\n/s;
$content = "\n#line 1 \"$script\"\n$content";
$content = $for_require ? "$preload package main; $content" : "package main; sub { $preload$content }";
return $content;
}
# Load script
sub load_script
{
my $self = shift;
my ($script) = @_;
if (!$subs{$script})
{
my $content = $self->get_script($script);
$subs{$script} = eval $content;
if ($@)
{
$self->internal_error("Error while loading $script:\n$@");
}
}
}
# Simple "FastCGI" implementation - cache *.cgi in subs
sub run_script
{
my $self = shift;
my ($script) = @_;
$self->load_script($script);
my $start = [gettimeofday];
$in_eval = 1;
eval { &{$subs{$script}}(); };
$self->check_errors($script);
my $elapsed = tv_interval($start) * 1000;
print STDERR strftime("[%Y-%m-%d %H:%M:%S]", localtime)." Served $script in $elapsed ms\n";
}
# Use require() instead of sub caching under NYTProf profiler for more correct reports
sub run_script_require
{
my $self = shift;
my ($script) = @_;
my $content = $self->get_script($script, 1);
my $start = [gettimeofday];
$in_eval = 1;
eval $content;
$self->check_errors($script);
my $elapsed = tv_interval($start) * 1000;
print STDERR strftime("[%Y-%m-%d %H:%M:%S]", localtime)." Served $script via require() in $elapsed ms\n";
}
# Finish script run and check for errors
sub check_errors
{
my $self = shift;
my ($script) = @_;
my $err;
if ($@ && (!ref($@) || ref($@) ne 'Bugzilla::HTTPServerSimple::FakeExit'))
{
$err = "Error while running $script:\n$@";
}
eval { Bugzilla::_cleanup(); };
if ($@ && (!ref($@) || ref($@) ne 'Bugzilla::HTTPServerSimple::FakeExit'))
{
print STDERR "Error in _cleanup():\n$@";
}
$in_eval = 0;
if ($err)
{
$self->internal_error($err);
}
}
sub handle_request
{
my $self = shift;
@ -167,67 +298,22 @@ sub handle_request
binmode STDOUT, ':utf8' if Bugzilla->can('params') && Bugzilla->params->{utf8};
# Clear request cache for new versions
$Bugzilla::_request_cache = {};
# Reload Bugzilla.pm for old versions on each request
my $preload = Bugzilla->can('request_cache') ? '' : "delete \$INC{'Bugzilla.pm'}; require 'Bugzilla.pm';";
# Use require() instead of sub caching under NYTProf profiler for more correct reports
my $content;
if ((!$subs{$script} || $ENV{NYTPROF} && $INC{'Devel/NYTProf.pm'}) && open $fd, "<$script")
eval
{
local $/ = undef;
$content = <$fd>;
close $fd;
# untaint
($content) = $content =~ /^(.*)$/s;
$content =~ s/\n__END__.*/\n/s;
$content = "\n#line 1 \"$script\"\n$content";
}
if ($ENV{NYTPROF} && $INC{'Devel/NYTProf.pm'})
if ($ENV{NYTPROF} && $INC{'Devel/NYTProf.pm'})
{
$self->run_script_require($script);
}
else
{
$self->run_script($script);
}
};
if ($@ && ref($@) eq 'Bugzilla::HTTPServerSimple::FakeExit')
{
my $start = [gettimeofday];
eval "$preload package main; $content";
if ($@)
{
return $self->print_error(500, 'Internal Server Error', "Error while running $script:\n$@");
}
my $elapsed = tv_interval($start) * 1000;
print STDERR strftime("[%Y-%m-%d %H:%M:%S]", localtime)." Served $script via require() in $elapsed ms\n";
return 200;
return $self->print_error(@{$@->{error}});
}
# Simple "FastCGI" implementation - cache *.cgi in subs
if (!$subs{$script} && $content)
{
$subs{$script} = eval "package main; sub { $preload$content }";
if ($@)
{
return $self->print_error(500, 'Internal Server Error', "Error while loading $script:\n$@");
}
}
# Run cached sub
if ($subs{$script})
{
my $start = [gettimeofday];
$in_eval = 1;
eval { &{$subs{$script}}(); };
my $err;
if ($@ && (!ref($@) || ref($@) ne 'Bugzilla::HTTPServerSimple::FakeExit'))
{
$err = "Error while running $script:\n$@";
}
eval { Bugzilla::_cleanup(); };
if ($@ && (!ref($@) || ref($@) ne 'Bugzilla::HTTPServerSimple::FakeExit'))
{
warn "Error in _cleanup():\n$@";
}
if ($err)
{
return $self->print_error(500, 'Internal Server Error', $err);
}
$in_eval = 0;
my $elapsed = tv_interval($start) * 1000;
print STDERR strftime("[%Y-%m-%d %H:%M:%S]", localtime)." Served $script in $elapsed ms\n";
return 200;
}
return $self->print_error(404, 'Not Found', "The requested URL $script was not found on this server.")
return 200;
}
# This implementation is nearly the same as the original, but also uses buffered input
@ -318,3 +404,7 @@ http_env PROJECT
# proxy_set_header X-Project 'project';
# Or for Apache:
# RequestHeader set X-Project project
# You can preload all scripts during startup so the maximum amount
# of memory will be shared between workers
preload *.cgi