Спецификация CGI
Итак, что в точности представляет собой «набор правил», позволяющий CGI-программе, скажем, в Батавии, штат Иллинойс, обмениваться данными с веб-броузером во Внешней Монголии? Официальную спецификацию CGI наряду с массой других сведений о CGI можно найти на сервере NCSA по адресу http://hoohoo. ncsa.uluc.edu/ cgi/. Однако эта глава для того и существует, чтобы вам не пришлось долго путешествовать и самому ее искать.
Есть четыре способа, которыми CGI передает данные между CGI-npor-раммой и веб-сервером, а следовательно, и клиентом Web:
С помощью этих четырех методов сервер пересылает все данные, переданные клиентом, CGI-программе. Затем CGI-программа делает свое волшебное дело и пересылает выходные данные обратно серверу, который переправляет их клиенту.
Эти данные приводятся с прикидкой на сервер HTTP Apache. Apache - наиболее распространенный веб-сервер, работающий практически на любой платформе, включая Windows 9х и Windows NT. Однако они могут быть применимы ко всем HTTP-серверам, поддерживающим CGI. Некоторые патентованные серверы, например, от Microsoft и Netscape, могут иметь дополнительные функции или работать несколько иначе. Поскольку лицо Web продолжает изменяться с невероятной скоростью, стандарты все еще развиваются, и в будущем, несомненно, произойдут изменения. Однако, что касается CGI, то эта технология представляется устоявшейся - расплачиваться за это приходится тем, что другие технологии, такие как апплеты, ее потеснили. Все CGI-программы, которые вы напишете, используя эти сведения, почти наверное смогут работать еще долгие годы на большинстве веб-серверов.
Когда CGI-программа вызывается посредством формы — наиболее распространенного интерфейса, броузер передает серверу длинную строку, в начале которой стоит путь к CGI-программе и ее имя. Затем следуют различные другие данные, которые называются информацией пути и передаются CGI-программе через переменную окружения PATH_INFO (рис. 9-1). После информации пути следует символ «?», а за ним - данные формы, которые посылаются серверу с помощью метода HTTP GET. Эти данные становятся доступными CGI-программе через переменную окружения QUERY_STRING . Любые данные, которые страница посылает с использованием метода HTTP POST, который используется чаще всего, будут переданы CGI-программе через стандартное устройство ввода. Типичная строка, которую может получить сервер от броузера, показана на рис. 9-1. Программа с именем formread в каталоге cgi-bin вызывается сервером с дополнительной информацией пути extra/information и данными запроса choice=help - по-видимому, как часть исходного URL. Наконец, данные самой формы (текст «CGI programming» в поле «keywords») пересылаются через метод HTTP POST .
Рис. 9-1. Части строки, переданной броузером серверу
Переменные окружения
Когда сервер выполняет CGI-программу, то прежде всего передает ей некоторые данные для работы в виде переменных окружения. В спецификации официально определены семнадцать переменных, но неофициально используется значительно больше - с помощью описываемого ниже механизма, называемого HTTP_/nec/zams/n. CGI-программа
имеет доступ к этим переменным так же, как и к любым переменным среды командного процессора при запуске из командной строки. В сценарии командного процессора, например, к переменной окружения F00 можно обращаться как $F00; в Perl это обращение выглядит, как $ENV{'F00'} ; в С - getenv("F00") ; и т. д. В таблице 9-1 перечислены переменные, которые всегда устанавливаются сервером - хотя бы и в значение null. Помимо этих переменных данные, возвращаемые клиентом в заголовке запроса, присваиваются переменным вида HTTP_F00 , где F00 - имя заголовка. Например, большинство веб-броузеров включает данные о версии в заголовок с именем USEfl_AGENT . Ваша CGI-npor-рамма может получить эти данные из переменной HTTP_USER_AGENT .
Таблица 9-1. Переменные окружения CGI
Переменная окружения |
Описание |
||
CONTENT_LENGTH |
Длина данных, переданных методами POST или PUT, в байтах. |
||
CONTENT_TYPE |
Тип MIME данных, присоединенных с помощью методов POST или PUT . |
||
GATEWAY_INTERFACE |
Номер версии спецификации CGI, поддерживаемой сервером. |
||
PATH_INFO |
Дополнительная информация пути, переданная клиентом. Например, для запроса http://www.myserver.eom/test.cgi/this/is/a/ path?field=green значением переменной РАТН_ INFO будет /this/is/a/path. |
||
PATH_TRANSLATED |
То же, что PATH_INFO , но сервер производит всю |
||
возможную трансляцию, например, расширение имен типа «-account». » |
|||
QUERY_STRING |
Все данные, следующие за символом «?» в URL. Это также данные, передаваемые, когда REQ-UEST_METHOD формы есть GET. |
||
REMOTE_ADDR |
IP-адрес клиента, делающего запрос. |
||
REMOTE_HOST |
Имя узла машины клиента, если оно доступно. |
||
REMOTE_IDENT |
Если веб-сервер и клиент поддерживают идентификацию типа identd, то это имя пользователя учетной записи, которая делает запрос. |
||
REQUEST_METHOD |
Метод, используемый клиентом для запроса. Для CGI-программ, которые мы собираемся создавать, это обычно будет POST или GET. |
||
SERVER_NAME | Имя узла - или IP-адрес, если имя недоступно, -машины, на которой выполняется веб-сервер. | ||
SERVER_PORT | Номер порта, используемого веб-сервером. | ||
SERVER_PROTOCOL |
Протокол, используемый клиентом для связи с сервером. В нашем случае этот протокол почти всегда HTTP. | ||
SERVER_SOFTWARE | Данные о версии веб-сервера, выполняющего CGI-программу. | ||
SCRIPT_NAME |
Путь к выполняемому сценарию, указанный клиентом. Может использоваться при ссылке URL на самого себя, и для того, чтобы сценарии, ссылки на которые существуют в разных местах, могли выполняться по-разному в зависимости от места. |
||
Приведем пример сценария CGI на Perl, который выводит все переменные окружения, установленные сервером, а также все унаследованные переменные, такие как PATH, установленные командным процессором, запустившим сервер.
#!/usr/bin/perl -w
print << HTML;
Content-type: text/html\n\n
<HTML><HEAD><TITLE></title></head>
<BODY> <р>Переменные окружения
<P> HTML
foreach (keys %ENV) { print "$_: $ENV{$_}<br>\n"; }
print <<HTML; </body></html>
HTML
Все эти переменные могут быть использованы и даже изменены вашей CGI-программой. Однако эти изменения не затрагивают веб-сервер, запустивший программу.
Командная строка
CGI допускает передачу CGI-программе аргументов в качестве параметров командной строки, которая редко используется. Редко используется она потому, что практические применения ее немногочисленны, и мы не будем останавливаться на ней подробно. Суть в том, что если переменная окружения QUERY_STRING не содержит символа « = », то CGI-программа будет выполняться с параметрами командной строки, взятыми из QUERY_STRING . Например, http://www.myserver.com/cgi-bin/finger?root запустит finger root на www.myserver.com.
Есть две основные библиотеки, обеспечивающие CGI-интерфейс для Perl. Первая из них - cgi-lib.pl Утилита cgi-lib.pl очень распространена, поскольку в течение долгого времени была единственной имеющейся большой библиотекой. Она предназначена для работы в Perl 4, но работает и с Perl 5. Вторая библиотека, CGI.pm, более новая и во многом превосходит cgi-lib.pl. CGI.pm написана для Perl 5 и использует полностью объектно-ориентированную схему для работы с данными CGI. Модуль CGI.pm анализирует стандартное устройство ввода и переменную QUERY_STRING и сохраняет данные в объекте CGI. Ваша программа должна лишь создать новый объект CGI и использовать простые методы, такие как paramQ, для извлечения нужных вам данных. Пример 9-2 служит короткой демонстрацией того, как CGI.pm интерпретирует данные. Все примеры на Perl в этой главе будут использовать CGI.pm.
Пример 9-2. Синтаксический анализ CGI-данных на Perl
#!/usr/bin/perl -w
use CGI qw(:standard);
# Используется модуль CGI.pm. qw(:standard) импортирует
# пространство имен стандартных CGI-функций,чтобы получить
# более понятный код. Это можно делать, если в сценарии
# используется только один объект CGI.
$mycgi = new CGI; #Создать объект CGI, который будет 'шлюзом' к данным формы
@fields = $mycgi->param; # Извлечь имена всех заполненных полей формы
print header, start_html('CGI.pm test'); ft Методы 'header' и 'start_html',
# предоставляемые
# CGI.pm, упрощают получение HTML.
# 'header' выводит требуемый заголовок HTTP, a
#'start_html' выводит заголовок HTML с данным названием,
#a также тег <BODY>.
print "<р>Данные формы:<br>";
foreach (@fields) { print $_, ":",- $mycgi->param($_), "<br>"; }
# Для каждого поля вывести имя и значение, получаемое с помощью
# $mycgi->param('fieldname').
print end_html; # Сокращение для вывода завершающих тегов "</body></html>".
Обработка входных данных в С
Поскольку основные API для MySQL и mSQL написаны на С, мы не будем полностью отказываться от С в пользу Perl, но там, где это уместно, приведем несколько примеров на С. Есть три широко используемые С-библиотеки для CGI-программирования: cgic Тома Бу-телла (Tom Boutell)*; cgihtml Юджина Кима (Eugene Kim)t и libcgi от EIT*. Мы полагаем, что cgic является наиболее полной и простой в использовании. В ней, однако, недостает возможности перечисления всех переменных формы, когда они не известны вам заранее. На самом деле, ее можно добавить путем простого патча, но это выходит за рамки данной главы. Поэтому в примере 9-3 мы используем библиотеку cgihtml, чтобы повторить на С приведенный выше сценарий Perl.
Пример 9-3. Синтаксический анализ CGI-данных на С
/* cgihtmltest.c - Типовая CGI-программа для вывода ключей и их значений
из данных, полученных от формы */
#include <stdio.h>
#include "cgi-lib.h" /* Здесь содержатся все определения функций СGI */
#include "html-lib.h" /* Здесь содержатся' все определения вспомогательных функций для HTML */
void print_all(llist 1)
/* Эти функции выводят данные, переданные формой, в том же формате, что и приведенный выше сценарий Perl. Cgihtml предоставляет также встроенную функцию
print_entries(), которая делает то же самое, используя формат списка HTML. */ {
node* window;
/* Тип 'node' определен в библиотеке cgihtml и ссылается на связанный список, в котором хранятся все данные формы. */
window = I.head; /* Устанавливает указатель на начало данных формы */
while (window != NULL) { /* Пройти по связанному списку до последнего (первого пустого) элемента */
printf(" %s:%s<br>\n",window->entry. name,replace_ltgt(window->entry.value));
/* Вывести данные. Replace__ltgt() - функция, понимающая HTML-кодировку текста и обеспечивающая его правильный вывод на броузер клиента. */
window = window->next; /* Перейти к следующему элементу списка. */
} }
int main() {
llist entries; /* Указатель на проанализированные данные*/
int status; /* Целое число, представляющее статус */
html__header(); /* Вспомогательная функция HTML, выводящая заголовок HTML*/
html_begin("cgihtml test");
/* Вспомогательная функция HTML, выводящая начало страницы HTML с указанным заголовком. */
status = read_cgi_input(&entries); /* Производит ввод и синтаксический анализ данных формы*/
printf("<р>Данные формы:<br>");
print_all(entries); /* Вызывает определенную выше функцию print_all(). */
html_end(); /* Вспомогательная функция HTML, выводящая конец страницы HTML. */
list_clear(&entries); /* Освобождает память, занятую данными формы. */
return 0; }
Стандартное устройство вывода
Данные, посылаемые CGI-программой на стандартное устройство вывода, читаются веб-сервером и отправляются клиенту. Если имя сценария начинается с nph-, то данные посылаются прямо клиенту без вмешательства со стороны веб-сервера. В этом случае CGI-программа должна сформировать правильный заголовок HTTP, который будет понятен клиенту. В противном случае предоставьте веб-серверу сформировать HTTP-заголовок за вас.
Даже если вы не используете nph-сценарий, серверу нужно дать одну директиву, которая сообщит ему сведения о вашей выдаче. Обычно это HTTP-заголовок Content-Type , но может быть и заголовок Location . За заголовком должна следовать пустая строка, то есть перевод строки или комбинация CR/LF.
Заголовок Content-Type сообщает серверу, какого типа данные выдает ваша CGI-программа. Если это страница HTML, то строка должна быть Content-Type: text/html. Заголовок Location сообщает серверу другой URL - или другой путь на том же сервере, - куда нужно направить клиента. Заголовок должен иметь следующий вид: Location: http:// www. myserver. com/another/place/.
После заголовков HTTP и пустой строки можно посылать собственно данные, выдаваемые вашей программой, - страницу HTML, изображение, текст или что-либо еще. Среди CGI-программ, поставляемых с сервером Apache, есть nph-test-cgi и test-cgi, которые хорошо демонстрируют разницу между заголовками в стилях nph и не-nph, соответственно.
В этом разделе мы будем использовать библиотеки CGI.pm и cgic, в которых есть функции для вывода заголовков как HTTP, так и HTML. Это позволит вам сосредоточиться на выводе собственно содержания. Эти вспомогательные функции использованы в примерах, приведенных ранее в этой главе.