которым закодировано тело сообщения, например,
If-Match
If-None-Match
If-Range
If-Unmodified-Since
10. Читает передаваемые программой CGI данные, формирует из них сообщение HTTP ответа и отправляет его пользовательскому агенту.
Если какие-либо из действий выполнить не удалось, то пользовательскому агенту отправляется строка с ошибочным кодом состояния и созданный процесс уничтожается.
Все современные операционные системы содержат в своем API средства для запуска дочернего процесса с данной средой окружения. В DOS – функция 4B00h прерывания 21h, в UNIX – системный вызов execve, в WIN32 – функция API CreateProcess. В любом случае, прикладная программа может получить доступ к среде окружения. Языки высокого уровня унифицируют интерфейс доступа к среде окружения для различных ОС. Например, в языках C и C++ имеются следующие переменные и функции:
#include <stdlib.h>
extern char **environ;
#include <stdlib.h>
char *getenv(char *varname);
Указатель на массив строк environ может быть использован для доступа ко всей среде окружения процесса. В массиве содержатся переменные окружения в формате "name=value", где name – имя переменной окружения, value – ее значение. Последний элемент массива содержит пустую строку в качестве признака окончания среды процесса.
Функция getenv позволяет получить значение переменной окружения, имя которой передано в качестве параметра varname. Если переменная с данным именем отсутствует, функция возвращает 0.
Если программой CGI обрабатывается запрос, содержащий метод POST, то тело сообщения запроса, в котором находится кодированное содержимое отправленной клиентом формы, можно получить из стандартного потока ввода. С каждым процессом связан стандартный поток ввода. Чтение из этого потока стандартно означает чтение с клавиатуры. При помощи операций с дескрипторами можно поместить блок данных в стандартный поток ввода. Читать из стандартного потока ввода в языке C удобнее всего при помощи функции чтения потока:
#include <stdio.h>
int fread(void *buffer, size_t size, size_t count, FILE *stream);
В параметре buffer передается адрес буфера, куда будут помещены прочитанные данные. В параметрах size и count указывается длина читаемого блока. Поскольку пользовательский агент передает длину тела сообщения в байтах, то в параметре size указывается 1, а в параметре count – длина тела сообщения запроса. В качестве параметра stream следует передать константу stdin для указания, что считывание производится из стандартного потока ввода. Функция fread возвращает количество фактически прочитанных символов.
Чтение из стандартного потока ввода является блокирующим, поэтому следует заранее определить число читаемых символов. Клиент вместе с другими заголовками передает заголовок Content-Length, в котором сообщается длина тела сообщения. Сервер при формировании среды окружения создает переменную CONTENT_LENGTH, значение которой совпадает со значением соответствующего заголовка. Поэтому чтением данной переменной можно определить число символов в стандартном потоке ввода.
Помимо стандартного потока ввода, с каждым процессом связан стандартный поток вывода. Для обычных процессов стандартный поток вывода, как правило, связан с экраном. При помощи операций замены дескриптора можно переключить стандартный поток вывода на любой открытый дескриптор. Сервер Web осуществляет переключение стандартного потока вывода на дескриптор канала или другого коммуникационного объекта. В результате такого перенаправления вся выводимая программой CGI в стандартный поток вывода информация будет передаваться серверу Web.
Формат данных, передаваемых от приложения CGI серверу Web, совместим с форматом сообщений протокола HTTP. Эти данные должны содержать заголовки, пустую строку и тело сообщения. Заголовки делятся на заголовки CGI и заголовки HTTP. Существует 3 заголовка CGI:
1. Content-Type – тип содержимого передаваемого тела сообщения. Значение соответствует заголовку Content-Type протокола HTTP.
2. Location – идентификатор, в котором содержится запрашиваемый ресурс. Значение соответствует заголовку Location протокола HTTP. При получении данного заголовка сервер Web передает пользовательскому агенту ответ с кодом перенаправления.
3. Status – строка состояния. Значением данного заголовка является трехзначный код состояния и поясняющая фраза, которые сервер Web должен передать пользовательскому агенту.
Все остальные заголовки считаются сервером Web заголовками HTTP и передаются пользовательскому агенту без изменения, также как и тело сообщения. Можно считать, что данные передаются от программы CGI непосредственно клиенту Web.
Выводить в стандартный поток вывода можно при помощи функции printf():
#include <stdio.h>
int printf(char *format, ...);
При обмене двоичными данными между приложением CGI и сервером Web, например, при отправке графического изображения, сформированного приложением CGI в процессе своей работы, следует учитывать некоторые особенности. Потоки могут находиться в двух режимах: двоичном и текстовом. В двоичном режиме данные передаются в том виде, в котором они представляются при хранении этих данных в памяти. В текстовом режиме некоторые символы могут приводить к определенным эффектам. Например, при записи символа конца файла происходит закрытие текстового потока, а при записи символа перевода строки в текстовый поток записываются два символа: перевода строки и возврата каретки. Аналогично, при чтении двух символов из потока, содержащего подряд символы перевода строки и возврата каретки, будет считан только один символ перевода строки. Поскольку стандартные потоки, как правило, открываются в текстовом режиме, перед передачей двоичных данных следует перевести эти потоки в двоичный режим. Для перевода потока в двоичный режим следует сначала получить его дескриптор. Это можно сделать, вызвав функцию fileno() стандартной библиотеки:
#include <stdio.h>
int fileno(FILE *stream);
Единственным параметром данной функции является указатель на поток. При получении дескриптора стандартного потока вывода в качестве значения параметра следует указать константу stdout, а при получении дескриптора стандартного потока ввода – stdin. Полученное значение дескриптора теперь можно использовать при вызове функции setmode() для перевода потока в двоичный режим:
#include <io.h>
#include <fcntl.h>
int setmode(int handle, int mode);
Первым параметром данной функции является дескриптор открытого потока. Вторым параметром является режим, в который будет переведен поток, задаваемый одной из констант: O_TEXT при переводе потока в текстовый режим или O_BINARY при переводе потока в двоичный режим. Функция возвращает ранее установленный режим для данного потока или –1 в случае ошибки.
Для записи в поток, находящийся в двоичном режиме, удобно использовать функцию fwrite():
#include <stdio.h>
int fwrite(void *buffer, size_t size, size_t count, FILE *stream);
В параметре buffer передается адрес буфера, откуда будут взяты записываемые в поток данные. В параметрах size и count указывается длина читаемого блока. Поскольку приложение CGI передает длину тела сообщения в байтах, то в параметре size указывается 1, а в параметре count – длина передаваемого блока данных. В качестве параметра stream следует передать константу stdout, для указания того, что запись производится в стандартный поток вывода. Функция fwrite() возвращает количество фактически записанных символов.
Функцию printf() неудобно использовать при передаче двоичных данных, поскольку в передаваемом блоке могут оказаться нули.
Стандартный поток ввода практически всегда нужно переводить в двоичный режим, поскольку, если передаваемые от пользовательского агента данные являются многострочными, то есть содержат символы перевода строки и возврата каретки, то значение переменной окружения CONTENT_LENGTH не будет соответствовать фактическому числу символов, прочитанному из стандартного потока ввода в текстовом режиме.
Программа CGI должна выполнить следующие действия:
1. Прочитать значение заголовка REQUEST_METHOD для определения запрашиваемого метода.
2. Если запрашиваемый метод – GET, то прочитать переменную окружения QUERY_STRING и считать полученное значение кодированным содержимым формы. Способ кодирования считать равным application/x-www-url-encoded.
3. Если запрашиваемый метод – POST, то прочитать длину тела сообщения запроса из переменной окружения CONTENT_LENGTH. Из стандартного потока ввода прочитать тело сообщения запроса и считать его кодированным содержимым формы. Способ кодирования прочитать из переменной окружения CONTENT_TYPE.
4. Разобрать кодированное содержимое формы в зависимости от способа кодирования.
5. Прочитать необходимые программе CGI переменные окружения.
6. Выполнить все необходимые в программе действия и сформировать документ с результатом работы программы.
7. Отправить в стандартный поток вывода все необходимые заголовки CGI и HTTP и пустую строку.
8. Отправить в стандартный поток вывода сформированный документ.