Любой вебмастер, сталкивавшийся с Perl-скриптами, не понаслышке знаком с ошибкой 500. Она же Internal Server Error. Она же внутренняя ошибка сервера. Бабай, которым пугают нюбов, и которого опасаются даже самые опытные веб-программеры. Отличительные особенности – проблематичность диагностики и выявления причины возникновения. Логи зачастую содержат фразу “Premature end of script headers”, которая в принципе может означать что угодно.

В этой статье я опишу все проблемы, в 99,999% случаев вызывающие злополучный 500 вылет и методики борьбы с ними. Можешь называть это сборником граблей, на которые регулярно наступают даже опытные девелоперы. И при возникновении неполадок с выводом ошибки с кодом 500 можешь смело брать эту статью и сверяться попунктово.

Поехали

Перво-наперво надо убедиться, что Perl скрипт вообще закачан в ASCII режиме. Для уверенности запусти клиент Cute FTP, и в меню File найди пункт “Transfer Type”, в который проставь “ASCII”. Если ты закачаешь .pl или .cgi файл через Internet Explorer по
FTP - гарантированно получишь 500 ошибку, ибо ослик по дефолту копирует файло в бинарном режиме. (Лирическое отсутпление: тот же Cute FTP смотрит на расширение файла и с радостью закачает твой Fast CGI скрипт в бинарном режиме. Выставляй Transfer Type вручную в ASCII)

Далее проверь права доступа (chmod). Оптимально – поставь 0755. Помимо прав важную роль играет владелец файла и (важно!) папки с этим файлом. Обладатели виртуального хостинга в принципе могут переходить к следующему абзацу, эта проблема скорее всего их не коснётся. Счастливые же владельцы выделенных серверов дружно топают читать httpd.conf (главный конфиг апача, если кто не знает) и искать в нём примерно следующие строчки:

User admin
Group apache

Нашли? Отлично. Юзер и группа на выполняемый Perl-скрипт должны в точности соответствовать. Запускаем putty, коннектимся, логинимся и пишем примерно следующее:

Chown admin:apache /home/www/script.pl
Chown admin:apache /home/www

Не будет лишним проверить целостность скрипта и верность форматирования никсовой командой cat. Кто знает, может ты когда-то решил править скрипт редактором MS Word
:). Особенно много проблем вызывает то, что в никсах и виндах символы перевода строки различаются. Поэтому не имеющие спец. редактора (например dzsoft perl editor) – юзайте notepad (извращенцы…).

Теперь стоит убедиться, что скрипту можно запускаться в той директории, где он лежит. Часто по дефолту cgi-файлы можно вызывать только из диры
cgi-bin. Чтобы разрешить запуск скриптов из другой директории открываем httpd.conf и пишем примерно следующее (в этом примере разрешаем запуск скриптов из диры
/home/www/dir):

<Directory /home/www/dir>
Options +ExecCGI
</Directory>

И перезапускаем апача. Или закачиваем в папку /home/www/dir файлик .htaccess со строчкой Options +ExecCGI внутри.

Лезем вглубь!

Итак, все условия размещения файла соблюдены, а апач всё равно заботливо возвращает Error 500. Возможно проблема скрыта внутри самого скрипта.

Перво-наперво убедись, что скрипт начинается так:

#!/usr/bin/perl -w
use strict;

Первая строчка вызывает интерпретатор с ключом –w, (подробнее – perldoc perlrun) а вторая включает прагму strict, спасающую от глупостей и сводящую на минимум человеческий фактор (подробнее - perldoc
strict). Довольно распространённой ошибкой в коде (которую я ставлю в ряд с синтаксическими) является вызов несуществующей процедуры. Следствие – 500 ошибка. От всего этого спасут две вышеуказанные волшебные строчки. Или, например, есть у нас переменная $variable, а в одном месте кода мы опечатались и написали $vaiable. Такую ошибку можно искать часами, а можно указать ключ –w и интерпретатор поругается. C указанием строчки, содержащей бяку, of course. Лирическое отступление
[/off].

Затем обязательно поищи в тексте скрипта место, где сценарий отдаёт браузеру заголовки. Контент без заголовков автоматически равняется пятисотой ошибке.
В общем случае третья строчка нашего скрипта будет выглядеть так:

