Основные особенности 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 подробную информацию о
конфигурации.