Общий шлюзовый интерфейс (CGI)
Common Gateway Interface (CGI, рус. Общий шлюзовый интерфейс) — это стандартный метод динамического управления содержимым веб-страниц с помощью специальных программ, выполняющихся на стороне веб-сервера. Эти программы называются CGI-обработчики или шлюзы, но чаще — CGI-скрипты, т.к. обычно они пишутся на скриптовых языках, в основном на Perl.
Поскольку гипертекст статичен по своей природе, веб-страница не может непосредственно взаимодействовать с пользователем. До появления JavaScript, не было иной возможности отреагировать на действия пользователя, кроме как передать введенные им данные на веб-сервер для дальнейшей обработки. В случае CGI эта обработка осуществляется с помощью внешних программ и скриптов, обращение к которым выполняется через стандартизованный (см. RFC 3875: CGI Version 1.1) интерфейс — общий шлюз. Упрощенная модель, иллюстрирующая работу CGI, приведена на рис. 1.
Как работает CGI?
Обобщенный алгоритм работы через CGI можно представить в следующем виде:
- Клиент запрашивает CGI-приложение по его URI.
- Веб-сервер принимает запрос и устанавливает переменные окружения, через них приложению передаются данные и служебная информация.
- Веб-сервер перенаправляет запросы через стандартный поток ввода (stdin) на вход вызываемой программы.
- CGI-приложение выполняет все необходимые операции и формирует результаты в виде HTML.
- Сформированный гипертекст возвращается веб-серверу через стандартный поток вывода (stdout). Сообщения об ошибках передаются через stderr.
- Веб-сервер передает результаты запроса клиенту.
Области применения 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_ADDR | IP-адрес клиента, выполняющего запрос. |
REMOTE_HOST | Полное имя (FQDN) клиента. (Если доступно) |
REQUEST_METHOD | Метод, которым выполняется запрос. Чаще всего GET или POST. |
SCRIPT_FILENAME | Полный путь к запрашиваемому скрипту (в файловой системе сервера). |
SCRIPT_NAME | Имя скрипта |
SERVER_NAME | Имя сервера |
SERVER_ADDR | IP-адрес сервера |
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;
Передача данных: метод 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