30 August 2007

goto в CORE:: GLOBAL:: exit — где грабли?

Perl
У меня есть CGIшка, которую хочется запустить под FastCGI.

Она использует фреймвок, который обрабатывает html-шаблоны, выводит ошибки (die) в браузер, делает всяческие редиректы, отдаёт файлы, etc. — всё как обычно. Этот фреймвок, после формирования и вывода ответа на STDOUT, делает exit() — что тоже, в общем, не оригинально.

Этот exit() может быть вызван внутри eval(), а то и нескольких вложенных eval-ов — к примеру, если где-то, в глубине вычислений, CGIшка решает выдать юзеру редирект. Она при этом вызывает функцию фреймвока, он делает print "Location: ..." и exit().

Но под FastCGI exit() делать нельзя.

Следовательно, приходится оный exit перехватывать, и делать либо die() (точно так же, как это делает, например, mod_perl), либо goto. Проблема с die() заключается в том, что в случае если exit() был вызван внутри eval(), и после вызова eval юзер не передаст неизвестную ему ошибку выше снова вызвав die() (а гарантировать такое поведение после каждого eval в этой CGIшке я не могу — скорее могу гарантировать что обычно этого не происходит), то exit() приведёт просто к выходу из ближайшего eval(), на что CGIшка вызвавшая редирект явно не рассчитывала!

Псевдо-код CGIшки


sub that_cgi_script {
...
eval { do_something() };
...
}
sub do_something {
# 1) may return if everything ok
# 2) may die on error, but such error non-important to
# calling code in above case and will be catched by
# eval and ignored
# 3) may redirect: print Location header and call exit()
}

Вариант exit через die


my $FCGI_EXIT = "FCGI NORMAL EXIT\n";
BEGIN { *CORE::GLOBAL::exit = sub { die $FCGI_EXIT }; }
while (CGI::Fast->new()) {
eval { that_cgi_script(); };
die $@ if $@ && $@ ne $FCGI_EXIT;
$CGI::Fast::Ext_Request->Finish();
}

Вариант exit через goto


BEGIN { *CORE::GLOBAL::exit = sub { goto EXIT }; }
while (CGI::Fast->new()) {
eval { that_cgi_script(); };
die $@ if $@;
EXIT:
$CGI::Fast::Ext_Request->Finish();
}

Что говорит perldoc -f goto


It may not be used to go into any construct that requires initialization, such as a subroutine or a «foreach» loop. It also can't be used to go into a construct that is optimized away, or to get out of a block or subroutine given to «sort».

Итак, где же грабли?


Похоже, единственная проблема с goto, применимая к этой ситуации, это вызов exit() внутри sort. :) Я искренне верю, что эта CGIшка такого себе не позволяет. Аминь.

Какие ещё могут быть грабли при использовании goto в CORE::GLOBAL::exit? Меня настораживает то, что mod_perl goto не использует, хотя вроде-бы это более эффективное решение, чем die().
Tags:perlграблиfastcgidieexit
Hubs: Perl
+2
851 2
Comments 31
Popular right now
Тренажер product-менеджера
December 3, 202028,900 ₽SkillFactory
SEO-специалист
December 7, 202064,900 ₽Нетология
iOS-разработчик с нуля
December 7, 202070,740 ₽Нетология
Python для работы с данными
December 7, 202031,500 ₽Нетология
Top of the last 24 hours