Основные особенности CGI-сценариев
Такие сценарии --- это активные фильтры, которые обрабатывают поток ввода
HTTP-сервера (Hyper Text Transfer Protocol --- протокол работы WWW Internet)
как свой поток ввода и производят на основе него свой поток вывода, который
возвращается обратно клиенту HTTP-сервера. Сценарии выполняются в среде
оболочки ОС.
Широко используемый в Internet HTTP-сервер --- это программа Apache.
CGI-сценарии для Apache обычно хранятся в подкаталоге cgi-bin каталога
данных Apache.
Создадим в этом каталоге CGI-сценарий в файл cgi1.sh:
#!/bin/bash
echo 'Content-type: text/html; charset=UTF8'
echo
echo '<p>Hello from CGI-script'
В первой строчке указывается интерпретатор языка программирования для
выполнения сценария стандартным для оболочки ОС образом. В следующих, до
пустой строки формируется заголовок ответа. Необходимо указывать тип
содержимого - это обычно text/html. Информация о кодировке опциональна,
но она имеет высший приоритет для браузера. Если кодировка не указана,
то она устанавливается браузером по тегу META в заголовке формируемого
html-документа. Иногда, особеннно при отладке в заголовок ответа полезно
включать строку
Pragma: nocache
для отключения кэширования. После заголовка идёт текст программы на выбранном
языке. Для запуска сценария необходимо установить его файлу соответствующие
права.
В строке браузера теперь можно набрать localhost/cgi-bin/cgi1.sh
Сделаем сценарий cgi2.sh, добавив следующие строки
echo '<pre>'
set
echo '</pre>'
Запустив его, получим распечатку окружения. Особенно важны переменные
REMOTE_ADDR - сетевой адрес браузера (ip) и SERVER_ADDR - адрес сервера.
Поток вывода сценария - это поток html-текста, отправляемый браузеру. Поток
ввода сценария формируется браузером в виде текста в специальном формате.
Для взаимодействия с CGI-сценариями в HTML используют тег (tag)
FORM для создания интерактивных компонент документа,
которые оформляются как его элементы INPUT (кнопки и однострочный текст),
SELECT (всплывающее меню и списки) и TEXTAREA (многострочный текст).
Тег FORM может содержать и неинтерактивные элементы, например,
сопроводительный текст. Атрибут ACTION тега FORM должен содержать имя
соответствующего форме CGI-сценария.
Пример HTML-документа (post.html), использующего CGI-сценарий.
<head>
<meta http-equiv=Content-Type content=text/html;charset=koi8-r>
</head>
<body>
<p align=center><h1>Здравствуйте!</h1>
<br>
<p>Это страница с интерактивными элементами для взаимодействия с
сервером.
<br>
<br>
<form action=/cgi-bin/cgi3.sh method=post>
<p>Введите два сообщения.<br>
<input type=text name=field1 value="" size=32 maxlength=64>
<br>
<input type=text name=field2 value="" size=32 maxlength=64>
<br>
<input type=submit value=OK>
<input type=reset value=Clear>
</form>
<hr>
</body>
Введенный пользователем текст и другая информация посылается CGI-сценарию в
виде <имя поля>=<значение>, все пробелы в тексте заменяются знаками плюс,
а все символы с кодами, большими 127, и специальные символы такие как ``+'',
``&'', ``!'' и ``='' --- на их шестнадцатеричные из двух цифр коды,
предваренные знаком процента. Поля разделяются знаком ``&''.
Создадим сценарий cgi3.sh, который будет просто возвращать весь ввод обратно
браузеру.
#!/bin/bash
echo Content-type: text/html
echo
echo '<p>Input stream for CGI-script:'
cat
CGI-сценарий должен сначала разделить поля, затем имена полей и соответствующих
им текстовых значений, затем, как правило, преобразовать значения-тексты в их
исходный вид и,наконец, провести собственно обработку запроса к HTTP-серверу,
т.е. сформировать на основе запроса HTML-документ (динамическую HTML-страницу).
Кроме метода передачи данных на сервер post можно использовать метод get.
Разница между ними в том, что при использовании get все данные передаются явно,
в URI серверу, а при использовании post без использования URI. Если не указать
метод явно, то будет использован get. Метод get позволяет использовать закладки,
а post не ограничивает размер посылаемых данных. Кроме того, метод get заносит
данные в переменную среды QUERY_STRING, а не в поток вывода.
Сделаем cgi4.sh, изменив последнюю строку в последнем сценарии на
echo $QUERY_STRING
Сделаем также документ get.html из post.html, заменив post на get и cgi3 на
cgi4. Посмотрим в браузере на get.html и заметим отличие в адресной строке.
Проведем теперь обработку посланных от браузера данных и сформируем
динамический html-документ. В post.html заменим cgi3 на cgi5 и сохраним файл с
именем dynamic.html.
Сценарий cgi5.sh заносит все сообщения в общий список и затем создает из него
новую, динамическую страницу.
#!/bin/bash
echo Content-type: text/html\; charset=UTF8
echo
echo '<br>Ваши сообщения:<br><br>'
awk '{
n = split($0, b, "&")
for (i = 1; i <= n; i++) {
split(b[i], a, "=")
s = a[2]
gsub("+", " ", s)
p = index(s, "%")
while (p) {
d = sprintf("%c", strtonum("0x"substr(s, p + 1, 2)))
s = substr(s, 1, p - 1) d substr(s, p + 3)
p = index(s, "%")
}
print s "<br>"
}
}' >>xmlist
awk '{print NR, $0}' xmlist
У файла xmlist (он разщещен в каталоге cgi-bin) должны быть установлены права,
позволяющие писать в него и читать из него.
Этот сценарий написан на языке оболочки, но почти все операции по обработке
данных осуществляются вызовами программы аук (awk). CGI-программа может быть
написана на любом языке сценариев или быть бинарным файлом. Некоторые языки,
например перл, поддерживают специальные операции для проведения замен, более
мощные, чем gsub в аук, которые позволяют провести приведенные в сценарии
операции замены без цикла двумя строками.
При написании CGI-сценариев часто бывает полезна http-команда переадресации,
которая вставляется в тег META:
<meta http-equiv=refresh content=1;url=http://СЕРВЕР/ФАЙЛ>
Например, создадим сценарий cgi6.sh, который осуществит переход на заданную
страницу.
#!/bin/bash
echo Content-type: text/html
echo
echo '<meta http-equiv=refresh content=1;url=http://'$SERVER_NAME'/get.html>'
Величина content задает частоту в секундах для перезагрузки страницы. Можно
поставить content=0, тогда переход должен проходить мгновенно, но с
некоторыми редкими браузерами возможны проблемы.
Для переадресации можно использовать строку http-заголовка, например, сценарий
cgi7.sh
#!/bin/bash
echo Content-type: text/html
echo Pragma: nocache
echo Location: http://$SERVER_NAME/get.html
echo
Делает тоже самое, что и cgi6.sh, но без задержки и проблем с кэшированием.
Для взаимодействия с CGI-сценариями удобно пользоваться возможностью
предварительной обработки html-документов, встроенной во многие http-серверы.
Эта возможность даёт вставлять коды сценария прямо в html-файл, используя
элементы между <? и >. Чаще всего так работают с языком PHP. Помимо этого,
можно использовать специальные команды для сервера, например, для вставки
содержимого файла, текущего времени и т.п. Такие команды, позволяют также
использовать переменные, условные конструкции, настраивать конфигурацию и др.
В сервере Apache их заключают между <!--# -->, т.е. оформляют как комментарий.
Эти команды называют SSI - Server Side Includes - вставки стороны сервера.
Например, команда <!--#include virtual="/cgi-bin/htmlgen.sh"--> вставит в html
содержимое потока вывода указанного сценария, а команда
<!--#include virtual="/data.html"--> - содержимое указанного файла, т.е.
порядок обработки определяется местоположением файла. Команда exec, например,
<!--#exec cmd="ls"-->, очень похожа на первую, разница только в том, что тут
можно использовать полный путь к программе относительно ФС ОС и не надо
формировать http-заголовок из 2 строк. В первых двух случаях использовалась
адресация относительно виртуальной ФС сервера. Вместо virtual можно
использовать слово file и использовать адресацию относительно текущего файла,
но без .. - перехода на уровень выше.
Обычно файл, предназначенный для предварительной обработки, т.е. содержащий
коды PHP, SSI и т.п., имеет специальное расширение: php, shtml, ... В
конфигурации сервера прописывается, как обрабатывать файлы с такими
расширениями.
Справку по командам SSI и другим возможностям сервера можно получить из
браузера, выбирая ресурс /manual на сервере. Ресурс /server-status показывает
текущее состояние сервера Apache, а ресурс /server-info подробную информацию о
конфигурации.