Учебно-методические материалы для студентов кафедры АСОИУ

Общий шлюзовый интерфейс (CGI)

Common Gateway Interface (CGI, рус. Общий шлюзовый интерфейс) — это стандартный метод динамического управления содержимым веб-страниц с помощью специальных программ, выполняющихся на стороне веб-сервера. Эти программы называются CGI-обработчики или шлюзы, но чаще — CGI-скрипты, т.к. обычно они пишутся на скриптовых языках, в основном на Perl.

Поскольку гипертекст статичен по своей природе, веб-страница не может непосредственно взаимодействовать с пользователем. До появления JavaScript, не было иной возможности отреагировать на действия пользователя, кроме как передать введенные им данные на веб-сервер для дальнейшей обработки. В случае CGI эта обработка осуществляется с помощью внешних программ и скриптов, обращение к которым выполняется через стандартизованный (см. RFC 3875: CGI Version 1.1) интерфейс — общий шлюз. Упрощенная модель, иллюстрирующая работу CGI, приведена на рис. 1.

Common Gateway Interface

Рис. 1. Общий принцип работы CGI

Как работает CGI?

Обобщенный алгоритм работы через CGI можно представить в следующем виде:

  1. Клиент запрашивает CGI-приложение по его URI.
  2. Веб-сервер принимает запрос и устанавливает переменные окружения, через них приложению передаются данные и служебная информация.
  3. Веб-сервер перенаправляет запросы через стандартный поток ввода (stdin) на вход вызываемой программы.
  4. CGI-приложение выполняет все необходимые операции и формирует результаты в виде HTML.
  5. Сформированный гипертекст возвращается веб-серверу через стандартный поток вывода (stdout). Сообщения об ошибках передаются через stderr.
  6. Веб-сервер передает результаты запроса клиенту.

Области применения CGI

Наиболее частая задача, для решения которой применяется CGI — создание интерактивных страниц, содержание которых зависит от действий пользователя. Типичными примерами таких веб-страниц являются форма регистрации на сайте или форма для отправки комментария. Другая область применения CGI, остающаяся за кулисами взаимодействия с пользователем, связана со сбором и обработкой информации о клиенте: установка и чтение «печенюшек»-cookies; получение данных о браузере и операционной системе; подсчет количества посещений веб-страницы; мониторинг веб-трафика и т.п.

Эти возможности обеспечиваются тем, что CGI-скрипт может быть подключен к базе данных или обращаться к файловой системе сервера. Таким образом CGI-скрипт может сохранять информацию в таблицах БД или файлах и получать ее оттуда по запросу, чего нельзя сделать средствами HTML.

ОБРАТИТЕ ВНИМАНИЕ: CGI — это не язык программирования! Это простой протокол, позволяющий веб-серверу передавать данные через stdin и читать их из stdout. Поэтому, в качестве CGI-обработчика может ипользоваться любая серверная программа, способная работать со стандарными потоками ввода-вывода.

Hello, world!

Пример простого CGI-скрипта на языке Perl приведен в листинге 1. Если этот код сохранить в файле с именем hello (имя может быть любым, расширение — тоже), поместить файл в серверный каталог cgi-bin (точнее, в тот каталог веб-сервера, который предназначен для CGI-приложений и указан в настройках веб-сервера) и установить для этого файла права на исполнение (chmod uo+x hello), то он станет доступен по адресу вида http://servername/cgi-bin/hello.

Листинг 1. Пример CGI-скрипта (Perl)

#!/usr/bin/perl
print "Content-type: text/html\n\n";
print <<HTML;
<html>
<head>
<title>CGI say Hello</title>
</head>
<body>
<h1>Hello, world!</h1>
</body>
HTML
exit; 

В этом коде строка #!/usr/bin/perl указывает полный путь к интерпретатору Perl. Строка Content-type: text/html\n\n — http-заголовок, задающий тип содержимого (mime-type). Удвоенный символ разрыва строки (\n\n) — обязателен, он отделяет заголовки от тела сообщения.

Переменные окружения

Все CGI-приложения имеют доступ к переменным окружения, устанавливаемым веб-сервером. Эти переменные играют важную роль при написании CGI-программ. В таблице перечислены некоторые из переменных, доступных CGI.

Переменная окруженияОписание
CONTENT_TYPE Тип данных, передаваемых на сервер. Используется, когда клиент отправляет данные, например, загружает файл.
CONTENT_LENGTHРазмер содержимого запроса. Эта переменная определена для POST-запросов.
HTTP_COOKIEВозвращает набор «куков» в виде пар «ключ значение».
HTTP_USER_AGENTИнформация об агенте пользователя (браузере)
PATH_INFOПуть к каталогу CGI
QUERY_STRINGСтрока запроса (URL-encoded), передаваемая методом GET.
REMOTE_ADDRIP-адрес клиента, выполняющего запрос.
REMOTE_HOSTПолное имя (FQDN) клиента. (Если доступно)
REQUEST_METHODМетод, которым выполняется запрос. Чаще всего GET или POST.
SCRIPT_FILENAMEПолный путь к запрашиваемому скрипту (в файловой системе сервера).
SCRIPT_NAMEИмя скрипта
SERVER_NAMEИмя сервера
SERVER_ADDRIP-адрес сервера
SERVER_SOFTWAREИнформация о серверном ПО