print "Content-type: text/htm\n\n”;

Всё так? ОК. Теперь, если на твоём компьютере установлен Win, скачиваем Activestate Perl и редактор DzSoft Perl. Открываем скрипт редактором на локальной машине и нажимаем на кнопочку “Syntax Check”. Под никсами всё проще:

perl –c /home/www/script.pl

Да, верно, любая ошибка в синтаксе, вызов несуществующей процедуры, попытка заюзать
не установленные в системе модули, бесконечный цикл/рекурсия вызовут ошибку 500 с вероятностью близкой к единице. Ключ –c при запуске интерпертатора проверит синтаксис. 

А в случае запуска perl /home/www/script.pl нам в консоли напишут, что конкретно не так в нашем скрипте.
Например, так:

Unrecognized character \xF0 at p.cgi line 5.

Согласись, такой репорт всяко приятнее Internal Server Error. Вообще, запуск из консоли традиционно снимает большинство вопросов. Так что проси SSH у хостера, или ставь Denver + Activestate Perl у себя на машине. В
конце концов, админ мог поставить ограничение по времени на выполнение, а твой мега-тормознутый спам-бот не укладывается в 20 секунд и сворачивается по таймауту. С выводом нашей любимой ошибки в браузер, ага.

Так мы плавно подошли к ещё одной строчке, которую я крайне рекомендую включать во время отладки в наш глючащий скрипт.

use CGI::Carp qw(fatalsToBrowser);

И не надо презрительно воротить нос. В отсутствие возможности запустить скрипт из консоли родным интерпретатором и посмотреть на его ругань данная мулька модуля CGI выведет в браузер часть ошибок (прямо как в облизанном веб-девелопырями PHP).

В итоге мы имеем универсальную конструкцию:

#!/usr/bin/perl -w
use strict;
use CGI::Carp qw(fatalsToBrowser);
print "Content-type: text/htm\n\n”;

После отладки третью строчку можно закомментировать.

Нередко случается, что скрипт вылетает из-за неоптимизированного/непродуманного кода. Про таймаут я уже писал выше, теперь же приведу пример из личной практики. Есть некоторый скрипт, выполняющий роль загрузчика файлов. Отдаёт пользователям файло, лежащее в директории вне открытого для веба раздела. Вот кусок кода:

print "Content-Length: $size\n";
print "Content-Type: application/$ext\n\n";
open (FILE, "../../files/b/$file.$ext") || die;
@lines = <FILE>;
close(FILE);
binmode(STDOUT);
print @lines;

Доколе скрипт отдавал масенькие файлики до мегабайта, всё было ОК, но как только таким
макаром попытались открыть стомегабайтный архив… Весь этот архив читался и полностью забивался в массив @lines, после чего отдавался в бинарном режиме юзеру. Руки отрывать за такие вещи надо! То же самое, но корректно (в браузер сразу выкидывается прочитанная информация, без создания ненужного здесь транзитивного массива):

print "Content-Length: $size\n";
print "Content-Type: application/$ext\n\n";
binmode(STDOUT);
open (FILE, "../../files/b/$file.$ext") || die;
while (<FILE>) {print}
close(FILE);

Я думаю, примера доступнее не найти и за сим сползаю с темы оптимизации кода, ибо она достойна отдельного материала (книги).

К слову о функции OPEN. В случае попытки доступа к несуществующему файлу скрипт может вывести всю информацию до левого OPEN, а может сразу выдать 500 ошибку. Будьте бдительны.

О бедном апаче замолвите слово…

Часть типичных граблей с настройкой веб-сервера описана в начале статьи, так как именно они чаще всего портят жизнь простым сайтостроителям. Ниже я расскажу о ещё нескольких нюансах.

Для многих это может показаться неожиданным, но Internal Server Error может вылететь и из-за неверной настройки веб-сервера. Достаточно поместить в корень сайта файл .htaccess с синтаксической ошибкой, и весь сайт будет состоять из одной страницы с 500 ошибкой. В случае же ошибки в httpd.conf после перезагрузки сервер просто будет не доступен. К счастью, проблема с .htaccess несложно выявляется просмотром error-лога апача. Думаю, что строчка наподобие

