在过去的几周里,我一直在磨练性能,这是我用于项目的几十年前的大型代码库,有人建议我应该看看像FastCGI或HTTP::Engine这样的东西。我发现使用FastCGI非常简单,但是有一个令人烦恼的问题我找到了混合的答案。
我读过的一些文档说,你永远不应该在通过FastCGI运行的脚本上调用exit
,因为这会损害保持它持久加载的整个概念。其他人说没关系。我的代码在很多地方使用 exit 很重要,以确保没有任何东西继续执行。例如,我有调用授权检查的受限访问组件:
use MyCode::Authorization;
our $authorization = MyCode::Authorization->new();
sub administration {
$authorization->checkCredentials();
#...Do restricted access stuff.
}
为了使代码中尽可能难以出现错误,即允许某人在不应该访问这些功能时访问这些功能,如果答案是用户没有适当的凭据,checkCredentials
在生成带有登录页面的用户友好响应后使用 exit() 结束该过程。 例如:
sub checkCredentials {
#Logic to check credentials
if ($validCredential) {
return 1;
}
else {
# Build web response.
# Then:
exit;
}
}
}
我使用了它,这样我就不会意外地忽略导致安全漏洞的持续内容。目前,调用例程可以安全地假定它只有在提供了正确的凭据时才能从checkCredentials
获得控制权。
但是,我想知道我是否需要删除这些调用才能充分利用 FastCGI。FCGI的$req->Finish()
(或PSGI中HTTP::Engine的等效版本)是否足以替代?
我读过说你永远不应该在通过 FastCGI 运行的脚本上调用 exit,
您不希望进程退出,因为使用 FastCGI 的目的是使用单个进程来处理多个请求(以避免加载时间等)。
所以你要做的是覆盖退出,这样就结束了特定于请求的代码,而不是 FastCGI 请求循环。
您可以覆盖exit
,但必须在编译时这样做。因此,请使用标志来指示覆盖是否处于活动状态。
our $override_exit = 0;
BEGIN {
*CORE::GLOBAL::exit = sub(;$) {
die "EXIT_OVERRIDEn" if $override_exit;
CORE::exit($_[0] // 0);
};
}
while (get_request()) {
# Other setup...
eval {
local $override_exit = 1;
handle_request();
};
my $exit_was_called = $@ eq "EXIT_OVERRIDEn";
log_error($@) if $@ && !$exit_was_called;
log_error("Exit calledn") if $exit_was_called;
# Other cleanup...
}
但这会产生一个可能无意中捕获的异常。因此,让我们改用last
。
our $override_exit = 0;
BEGIN {
*CORE::GLOBAL::exit = sub(;$) {
no warnings qw( exiting );
last EXIT_OVERRIDE if $override_exit;
CORE::exit($_[0] // 0);
};
}
while (get_request()) {
# Other setup...
my $exit_was_called = 1;
EXIT_OVERRIDE: {
local $override_exit = 1;
eval { handle_request() };
log_error($@) if $@;
$exit_was_called = 0;
}
log_error("Exit calledn") if $exit_was_called;
# Other cleanup...
}