Pull to refresh

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

Reading time2 min
Views1.1K
У меня есть 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:
Hubs:
+2
Comments31

Articles