1.3 CGI изнутри

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

Передача документа пользователю
Вначале рассмотрим более простой вопрос: как программа посылает свой ответ (т. е. документ) пользователю.
Сделано это просто и логично (а главное, универсально и переносимо между операционными системами): сценарий просто помещает документ в стандартный поток вывода (в языке C он называется stdout), который находится под контролем программного обеспечения сервера. Иными словами, программа работает так, как будто нет никакого пользователя, а нужно вывести текст прямо на экран. Это она так “думает”, на самом деле выводимая информация будет перенаправлена сервером в браузер пользователя. Ясно, что у сценария никакого “экрана” нет и быть не может.

ЗАМЕЧАНИЕ
FastCGI взаимодействует с клиентом через сокеты или TCP/IP-соединение.

Ответ программы, как и запрос пользователя, должен состоять из заголовков. Иными словами, мы не можем просто направить документ в стандартный поток вывода: нам сначала нужно, по крайней мере, указать, в каком формате информация должна быть передана пользователю. Действительно, представьте, что произойдет, если браузер попытается отобразить GIF-рисунок в текстовом виде? В худшем случае вашим пользователям придется всю жизнь лечиться от заикания — особенно если до этого их просили ввести номер кредитной карточки…

Заголовки ответа
Заголовки ответа должны следовать точно в таком же формате, как и заголовки запроса, рассмотренные нами в предыдущей главе. А именно, это набор строк (завершающийся пустой строкой), каждая из которых представляет собой имя заголовка и его значение, разделенные двоеточием. Наличие пустого заголовка в конце так же можно
интерпретировать, как два стоящих подряд символа перевода строки \n\n. Затем, как обычно, могут следовать данные ответа, являющиеся документом, который будет отображен браузером.

Заголовок кода ответа
Здесь все же имеется одно отличие от формата, который используется в заголовках запроса. Дело в том, что первый заголовок ответа обязан иметь слегка специфичный вид — в нем не должно быть двоеточия. Он задает так называемый код ответа сервера и выглядит, например, так:
HTTP/1.1 OK
или так:
HTTP/1.1 404 Not Found
В первом примере заголовок говорит браузеру, что все в порядке и дальше следует некоторый документ. Во втором примере сообщается, что затребованный файл не был найден на сервере. Конечно, существует еще множество других кодов ошибок.

Content-type
Формат:
Content-type: mime_тип; charset=utf-8
Задает тип документа и его кодировку. Параметр charset указывает кодировку документа (в нашем примере это UTF-8). Поле mime_тип определяет тип информации, которую содержит документ:
 text/html — HTML-документ;
 text/plain — простой текстовый файл;
 image/gif — GIF-изображение;
 image/jpeg — JPG-изображение;
еще несколько десятков других типов.

Pragma
Формат:
Pragma: no-cache
Запрещает кэширование документа браузером, так что при повторном визите на страницу браузер гарантированно загрузит ее снова, а не извлечет из своего кэша. Это может быть полезно, если страница содержит, например, динамический счетчик посещений. Заголовок Pragma используется так же и для других целей (и соответственно, после двоеточия находятся иные значения строки), но мы не будем их здесь рассматривать.

Location
Формат:
Location: http://www.otherhost.com/somepage.html
Этот заголовок особенный и определяет, что браузер пользователя должен немедленно перейти по указанному адресу, не дожидаясь тела документа ответа (как будто бы пользователь сам набрал в адресной строке нужный URL). Так что если вы собираетесь использовать заголовок Location, то за ним не должен следовать документ.

Set-cookie
Формат:
Set-cookie: параметры_cookie
Устанавливает cookie в браузер пользователя. В конце этой главы (см. разд. “Что такое cookies и „с чем их едят“?”) мы рассмотрим подробнее, что такое cookies и как с ними работать.

Date
Формат:
Date: Sat, 08 Jan 2000 11:56:26 GMT
Указывает браузеру дату отправки документа.

Примеры CGI-сценариев на C
Настало время привести небольшой сценарий на C, иллюстрирующий некоторые возможности, которые были описаны выше:

#include <time.h> // Директива нужна для инициализации функции rand()
#include <stdio.h> // Включаем поддержку функций ввода/вывода
#include <stdlib.h> // А это — для поддержки функции rand()

// Главная функция. Именно она и запускается при старте сценария.
int main(void) {
// инициализируем генератор случайных чисел
int num;
type: text/html\n”);
// запрет кэширования данных браузером
printf(“Pragma: no-cache\n”);
// пустой заголовок
printf(“\n”);
// выводим текст документа — его мы увидим в браузере
printf(“<!DOCTYPE html>”);
printf(“<html lang=’ru’>”);
printf(“<head>”);
printf(“<title>Случайное число</title>”);
printf(“<meta charset=’utf-8′>”);
printf(“</head>”);
printf(“<body>”);
printf(“<h1>Здравствуйте!</h1>”);
printf(“<p>Случайное число в диапазоне 0-9: %d</p>”, num);
printf(“</body>”);
printf(“</html>”);
}

Исходный текст можно откомпилировать и поместить в каталог с CGI-сценариями на сервере. Обычно стараются все сценарии хранить в одном месте — в каталоге cgi-bin, у которого имеется разрешение на выполнение всех файлов внутри него. Правда, это правило не является обязательным — конечно же, можно разместить файлы сценария где душе угодно (не забыв проставить соответствующие права на каталог в настройках сервера). На наш взгляд, логично хранить файлы сценариев там, где это наиболее вам удобно, а не пользоваться общепринятыми штампами. Теперь наберем в адресной строке браузера:
http://www.myhost.com/cgi-bin/script.cgi
Мы получим нашу HTML-страницу. Заметьте, что при нажатии кнопки Reload (а также при повторном посещении страницы) браузер перезагрузит страницу целиком, а не возьмет ее копию из своего кэша (это можно видеть по постоянно изменяющемуся случайному числу или по лампочкам модема). Мы добились такого результата благодаря заголовку
Pragma: no-cache

 

Залишити відповідь