В листинге 2 приведен код небольшой программы на Perl, выводящей список переменных окружения. Результат ее работы приведен на рис. 2.

Листинг 2. Переменные окружения

#!/usr/bin/perl
print "Content-type: text/html\n\n";
print "<html>\n<body>\n<h1>Environment</h1>\n";
foreach (sort keys %ENV) {
print "<b>$_</b>: $ENV{$_}<br>\n";
}
print "</body>\n</html>";
exit;

Рис. 2. Список переменных окружения

Передача данных: метод GET

Метод GET используется для передачи urlencoded-данных через строку запроса. Адрес запрашиваемого ресурса (CGI-скрипта) и передаваемые ему данные отделяются знаком «?». Пример такого адреса:

http://example.com/cgi-bin/script.cgi?key1=value1&key2=value2

Метод GET используется по умолчанию для данных, введенных в адресную строку браузера. Такая же строка может быть сформирована при отправке данных из веб-формы (тег <form>), если метод передачи для формы не указан. Вся информация, отправляемая методом GET, передается в открытом виде, поэтому никогда не следует использовать его для отправки на сервер паролей или другой подобной информации. Метод GET имеет ограничение по размеру: строка запроса должна быть не длинее 1024 символов.

Информация, отправляемая методом GET передается в заголовке QUERY_STRING в виде строки, состоящей из пар вида ключ=значение, CGI-скрипт может получить ее через одноименную переменную окружения.

Листинг 3. Отправка данных из веб-формы методом GET

<html>
<head>
<title>A simple CGI scripting: Sending data using GET-method</title>
</head>
<body>
<form action="http://example.com/cgi-bin/sayhello" method="GET">
You name: <input type="text" name="user"><br>
Where are you from?: <input type="text" name="from"><br>
<input type="submit" value="Submit">
</form>
</body>
</html>

После ввода данных в форму из листинга 3 и нажатия кнопки "Submit" будет сформирована строка запроса вида:

http://example.com/cgi-bin/sayhello?user=sometext&from=anothertext

где: sayhello — имя CGI-скрипта; user и from — имена параметров; sometext и anothertext — введенные пользователем значения соответствующих параметров.

В листинге 4 приведен пример скрипта, который может обработать данные формы из листинга 3.

Листинг 4. Отправка данных из веб-формы методом GET

#!/usr/bin/perl
local ($buffer, @pairs, $pair, $name, $value, %FORM);
# Анализируем окружение
$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/; if ($ENV{'REQUEST_METHOD'} eq "GET") {
$buffer = $ENV{'QUERY_STRING'};
}
# Разделяем строку запроса на пары вида ключ/значение @pairs = split(/&/, $buffer);
foreach $pair (@pairs) {
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%(..)/pack("C", hex($1))/eg;
$FORM{$name} = $value; # Сохраняем данные в массив
}
# Отправляем заголовок
print "Content-type: text/html\n\n";
# Отправляем гипертекст
print <<HTML;
<html>
<head><title>CGI say Hello</title></head>
<body>
<h1>Hello, $FORM{user} from $FORM{from}!</h1>
</body>
HTML
exit; 

Передача данных: метод POST

В общем случае более подходящим для передачи информации CGI-скрипту является метод POST. Блок передаваемых данных формируется так же, как и для метода GET, но непосредственно передача осуществляется в теле запроса. Данные поступают на вход CGI-приложения через стандартный ввод (stdin).

Для отправки данных этим методом, он должен быть явно задан в описании формы (action="POST").

Для обработки входных данных CGI-скрипт должен прочитать stdin, а чтобы это правильно сделать, ему нужно узнать размер сообщения из переменной CONTENT_LENGTH. Для иллюстрации этого модифицируем блок анализа окружения в листинге 4, заменив его следующим кодом:

... # Анализируем окружение
$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;
if ($ENV{'REQUEST_METHOD'} eq "POST"){
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
}
...

Дальнейшая обработка сохраненных в переменной $buffer параметров и их значений выполняется так же, как и в при использовании метода GET.

Преимущества CGI

Многие возможности CGI сейчас дублируются такими технологиями, как например DHTML, ActiveX или Java-апплетами. Основными преимуществами использования серверных скриптов является то, что вы можете быть уверены, что все клиенты (за редким исключением, как правило связанным с блокировкой доступа к определенным ресурсам на уровне файрвола) смогут работать с серверным приложением. Клиентские-же программы могут быть просто отключены в браузере, или вовсе не поддерживаться.

Недостатки CGI

Самым большим недостатком этой технологии являются повышенные требования к производительности веб-сервера. Дело в том, что каждое обращение к CGI-приложению вызывает порождение нового процесса, со всеми вытекающими отсюда накладными расходами. Если же приложение написано с ошибками, то возможна ситуация, когда оно, например, зациклится. Браузер прервет соединение по истечении тайм-аута, но на серверной стороне процесс будет продолжаться, пока администратор не снимет его принудительно. В этом отношении клиентские скрипты имеют существенное преимущество, т.к. они выполняются локально.

Другим недостатком CGI является меньшая, по сравнению с другими решениями, защищенность веб-сервера. Неправильная настройка прав доступа к серверным ресурсам из CGI-приложения может поставить под угрозу не только работоспособность веб-сервера, но и информационную безопасность. Впрочем, любую сетевую технологию можно считать потенциально небезопасной уже по определению.

CC-BY-SA Анатольев А.Г., 31.01.2012