[Wed Apr 14 17:01:38 2004] [alert] [client 217.16.16.16] /home/user/www/.htaccess: Invalid command 'DrectoryIndex', perhaps mis-spelled or defined by a module not included in the server configuration

ясно даёт понять, что ты тупо “очепятался” в корневом для твоего сайта хтаксесе, и именно из-за этого “ничего не грузится”.

А кто сказал, что твой сервер вообще считает .cgi файлы perl-скриптами? Пишем в .htaccess следующую строчку:

AddHandler cgi-script .cgi

Итоги

Ну вот фактически и всё. Я расписал практически все ситуации, вызывающие ошибку 500 и их решения. Самое основное, что надо помнить: грамотно настроенный сервер, логи и запуск скриптов из консоли – ваши друзья. Ключи –w, --c, прагма strict – тоже. Ну а опыт в диагностике – это наше всё. Набивайте свои шишки.

Check Also

Печать силой мысли. Как работают интерфейсы мозг-компьютер и на что они способны сегодня

Управлять компьютером силой мысли — давняя и пока что несбыточная мечта. Но шаги в этом на…

1 комментарий

  1. Аватар

    miXL520

    31.05.2019 at 09:37

    Добрый день!
    Может, сможете мне помочь?
    Ситуация такая: Oracle enterprise linux 7.6, Oracle 12.1.
    В системе линукс 2 юзера: root и oracle.
    Нужно настроить выгрузку отчета в браузере: клиент входит на страничку http://IP-сервера/, вводит период времени и жмет показать результат. В это время запускается perl — скрипт, коннектится к БД Oracle и выгружает данные.

    МОЯ ПРОБЛЕМА:
    пишу простейший скрипт perl:
    #!/usr/bin/perl
    print «Content-type: text/html\n\n»;
    print «Hello, World.»;

    Далее запускаю его так:
    cd /var/www/cgi-bin/
    perl test.pl

    и получаю результат.
    Далее набираю в браузере
    http://192.168.56.107/cgi-bin/test.pl
    И в браузере тоже получаю результат.

    Пишу второй скрипт:

    #!/usr/bin/perl -W
    use DBI;

    # $ENV{ORACLE_HOME}=»/home/orcl/app/oracle/product/11.2.0.4″;
    # $ENV{NLS_LANG}=»AMERICAN_CIS.CL8MSWIN1251″;

    my $db=»192.168.56.107″;
    my $dbsid=»test»;
    my $dbuser=»system»;
    my $dbpass=»123456″;

    $db = DBI->connect(«dbi:Oracle:host=$db;sid=$dbsid», $dbuser, $dbpass);

    $sql0=$db->prepare(qq{
    select * from m
    });
    $sql0->execute();

    while(@row=$sql0->fetchrow_array){
    print $row[0].»\n»;
    }

    Далее запускаю его так:
    cd /var/www/cgi-bin/
    perl test.pl
    и получаю результат.
    Но как только пытаюсь его в браузере запустить
    http://10.16.29.105/cgi-bin/test2.pl
    , то в браузере чистая страница, и ошибка в логах апача:
    install_driver(Oracle) failed: Can’t locate DBD/Oracle.pm in @INC (@INC contains: /home/oracle/perl5/lib/perl5/x86_64-linux-thread-multi/DBD/ /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at (eval 3) line 3.

    __________________________________________________
    Что я делал:
    (под root)установка apache:
    yum install -y httpd

    (под root)добавляем в автозагрузку
    systemctl enable httpd

    (под root)запускаем:
    systemctl start httpd

    (под root)проверяем
    netstat -tulnp | grep httpd

    (под root)(если в браузере не заходит на сайт)
    iptables -F

    (под root)
    yum install -y cpan

    (под oracle)
    cpan
    дать полные права на папку /usr
    cpan> install CGI
    cpan> install DBI
    cpan> install YAML
    cpan> install DBD::Oracle

    Причем, после установки каждого модуля в cpan везде было make Makefile.pl — OK
    только были предупреждения при установке install DBD::Oracle
    Unable to connect to database.
    Я даже пробовал во второй скрипт добавить
    use lib = /…..(путь к Oracle.pm)
    и все равно эта же ошибка.

Оставить мнение