Программирование с использованием 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.
В этом коде строка #!/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.
Метод GET используется для передачи urlencoded -данных через строку запроса. Адрес запрашиваемого ресурса (CGI-скрипта) и передаваемые ему данные отделяются знаком «?». Пример такого адреса:
Метод GET используется по умолчанию для данных, введенных в адресную строку браузера. Такая же строка может быть сформирована при отправке данных из веб-формы (тег
После ввода данных в форму из листинга 3 и нажатия кнопки "Submit" будет сформирована строка запроса вида:
В общем случае более подходящим для передачи информации CGI-скрипту является метод POST. Блок передаваемых данных формируется так же, как и для метода GET, но непосредственно передача осуществляется в теле запроса. Данные поступают на вход CGI-приложения через стандартный ввод (stdin).
Для отправки данных этим методом, он должен быть явно задан в описании формы (action="POST").
Для обработки входных данных CGI-скрипт должен прочитать stdin, а чтобы это правильно сделать, ему нужно узнать размер сообщения из переменной CONTENT_LENGTH. Для иллюстрации этого модифицируем блок анализа окружения в листинге 4, заменив его следующим кодом:
Дальнейшая обработка сохраненных в переменной $buffer параметров и их значений выполняется так же, как и в при использовании метода GET.
Преимущества CGI
Многие возможности CGI сейчас дублируются такими технологиями, как например DHTML , ActiveX или Java-апплетами. Основными преимуществами использования серверных скриптов является то, что вы можете быть уверены, что все клиенты (за редким исключением, как правило связанным с блокировкой доступа к определенным ресурсам на уровне файрвола) смогут работать с серверным приложением. Клиентские-же программы могут быть просто отключены в браузере, или вовсе не поддерживаться.
Недостатки CGI
Самым большим недостатком этой технологии являются повышенные требования к производительности веб-сервера. Дело в том, что каждое обращение к CGI-приложению вызывает порождение нового процесса , со всеми вытекающими отсюда накладными расходами. Если же приложение написано с ошибками, то возможна ситуация, когда оно, например, зациклится. Браузер прервет соединение по истечении тайм-аута, но на серверной стороне процесс будет продолжаться, пока администратор не снимет его принудительно. В этом отношении клиентские скрипты имеют существенное преимущество, т.к. они выполняются локально.
Другим недостатком CGI является меньшая, по сравнению с другими решениями, защищенность веб-сервера. Неправильная настройка прав доступа к серверным ресурсам из CGI-приложения может поставить под угрозу не только работоспособность веб-сервера, но и информационную безопасность. Впрочем, любую сетевую технологию можно считать потенциально небезопасной уже по определению.
Постоянный адрес этой страницы:
Когда пользователь заполняет html-форму и нажимает кнопку submit, данные отправляются
веб-серверу. Веб-сервер, будь это Apache, IIS или какой-либо другой, запускает программу,
указанную в качестве значения атрибута action. В нашем случае это test.cgi.
Веб-сервер запускает test.cgi и передает ей параметры в виде
текстовой строки, следующего
содержания: name1=value1&name2=value2&....nameN=valueN, т.е. имя_параметра=значение.
Эта строка передается на стандартный поток
ввода (STDIN) или в качестве значения переменной окружения QUERY_STRING. Соответственно,
считать данную
строку в программе можно одним из двух способов:
В первом случае параметры передаются методом POST, а во втором методом GET.
В первом случае мы читаем
строку из STDIN. Длину строки мы узнаем из значения параметра окружения
CONTENT_LENGTH. Во втором она хранится в переменной окружения QUERY_STRING.
Значение переменной окружения можно получить, вызвав функцию getenv.
Метод, с помощью которого передается строка с параметрами CGI-программе, можно
определить следующим образом: strcmp(getenv("REQUEST_METHOD"),"POST").
Далее придется разбирать строку и получать необходимые значения параметров.
Для того чтобы не делать это каждый раз,
мы написали небольшую, но очень удобную библиотечку ITCGI для написания CGI-скриптов.
Эта библиотека позволяет вам полностью абстрагироваться от метода, которым
передаются параметры, от кодировки, от разбора строки. Вы просто вызываете
функцию GetParamByName, в которую передаете имя интересующего вас параметра
и адрес строки, куда сохранить значение. Библиотека также предоставляет вам ряд
функций для написания эффективных и защищенных от взлома CGI-скриптов.
В простейшем случае, когда ваша программа не нуждается в параметрах, вам и не
потребуется ни самому разбирать и раскодировать строку, ни использовать для
этого нашу библиотеку. Самой простой CGI-программой будет:
Заголовок является обязательной частью. Он передается веб-серверу и определяет,
что следует за ним. В большинстве случаев у вас будет именно такой заголовок.
Он говорит веб-серверу, что дальше идет HTML-код. С другими типами заголовков
мы познакомимся чуть позже. В заголовке может быть несколько
строк. Конец заголовка обозначается двумя переходами на новую строку - \n\n.
Откомпилируйте эту программу, а исполняемый файл
положите в каталог /cgi-bin вашего веб-сайта. Переименуйте его в test.cgi.
К этому скрипту можно
обратится непосредственно через обозреватель, написав в командной строке URL,
например у меня это выглядит так http://сайт/cgi-bin/test.cgi
В результате, в вашем обозревателе вы увидите строку: "Hello, World!".
Далее мы рассмотрим CGI-программу такого же типа. Она не принимает никаких
параметров, но зато выдает более полезную информацию - список и значения всех
переменных окружения. Такой скрипт вам пригодится, когда вы будете
отлаживать свои CGI-программы на различных веб-серверах. Дело в том,
что переменные окружения различаются на различных веб-серверах. Так, например,
для веб-сервера Apache, путь к каталогу веб-сайта хранится в переменной окружения
DOCUMENT_ROOT. Для веб-сервера Microsoft Internet Information Server
это значение хранится в переменной PATH_TRANSLATED.
В операционной системе UNIX скрипт для вывода всех переменных выглядит
следующим образом.
Обратите внимание на CGI-заголовок. Он отличается от того, который у нас был в предыдущем
примере. plain означает, что скрипт выдаст не HTML-код, а чистый текст. Броузер
будет воспринимать его, как обычный текст и выводить в точности как есть.
Здесь не надо заменять спецсимволы типа < на их эквиваленты <.
Скопируйте этот скрипт в директорию /cgi-bin с именем env. Установите атрибут
755 (rwxr-xr-x).
Вот результат выполнения такого скрипта на моем unix-сервере:
GATEWAY_INTERFACE=CGI/1.1
REMOTE_USER=itsoft
REMOTE_ADDR=192.168.34.134
QUERY_STRING=
REMOTE_PORT=1781
HTTP_USER_AGENT=Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt)
DOCUMENT_ROOT=/usr/local/www/itsoft
AUTH_TYPE=Basic
SERVER_SIGNATURE=Apache/1.3.12
Server at itsoft..3.12 (Unix) PHP/3.0.17
HTTP_CONNECTION=Keep-Alive
HTTP_COOKIE=/cgi-bin/authenticate.cgi_LAST=956345778
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:
/usr/local/bin:/usr/X11R6/bin
HTTP_ACCEPT_LANGUAGE=ru
SERVER_PROTOCOL=HTTP/1..226.32.34
SERVER_PORT=80
SCRIPT_NAME=/cgi-bin/web/env
SERVER_NAME=сайт
Программа на языке Си для Windows и веб-сервера Internet Information Server
будет выглядеть следующим образом:
Сначала выполняется команда command.com /c set>c:\www\mysite\temp\env.dmp.
Результатом выполнения такой команды и будет список всех переменных
окружения, который затем сохраняется в файл. Далее мы читаем этот файл
и выдаем его содержимое веб-серверу. Вы можете заметить, что
в данном случае, как и в прошлом примере, мы печатаем не html-код, а чистый текст
и поэтому у нас заголовок: Content-type: text/plain. Не забудьте также, что
этот cgi-скрипт будет работать только под Internet Information Server.
Для веб-сервера Apache следует заменить getenv("PATH_TRANSLATED") на
getenv("DOCUMENT_ROOT").
Ниже приведен результат действия этого скрипта на WindowsNT, вы можете видеть, какое
количество параметров доступно через переменные окружения. Такой cgi-скрипт
пригодится вам при настройке ваших скриптов на чужом сервере, где переменные
окружения могут отличаться от ваших локальных.
COMSPEC=C:\WINNT\SYSTEM32\COMMAND.COM
COMPUTERNAME=JUPITER
CONTENT_LENGTH=0
GATEWAY_INTERFACE=CGI/1.1
HTTP_ACCEPT=image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,
application/vnd.ms-powerpoint, application/vnd.ms-excel, applic
HTTP_ACCEPT_LANGUAGE=ru
HTTP_CONNECTION=Keep-Alive
HTTP_HOST=www.oxygensoftware.com
HTTP_USER_AGENT=Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
HTTP_ACCEPT_ENCODING=gzip, deflate
HTTPS=off
INCLUDE=C:\Program Files\Mts\Include
INSTANCE_ID=1410
LIB=C:\Program Files\Mts\Lib
LOCAL_ADDR=168.144.29.178
NUMBER_OF_PROCESSORS=2
OS2LIBPATH=C:\WINNT\system32\os2\dll;
OS=Windows_NT
PATH=C:\WINNT\system32;C:\WINNT;C:\Program Files\Mts
PATH_TRANSLATED=e:\InetPub\Clients\oxygensoftware.com
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.JS;.VBE;.JSE;.WSF;.WSH
PROCESSOR_ARCHITECTURE=x86
PROCESSOR_IDENTIFIER=x86 Family 6 Model 5 Stepping 1, GenuineIntel
PROCESSOR_LEVEL=6
PROCESSOR_REVISION=0501
PROMPT=$P$G
REMOTE_ADDR=194.226.32.34
REMOTE_HOST=194.226.32.34
REQUEST_METHOD=GET
SCRIPT_NAME=/cgi-bin/env.exe
SERVER_NAME=www.oxygensoftware.com
SERVER_PORT=80
SERVER_PORT_SECURE=0
SERVER_PROTOCOL=HTTP/1.1
SERVER_SOFTWARE=Microsoft-IIS/4.0
SYSTEMDRIVE=C:
SYSTEMROOT=C:\WINNT
TEMP=C:\temp
TMP=C:\temp
USERPROFILE=C:\WINNT\Profiles\Default User
Далее, прежде чем перейти к рассмотрению cgi-скриптов, которые принимают и
обрабатываю параметры формы, мы напишем простенькую программу, которая
выдает строку параметров html-формы. О том, как считываются параметры
формы, читайте выше, здесь я привожу исходный код программы и ее результат
для html-формы, описанной в четвертой главе.
Скомпилируйте этот код. Он платформенно независимый, поэтому можете
скомпилировать как под Unix, так и под Windows.
Из четвертой главы возьмите HTML-форму, можете взять и любую другую.
В поле action пропишите путь к данной программе на вашем веб-сервере.
Результат после нажатия на кнопку "Опубликовать":
text=zero&text=zero&list=0&list2=0&textarea=%C7%E4%E5%F1%FC+%F2%E5%EA%F1%F2+%EF%EE+%F3%EC%EE%EB%F7%E0%ED%E8%FE
Это строка, которая содержит список всех параметров
Далее, вы можете воспользоваться нашей библиотекой ITCGI или же самим разбирать
эту строку параметров. О библиотеке и ее практическом использовании читайте
в следующей параграфе.
Глава
№9
.
Программирование с использованием CGI
Включение раздела о CGI в книгу по базам данных может показаться столь же
странным, как если бы в кулинарную книгу была включена глава о ремонте
автомобилей. Разумеется, для того чтобы съездить в магазин за продуктами,
нужен исправный автомобиль, но уместно ли об этом говорить? Полное изложение
CGI и веб-программирование в целом выходят за рамки данной книги, но
краткого введения в эти темы достаточно для того, чтобы расширить
возможности MySQL и mSQL по представлению данных в царстве Web.
В
основном эта глава предназначена тем, кто изучает базы данных, но не прочь
приобрести некоторые знания и в программировании для Web. Если ваша фамилия
Бернерс-Ли или Андрессен, вряд ли вы найдете здесь то, чего еще не знаете.
Но даже если вы не новичок в CGI, наличие под рукой краткого справочника во
время погружения в тайны MySQL и mSQL может оказаться весьма полезным.
Что
такое CGI?
Как и
большинство акронимов, Common Gateway Interface (CGI - общий шлюзовый
интерфейс) мало что говорит по сути. Интерфейс с чем? Где этот шлюз? О какой
общности речь? Чтобы ответить на эти вопросы, вернемся немного назад и
бросим взгляд на WWW в целом.
Тим
Бернерс-Ли, физик, работавший в CERN, придумал Web в 1990 году, хотя план
возник еще в 1988. Идея состояла в том, чтобы дать исследователям в области
физики элементарных частиц возможность легко и быстро обмениваться
мультимедийными данными - текстом, изображениями и звуком - через Интернет.
WWW состояла из трех основных частей: HTML, URL и HTTP. HTML -
язык
форматирования, используемый для представления содержания в Web. URL -
это адрес, используемый для получения содержимого в формате HTML (или
каком-либо ином) с веб-сервера. И, наконец, HTTP -
это язык, который
понятен веб-серверу и позволяет клиентам запрашивать у сервера документы.
Возможность пересылки через Интернет информации всех типов явилась
революцией, но вскоре была обнаружена и другая возможность. Если можно
переслать через Web любой текст, то почему нельзя переслать текст, созданный
программой, а не взятый из готового файла? При этом открывается море
возможностей. Простой пример: можно использовать программу, выводящую
текущее время, так, чтобы читатель видел правильное время при каждом
просмотре страницы. Несколько умных голов в National Center for
Supercomputing Applications (Национальный центр разработки приложений для
суперкомпьютеров -NCSA), которые создавали веб-сервер, такую возможность
увидели, и вскоре появился CGI.
CGI -
это набор правил, согласно которым программы на сервере могут через
веб-сервер посылать данные клиентам. Спецификация CGI сопровождалась
изменениями в HTML и HTTP, вводившими новую характеристику, известную как
формы.
Если CGI
позволяет программам посылать данные клиенту, то формы расширяют эту
возможность, позволяя клиенту посылать данные для этой CGI-программы. Теперь
пользователь может не только видеть текущее время, но и устанавливать часы!
Формы CGI открыли дверь для подлинной интерактивности в мире Web.
Распространенные приложения CGI включают в себя:
Динамический
HTML. Целые сайты могут генерироваться одной CGI-программой.
Поисковые
механизмы, находящие документы с заданными пользователем словами.
Гостевые книги и
доски объявлений, в которые пользователи могут добавлять свои сообщения.
Бланки заказов.
Анкеты.
Извлечение
информации из размещенной на сервере базы данных.
В
последующих главах мы обсудим все эти CGI-приложения, а также и некоторые
другие. Все они дают прекрасную возможность соединения CGI с базой данных,
что и интересует нас в этом разделе.
Формы HTML
Прежде
чем изучать особенности CGI, полезно рассмотреть наиболее часто
встречающийся способ, с помощью которого конечные пользователи получают
интерфейс к CGI-программам: формы HTML. Формы являются частью языка HTML,
предоставляющей конечному пользователю поля различных типов. Данные,
вводимые в поля, могут быть пересланы веб-серверу. Поля могут служить для
ввода текста или являться кнопками, которые пользователь может нажать или
отметить галочкой. Вот пример страницы HTML, содержащей форму:
<НТМL><НЕАD><ТITLЕ>Моя страница с формами
<р>Это
страница с формой.
Данная
форма создает строку длиной 40 символов, куда пользователь может ввести свое
имя. Под строкой для ввода располагается кнопка, при нажатии которой данные
формы передаются серверу. Ниже перечислены относящиеся к формам теги,
поддерживаемые HTML 3.2 -наиболее распространенным в настоящее время
стандартом. Названия тегов и атрибутов могут вводиться в любом регистре, но
мы придерживаемся необязательного соглашения, согласно которому открывающие
теги пишутся в верхнем регистре, а закрывающие - в нижнем.
.
Между тегами
Единственный тип ввода, который мы здесь не использовали, - это тип IMAGE
для тега . Можно было бы использовать его в качестве альтернативного
способа отправки формы. Однако тип IMAGE редко совместим с текстовыми и не
очень чуткими броузерами, поэтому благоразумно избегать его, если только ваш
сайт не выполнен в насыщенном графическом стиле.
После
знакомства с основами форм HTML можно приступить к изучению собственно CGI.
Спецификация 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 .
Переменные окружения
Когда
сервер выполняет 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
foreach
(keys %ENV) { print "$_: $ENV{$_} \n"; }
print
<
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 с данным названием,
# Для
каждого поля вывести имя и значение, получаемое с помощью
#
$mycgi->param("fieldname").
print
end_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
#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) { /* Пройти по связанному списку до последнего (первого
пустого) элемента */
Данные,
посылаемые 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. Это
позволит вам сосредоточиться на выводе собственно содержания. Эти
вспомогательные функции использованы в примерах, приведенных ранее в этой
главе.
Важные особенности сценариев CGI
Вы уже
знаете, в основном, как работает CGI. Клиент посылает данные, обычно с
помощью формы, веб-серверу. Сервер выполняет CGI-программу, передавая ей
данные. CGI-программа осуществляет свою обработку и возвращает свои выходные
данные серверу, который передает их клиенту. Теперь от понимания того, как
работают CGI-npor-раммы, нужно перейти к пониманию того, почему они так
широко используются.
Хотя вам
уже достаточно известно из этой главы, чтобы собрать простую работающую
CGI-программу, нужно разобрать еще несколько важных вопросов, прежде чем
создавать реально работающие программы для MySQL или mSQL. Во-первых, нужно
научиться работать с несколькими формами. Затем нужно освоить некоторые меры
безопасности, которые помешают злоумышленникам получить незаконный доступ к
файлам вашего сервера или уничтожить их.
Запоминание состояния
Запоминание состояния является жизненно важным средством предоставления
хорошего обслуживания вашим пользователям, а не только служит для борьбы с
закоренелыми преступниками, как может показаться. Проблема вызвана тем, что
HTTP является так называемым протоколом «без памяти». Это значит, что клиент
посылает данные серверу, сервер возвращает данные клиенту, и дальше каждый
идет своей дорогой. Сервер не сохраняет о клиенте данных, которые могут
понадобиться в последующих операциях. Аналогично, нет уверенности, что
клиент сохранит о совершенной операции какие-либо данные, которые можно
будет использовать позднее. Это накладывает непосредственное и существенное
ограничение на использование World Wide Web.
Составление сценариев CGI при таком протоколе аналогично неспособности
запоминать разговор. Всякий раз, разговаривая с кем-либо, независимо от
того, как часто вы общались с ним раньше, вам приходится представляться и
искать общую тему для разговора. Нет нужды объяснять, что это не
способствует продуктивности. Рисунок 9-2 показывает, что всякий раз, когда
запрос достигает программы CGI, это совершенно новый экземпляр программы, не
имеющий связи с предыдущим.
В части
клиента с появлением Netscape Navigator появилось выглядящее наспех
сделанным решение под названием cookies. Оно состоит в создании нового
HTTP-заголовка, который можно пересылать туда-сюда между клиентом и
сервером, похожего на заголовки Content-Type и Location. Броузер клиента,
получив заголовок cookie, должен сохранить в cookie данные, а также имя
домена, в котором действует этот cookie. После этого всякий раз при
посещении URL в пределах указанного домена заголовок cookie должен
возвращаться серверу для использования в CGI-программах на этом сервере.
Метод
cookie используется в основном для хранения идентификатора пользователя.
Сведения о посетителе можно сохранить в файле на машине сервера. Уникальный
ID этого пользователя можно послать в качестве cookie броузеру пользователя,
после чего при каждом посещении сайта пользователем броузер автоматически
посылает серверу этот ID. Сервер передает ID программе CGI, которая
открывает соответствующий файл и получает доступ ко всем данным о
пользователе. Все это происходит незаметным для пользователя образом.
Несмотря
на всю полезность этого метода, большинство больших сайтов не использует его
в качестве единственного средства запоминания состояния. Для этого есть ряд
причин. Во-первых, не все броузеры поддерживают cookie. До недавнего времени
основной броузер для людей с недостаточным зрением (не говоря уже о людях с
недостаточной скоростью подключения к сети) - Lynx - не поддерживал cookie.
«Официально» он до сих пор их не поддерживает, хотя это делают некоторые его
широко доступные «боковые ветви». Во-вторых, что более важно, cookie
привязывают пользователя к определенной машине. Одним из великих достоинств
Web является то, что она доступна из любой точки света. Независимо от того,
где была создана или где хранится ваша веб-страница, ее можно показать с
любой подключенной к Интернет машины. Однако если вы попытаетесь получить
доступ к поддерживающему cookie сайту с чужой машины, все ваши персональные
данные, поддерживавшиеся с помощью cookie, будут утрачены.
Многие
сайты по-прежнему используют cookie для персонализации страниц
пользователей, но большинство дополняет их традиционным интерфейсом в стиле
«имя регистрации/пароль». Если доступ к сайту осуществляется из броузера, не
поддерживающего cookie, то страница содержит форму, в которую пользователь
вводит имя регистрации и пароль, присвоенные ему при первом посещении сайта.
Обычно эта форма маленькая и скромная, чтобы не отпугивать большинство
пользователей, не заинтересованных ни в какой персонализации, а просто
желающих пройти дальше. После ввода пользователем в форму имени регистрации
и пароля CGI находит файл с данными об этом пользователе, как если бы имя
посылалось с cookie. Используя этот метод, пользователь может
регистрироваться на персонализированном веб-сайте из любой точки света.
Помимо
задач учета предпочтений пользователя и длительного хранения сведений о нем
можно привести более тонкий пример запоминания состояния, который дают
популярные поисковые машины. Осуществляя поиск с помощью таких служб, как
AltaVista или Yahoo, вы обычно получаете значительно больше результатов, чем
можно отобразить в удобном для чтения виде. Эта проблема решается тем, что
показывается небольшое количество результатов - обычно 10 или 20 - и дается
какое-либо средство перемещения для просмотра следующей группы результатов.
Хотя обычному путешественнику по Web такое поведение кажется обычным и
ожидаемым, действительная его реализация нетривиальна и требует запоминания
состояния.
Когда
пользователь впервые делает запрос поисковому механизму, тот собирает все
результаты, возможно, ограничиваясь некоторым предустановленным предельным
количеством. Фокус состоит в том, чтобы выдавать эти результаты одновременно
в небольшом количестве, запомнив при этом, что за пользователь запрашивал
эти результаты и какую порцию он ожидает следующей. Оставляя в стороне
сложности самого поискового механизма, мы встаем перед проблемой
последовательного предоставления пользователю некоторой информации по одной
странице. Рассмотрим пример 9-4, в котором показан сценарий CGI, выводящий
десять строк файла и предоставляющий ему возможность просмотреть следующие
или предыдущие десять строк.
Пример 9-4.
Сохранение состояния в сценарии CGI
#!/usr/bin/perl -w
use CGI;
Open(F,"/usr/dict/words") or die("He могу открыть! $!");
#Это файл,
который будет выводиться, может быть любым.
$output =
new CGI;
sub
print_range { # Это главная функция программы, my $start = shift;
# Начальная
строка файла, my $count = 0;
#
Указатель, my $line = "";
# Текущая
строка файла, print $output->header,
$output->start_html("Moй словарь");
#
Создает HTML с заголовком "Мой словарь", print "\n";
while
(($count < $start) and ($line = )) { $count++; }
#
Пропустить все строки перед начальной, while (($count < $start+10) and
($line ? )) { print $line; $count++; }
#
Напечатать очередные 10 строк.
my $newnext
= $start+10; my $newprev = $start-10;
#
Установить начальные строки для URL "Next" и "Previous",
print
"
";
unless
($start == 0) { # Включить URL "Previous", если только вы
# уже не в
начале.
print qq%Previous%; }
unless
(eof) { # Включить URL "Next", если только вы #не в конце файла.
print qq%
Next%;
}
print
«HTML; HTML
exit(0); }
# Если
данных нет, начать сначала,
if (not
$output->param) {
&print_range(0); }
# Иначе
начать со строки, указанной в данных.
&print_range($output->param("start"));
В этом
примере запоминание состояния производится с помощью простейшего метода.
Проблемы с сохранением данных нет, поскольку мы держим их в файле на
сервере. Нам нужно только знать, откуда начать вывод, поэтому сценарий
просто включает в URL начальную точку для следующей или предыдущей группы
строк - все, что необходимо для генерации очередной страницы.
Однако
если вам требуется нечто большее, чем возможность просто листать "файл, то
полагаться на URL бывает обременительно. Облегчить эту трудность можно через
использование формы HTML и включение данных о состоянии в теги типа
HIDDEN. Этот метод с успехом используется на многих сайтах, позволяя делать
ссылки между взаимосвязанными CGI-программами или расширяя возможности
использования одной CGI-программы, как в предыдущем примере. Вместо ссылки
на определенный объект, такой как начальная страница, данные URL могут
указывать на автоматически генерируемый ID пользователя.
Так
работают AltaVista и другие поисковые машины. При первом поиске генерируется
ID пользователя, который скрыто включается в последующие URL. С этим ID
связаны один или несколько файлов, содержащих результаты запроса. В URL
включаются еще две величины: текущее положение в файле результатов и
направление, в котором вы хотите перемещаться в нем дальше. Эти три значения
- все, что нужно для работы мощных систем навигации больших поисковых машин.
Впрочем, не хватает еще кое-чего. Использовавшийся в нашем примере файл
/usr/diet/words
очень велик. Что если на середине чтения мы его бросим,
но захотим вернуться к нему позднее? Если не запомнить URL следующей
страницы, никакого способа вернуться назад нет, даже AltaVista это не
позволит. Если вы перезагрузите свой компьютер или станете работать с
другого, невозможно вернуться к результатам прежнего поиска, не вводя заново
запрос. Однако такое долговременное запоминание состояния лежит в основе
персонализации вебсайтов, о которой мы говорили выше, и стоит посмотреть,
как им можно воспользоваться. Пример 9-5 является модифицированным вариантом
примера 9-4.
Пример 9-5.Устойчивое запоминание состояния
#!/usr/bin/perl -w
use CGI;
umask 0;
Open(F,"/usr/dict/words") or die("He могу открыть! $!");
Chdir("users") or die("He могу перейти в каталог $!");