(Q 6.5) ! Почему $ENV{REMOTE_USER}} пуста?
(5) CGI-скрипты
(Q 5.1) Что такое CGI и как с ним работать
(A) CGI -- Common Gateway Interface. Cтандарт интерфейса внешних программ
с http-сервером.
Как работать:
HTTP -- клиент-серверный протокол, следовательно со стороны CGI, как
серверного процесса, все взаимодействие выглядит следующим образом:
1. Получение данных от клиента
2. Обработка данных
3. Выдача ответа клиенту.
Пункты 1 и 3 я в кратце опишу здесь, а 2, надеюсь, сделате сами.
Начнем с п.3 как наиболее простого.
3. Выдача данных клиенту: Обычно клиенту выдают текст в формате HTML (ничто
не мешает Вам отправить ему и картинку/видео/etc). Для того, чтобы сервер и
клиент вас поняли, необходимо сказать, что вы выдаете, c помощью заголовка
Content-Type: mime-type/mime-subtype. Обратите внимание на регистр и
последовательность -- если вы скажите нечто типа Content_type, то сервер
вас скорее всего не поймет. (Сообщение типа "500 Internal
Server Error" будет симптомом).
Пример:
print "Content-Type: text/html\n"; # Мы выдаем текст в формате HTML. Также
# можно: text/plain -- простой текст, в
# брузере отобразится аналогично тексту,
# заключённому между тегами <pre></pre>.
# image/gif -- Картинка, формат gif
# video/mpeg -- mpeg-видео
# И целая куча других форматов, см.
# файл mime.types из apache
print "\n"; # <-- еще одна пустая строка, обозначает конец вывода наших
# заголовков. ВАЖНО!
# Теперь мы можем написать свой текст клиенту
print "
<html>
<head>
<title>Моя первая CGI программа</title>
</head>
<body>
<h1>Моя первая CGI программа</h1>
</body>
</head>
";
## Все.
1. Прием данных от клиента
Взаимодействие с клиентом обеспечивается так: Он заполняет форму своими
значениями, нажимает на кнопку "submit", броузер кодирует данные
соответствующим образом и отправляет их серверу.
Определение форм
Производится тегами <form> и </form>. Тег определения форм имеет следующие
аттрибуты
action -- скрипт на сервере, который будет запущен на сервере для
обработки данных формы.
method -- тип взаимодействия с сервером. Может иметь значения GET и
POST.
+ еще некоторые специальные, которые вы можете посмотреть в учебнике
Формы не могут быть вложенными.
Элементы ввода
Определяются тегами <input>, <textarea> и <select>
тег <input>:
<input type=checkbox> -- Элемен ввода "Опция"
<input type=hidden> -- Элемент ввода, который не виден пользователю
<input type=file> -- в Netscape Navigator позволяет загрузить файл на сервер
<input type=image> -- Изображение. Если по нему шелкнуть, это вызовет submit
формы и серверу будут выданы две переменные вида name.x и name.y, где name
-- то, что вы пропишете в name=... тега <input>
<input type=password> -- Элемент ввода Пароль. Позволяет ввести строку,
которая на экране отображается звездочками. Никаких методов защиты при
передаче на сервер не применяется
<input type=radio> -- Радиокнопки
<input type=reset> -- Кнопка сброса значений формы на начальные
<input type=submit> -- Кнопка отправки формы на сервер
<input type=text> -- строка ввода
Все теги <input> имеют аттрибут name -- Наименование элемента. Служит для
идентификации при передаче на сервер, а также другие типа value, width, etc,
название и назначение которых можно опять-таки посмотреть в настоящем
руководстве.
Тег <textarea> -- Поле многострочного ввода.
Тег <select> -- списочный выбор
Пример:
<form action=/cgi-bin/myscript.pl method=GET>
Имя: <input type=string name=name><p>
Пол: <input type=radio name=gender value=male>Мужской
<input type=radio name=gender value=female>Женский
<input type=submit><input type=reset>
</form>
Как это будет видно в моей программе?
Это определяется методом формы, GET или POST
В случае GET сервер установит переменную окружения QUERY_STRING в виде
name1=vaue1&name2=value2&..&nameN=vauleN.
В случае POST аналогичная строка будет записана на стандартный ввод. Ее
длину можно получить через переменную окружения CONTENT_LENGTH.
В обоих случаях данные будут закодированы по следующему алгоритму:
Eсли ASCII код символа больше 32 и меньше 128, то он будет выдан без
изменения.
если символ - пробел, то он заменится на "+" (плюсик, без кавычек)
все остальное преабразуется в вид %xx, где xx -- шестнадцатеричный
код сивола. Если вам повезло и у вас "Русский apache", то он
преобразует его в нормальную кодировку.
Декодирование на perl
tr/+/ /;
s/%([0-9a-fA-F]{2})/pack("c",hex($1))/ge;
(списано из CGI.pm)
На самом деле, можно не возится со всем этим, а использовать модуль CGI.pm
(Имеется в поставке Perl 5.004 и более поздних. Если у вас perl версии 4
или 1, нужно срочно взять на CPAN новую версию perl)
Самый большой и последний пример
Предположим мы делаем поисковую систему, тогда у нас должен быть HTML с
формой и программа, которая будет выполнять поиск.
---- search.html
<html>
<head>
<title>Поиск</title>
</head>
<body>
<h1>Чего искать?</h1>
<form action=/cgi-bin/search.pl method=get>
Строка для поиска: <input type=text name=string width=70><p>
Искать в <input type=checkbox name=searchin value=internet>Интернет
<input type=checkbox name=searchin value=intranet>Интранет
<input type=checkbox name=searchin value=extranet>Экстранет<p>
<input type=submit value=Давай!><input type=reset value="Нет, не надо">
</form>
</body>
</html>
---- конец search.html
Программа:
---- начало search.pl
#!/usr/bin/perl -- поменяйте, как надо
use CGI qw(:standard);
print "Content-Type: text/html\n\n"; # Не забудьте про два "\n"
$string = param("string");
@searchin = param("searchin"); # searchin это checkbox и его значения
# возвращаются списком
# Искать мы сегодня ничего не будем
print "<html>
<body>
<h1>Мы сегодня ничего не ищем</h1>
Но, если бы искали, то: <br>
Искали бы строку <b>$string</b><br>\n";
print "В <b>" . join(" ", @searchin) . "</b>\n";
print "
</body>
</html>
";
---- конец search.pl
Да, кстати, если вы собираетесь писать в файлы с помощью CGI-программ, не
забывайте про file locking.
(Q 5.2) Как заставить браузеры не кешировать выдаваемый результат?
(A)
Выдавайте заголовки в таком виде:
print "Content-Type: type/sub-type\n"; # Подставьте Ваш тип/подтип
print "Pragma: no-cache\n"; # Для HTTP/1.0 клиентов
print "Cache-Control: no-cache\n"; # Для HTTP/1.1
print "Expires: Thu Jan 1 00:00:00 1970\n\n"; # Это уже любой браузер должен
# понять
(Q 5.3) Как перенаправить клиента на другой URL?
(A)
В заголовке напишите:
print "Status: 302\n";
# Или 301. Разница состоит в том, что по стандарту 301 значит "перемещён
# навсегда", а 302 -- "перемещён временно"
print "Location: ВАШ Новый URL\n";
# URL должен быть указан абсолютный
print "URI: ВАШ новый URL\n\n"; # Для http/1.0
Подробности: см. rfc1945(http/1.0), rfc2068(http/1.1)
(Q 5.4) Как загрузить файл на сервер?
(A)
В поставку CGI.pm входит пример file_upload.cgi.
Подробности CGI(3)
(Q 5.5) Как получить файл по URL?
(A)
Возьмите на CPAN библиотеку libwww-perl и смотрите lwpcook(3), там
написаны основы использования библиотеки и есть примеры.
(Q 5.6) ! Как использовать плюшки (cookies)?(A)
Плюшки работают следующим образом: программа CGI добавляет в свой
ответ директиву Set-Cookie, а клиент (если он поддерживает плюшки), при
следующих запросах возвращает их. Сами плюшки представляют из себя пары
имя=значение.
Ситаксис Set-Cookie:
Set-Cookie: имя=значение; expires=дата; path=путь; domain=домен; secure
Сервер может установить не более 20 плюшек, размер всей плюшки не может
превышать 4Kb.
* Имя=значение единственный обязательный параметр. И имя, и значение
могут быть строками текста, не содержащими символов перевода строки,
побелов, ; и табуляции. Если необходимо их использовать, можно эти
символы закодировать при помощи стандартного url-кодирования.
* expires=дата, устанавливается дата истечения срока действия плюшки.
После этой дата плюшка клиентом не возвращается. Если дата не
установлена, то плюшка действует до завершения работы браузера.
* path=путь, обозначает путь на сервере, для которого возвращается
эта плюшка, если не указано, то только для того, который ее создал.
* domain=домен, домен, для которого возвращается эта плюшка. Может
быть доменом уровня 2 и выше, т.е. .example.ru, а не .ru. Если не
установлен, то плюшка возвращается только серверу, который выдал
плюшку.
* secure, указывает, что плющка должна возвращаться только по
защищенному соединению (SSL).
Синтаксис ответа клиента:
Cookie: имя1=значение1; имя2=значение2;....;имяn=значениеn
Программы CGI могут получить пары имя-значение через переменную
окружения HTTP_COOKIE. В модуле CGI.pm значение можно получить при
помощи метода cookie().
Пример:
1. Установка плюшки
---- начало setcookie.pl
#!/usr/bin/perl
print "Content-Type: text/html\n";
print "Set-Cookie: mycookie=some+string\n\n";
print "<html><head><title>Установка плюшки</title></head>\n";
print "<body><h1>Установка плюшки</h1>\n";
print "<a href=getcookie.pl>Щелкни здесь,</a> чтобы посмотреть, что
получилось.
print "</body></html>";
---- конец setcookie.pl
2. Получение плюшки
файл getcookie.pl
---- начало getcookie.pl
#!/usr/bin/perl
use CGI qw(:standard);
print "Content-Type: text/html\n\n";
print "<html><head><title>Получение плюшки</title></head>\n";
print "<body><h1>Плюшка</h1>\n";
print "mycookie = ", getcookie('mycookie');
print "</body></html>";
---- конец getcookie.pl
* * *
(6) Web серверы
(Q 6.1) Как заставить WWW сервер исполнять CGI-программы?
(A)
Apache для Unix/Win32
Надо поправить конфигурационные файлы (я расчитываю, что у вас default
конфиги apache)
Способ 1
srm.conf
Директива ScriptAlias
ScriptAlias /cgi-bin/ /usr/local/apache/cgi-bin/
и в файле access.conf прописать
<Directory /usr/local/apache/cgi-bin/>
Options ExecCGI
</directory>
(если приглядется, там нужно только раскоментировать опции)
Это позволит вам помещать программы в каталог
/usr/local/apache/cgi-bin/ и они будут видны по URL
http://you/cgi-bin/program_name
Способ 2
Добавить в srm.conf директиву
AddHandler cgi-script .cgi
и вы сможете вызывать cgi-программу из любого каталога. Но она должна иметь
окончание .cgi и для этого каталога должно быть разрешено исполнение CGI
(Options ExecCGI в access.conf, написано выше).
Оба способа можно без проблем использовать совместно.
(Q 6.2) Мой CGI-скрипт не работает! Как установить причину?Прежде чем читать дальше, убедитесь в том, что:
1) Вы запустили скрипт с командной строки (не через CGI!!!) и
он работает, а не говорит 'compilation stopped due to errors'
2) Скрипт не только работает, но и выдает нужный заголовок с пустой
строкой после него
3) (Для многопользовательских систем) Вы сделали тоже под пользователем,
под которым работает сервер (su -m имя_пользователя -c script в Unix)
и он столь же благополучно работает
(Q 6.3) Ошибки HTTP 500 и 403. Что это значит и что с ними делать(A) error_log для того и служит, чтобы туда смотреть :-)
Основные причины:
Ошибка 403 "Forbidden"
Возникает, если сервер не может отдать вам содержимое по причине отсутствия
полномочий.
Проверьте:
Может ли user (для apache под Unix это обычно nobody), под которым работает,
сервер исполнять файл?
В частности, может ли сервер исполнять интерпретатор (в случае
скриптовых языков)?
Можно ли для этого каталога исполнять CGI? (Options ExecCGI для
apache)
Ошибка 500 "Internal Server Error"
Возникает в случае внутренней ошибки.
Проверьте:
Выводите ли вы строку "Content-Type:"?
Правильно ли вы ее выводите? (Content-type, Content_Type --
неправильно)
Заканчиваете ли вы хедеры пустой строкой? (Нельзя написать
print "Content-Type: text/html\n";
print "<HTML>\n";
Надо писать:
print "Content-Type: text/html\n\n"; # Два "\n"
print "<HTML>\n";)
Также, эта ошибка возникает, если CGI-программа завершилась с ненулевым
кодом возврата, что часто встречается в случае некомпилируемости вашего
скрипта perl'ом.
Совет: делайте
use CGI::Carp qw(fatalsToBrowser)
во время отладки, тогда вам выдадут сообщение об ошибке perl.
(Q 6.4) ! Почему $ENV{REMOTE_HOST} пуста?(A)
apache устнавливает переменную окружения REMOTE_HOST, если ему
разрешено проводить dns запросы для определения имени. Для 1.3.x
по-умолчанию это выключено. Включается/выключается директивой
HostNameLookups, которая может принимать 3 значения: on -- проводить
запросы, off -- не запрашивать dns и double -- делать двойные запросы:
запрашивать имя хоста, а затем по имени запрашивать ip адрес, для
безопасности.
(Q 6.5) ! Почему $ENV{REMOTE_USER}} пуста?(A)
Переменная окружения REMOTE_USER устанавливается apache в случае, если
скрипт защищен паролем. Как это сделать рассказано на
http://www.apacheweek.com/features/userauth и
http://www.apacheweek.com/features/dbmauth