Любой вебмастер, сталкивавшийся с 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 – тоже. Ну а опыт в диагностике – это наше всё. Набивайте свои шишки.