Описание протокола Оглавление Библиотека ITCGI

Создание CGI-программ

Когда пользователь заполняет html-форму и нажимает кнопку submit, данные отправляются веб-серверу. Веб-сервер, будь это Apache, IIS или какой-либо другой, запускает программу, указанную в качестве значения атрибута action. В нашем случае это test.cgi. Веб-сервер запускает test.cgi и передает ей параметры в виде текстовой строки, следующего содержания: name1=value1&name2=value2&....nameN=valueN, т.е. имя_параметра=значение. Эта строка передается на стандартный поток ввода (STDIN) или в качестве значения переменной окружения QUERY_STRING. Соответственно, считать данную строку в программе можно одним из двух способов:
  unsigned int len;
  len = atoi( getenv("CONTENT_LENGTH") );
  query = (char*)malloc(len+1);
  fread(query, 1, len, stdin);
  query[len] = 0;
  
  или
  
  query=(char*)malloc(strlen(getenv("QUERY_STRING")));
  strcpy(query,getenv("QUERY_STRING"));
В первом случае параметры передаются методом POST, а во втором методом GET. В первом случае мы читаем строку из STDIN. Длину строки мы узнаем из значения параметра окружения CONTENT_LENGTH. Во втором она хранится в переменной окружения QUERY_STRING. Значение переменной окружения можно получить, вызвав функцию getenv. Метод, с помощью которого передается строка с параметрами CGI-программе, можно определить следующим образом: strcmp(getenv("REQUEST_METHOD"),"POST"). Далее придется разбирать строку и получать необходимые значения параметров. Для того чтобы не делать это каждый раз, мы написали небольшую, но очень удобную библиотечку ITCGI для написания CGI-скриптов. Эта библиотека позволяет вам полностью абстрагироваться от метода, которым передаются параметры, от кодировки, от разбора строки. Вы просто вызываете функцию GetParamByName, в которую передаете имя интересующего вас параметра и адрес строки, куда сохранить значение. Библиотека также предоставляет вам ряд функций для написания эффективных и защищенных от взлома CGI-скриптов.
В простейшем случае, когда ваша программа не нуждается в параметрах, вам и не потребуется ни самому разбирать и раскодировать строку, ни использовать для этого нашу библиотеку. Самой простой CGI-программой будет:
#include<stdio.h>

int main()
{
// выдаем обязательный заголовок
// это часть CGI-протокола
printf("Content-type: text/html\n\n");

printf("<html>");
printf("<body>");

printf("Hello, World!");

printf("</body>");
printf("</html>");
return 0;
}
Заголовок является обязательной частью. Он передается веб-серверу и определяет, что следует за ним. В большинстве случаев у вас будет именно такой заголовок. Он говорит веб-серверу, что дальше идет HTML-код. С другими типами заголовков мы познакомимся чуть позже. В заголовке может быть несколько строк. Конец заголовка обозначается двумя переходами на новую строку - \n\n. Откомпилируйте эту программу, а исполняемый файл положите в каталог /cgi-bin вашего веб-сайта. Переименуйте его в test.cgi. К этому скрипту можно обратится непосредственно через обозреватель, написав в командной строке URL, например у меня это выглядит так http://itsoft.ru/cgi-bin/test.cgi В результате, в вашем обозревателе вы увидите строку: "Hello, World!".
Далее мы рассмотрим CGI-программу такого же типа. Она не принимает никаких параметров, но зато выдает более полезную информацию - список и значения всех переменных окружения. Такой скрипт вам пригодится, когда вы будете отлаживать свои CGI-программы на различных веб-серверах. Дело в том, что переменные окружения различаются на различных веб-серверах. Так, например, для веб-сервера Apache, путь к каталогу веб-сайта хранится в переменной окружения DOCUMENT_ROOT. Для веб-сервера Microsoft Internet Information Server это значение хранится в переменной PATH_TRANSLATED. В операционной системе UNIX скрипт для вывода всех переменных выглядит следующим образом.
#!/bin/sh
echo "content-type: text/plain\n\n"
echo 
env
Обратите внимание на CGI-заголовок. Он отличается от того, который у нас был в предыдущем примере. plain означает, что скрипт выдаст не HTML-код, а чистый текст. Броузер будет воспринимать его, как обычный текст и выводить в точности как есть. Здесь не надо заменять спецсимволы типа < на их эквиваленты &lt;. Скопируйте этот скрипт в директорию /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=<ADDRESS>Apache/1.3.12 
  Server at itsoft.ru Port 80</ADDRESS>

HTTP_ACCEPT=image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
SCRIPT_FILENAME=/usr/local/www/itsoft/cgi-bin/web/env
HTTP_HOST=itsoft.ru
REQUEST_URI=/cgi-bin/web/env
SERVER_SOFTWARE=Apache/1.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.1
HTTP_ACCEPT_ENCODING=gzip, deflate
REQUEST_METHOD=GET
SERVER_ADMIN=igor@itsoft.ru
SERVER_ADDR=194.226.32.34
SERVER_PORT=80
SCRIPT_NAME=/cgi-bin/web/env
SERVER_NAME=itsoft.ru
Программа на языке Си для Windows и веб-сервера Internet Information Server будет выглядеть следующим образом:
#include <stdio.h>
#include <stdlib.h>
void main()
{
char *text;
char str[1024];
int length;
FILE *in;

sprintf(str,"command.com /c set>%s\\temp\\env.dmp",getenv("PATH_TRANSLATED"));
system(str);

sprintf(str,"%s\\temp\\env.dmp",getenv("PATH_TRANSLATED"));
in = fopen(str, "rb");
if( !in )
 {
  printf("Content-type: text/plain\n\nCan't open file %s.", str);
  return;
 }

fseek(in, 0, SEEK_END);
length = ftell(in);
fseek(in, 0, SEEK_SET);
text = (char*)malloc(length+1);
fread(text, 1, length, in);
text[length] = 0;
fclose(in);

printf("Content-type: text/plain\n\n%s", text);
free(text);
}

Сначала выполняется команда 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-формы, описанной в четвертой главе.
#include <stdio.h>
#include <stdlib.h>

void main()
{
char* query=NULL;

 if( !strcmp(getenv("REQUEST_METHOD"),"POST") )
 {
  unsigned int len;
  len = atoi( getenv("CONTENT_LENGTH") );
  query = (char*)malloc(len+1);
  fread(query, 1, len, stdin);
  query[len] = 0;
 }
 else if( !strcmp(getenv("REQUEST_METHOD"),"GET") )
 {
  query=(char*)malloc(strlen(getenv("QUERY_STRING")));
  strcpy(query,getenv("QUERY_STRING"));
 }
 else
  printf("unknown REQUEST_METHOD\n");


printf("Content-type: text/plain\n\n%s", query);
free(query);
}
Скомпилируйте этот код. Он платформенно независимый, поэтому можете скомпилировать как под 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 или же самим разбирать эту строку параметров. О библиотеке и ее практическом использовании читайте в следующей параграфе.