Використання CGI при створенні інтерактивних інтерфейсів

WWW (WorldWideWeb) і засоби інтерактивної взаємодії

Ціль даної глави познайомити користувача з тією частиною WWW-технологій яка зв'язана зі створенням інтерактивних інтерфейсів і передбачається, що користувач знайомий з основами WWW, HTML і С/С++.

В загальному випадку, інтерактивний інтерфейс користувача являє собою систему, що забезпечує взаємодію користувача і програми. Для WWW, інтерактивний інтерфейс можна визначити як послідовність HTML-документів, що реалізують інтерфейс користувача. Можна також умовно класифікувати принципи побудови інтерфейсу по типу формування HTML-документа:

статичний

динамічний

У першому випадку джерелом інтерфейсу є HTML-документ, створений у якому-небудь текстовому чи HTML-орієнтованому редакторі. Отже, даний документ залишається незмінним протягом використання. В другому випадку джерелом інтерфейсу є HTML-документ згенерований cgi-модулем. Отже, з'являється деяка гнучкість у видозміні інтерфейсу під час використання.

Таким чином, можна ввести поняття інтерактивного інтерфейсу для WWW.

Інтерактивний інтерфейс для WWW являє собою послідовність статичних чи динамічно сформованих HTML-документів, що реалізують інтерфейс користувача.

Практично будь-яка задача, що вирішує проблему одержання даних від клієнта, зв'язана з побудовою інтерфейсу. Найбільш цікавим є побудова інтерфейсів до різних баз даних, доступ до SQL-сервера, одержання інформації від периферійних пристроїв, створення клієнтських робочих місць. Усе це можливо за допомогою CGI(Common Gateway Interface).

Common Gateway Interface (CGI) є стандартом інтерфейсу зовнішньої прикладної програми з WWW сервером.

Задача побудови вищезгаданих інтерфейсів поділяється на дві частини:

Клієнтська частина

Серверна частина

Малюнок 2-1. Дві частини інтерактивного інтерфейсу.

Клієнтська частина

Для створення клієнтської частини необхідно створити HTML-документ, у якому реалізований інтерфейс із користувачем. У мові HTML це можливо за допомогою форм.

Серверна частина

Серверна частина складається з виконуючого модуля, що вирішує основні задачі обробки даних, що надходять від клієнтської частини, формування відповіді у форматі HTML, і т.д. Такий модуль називається cgi-модулем.

Методи HTTP запиту

Для реалізації взаємодії "клієнт-сервер" важливо, який метод HTTP запиту використовує клієнтська частина при звертанні до WWW сервера. У загальному випадку, запит - це повідомлення, що посилається клієнтом серверу. Перший рядок HTTP запиту містить у собі метод, що повен бути застосований до запитуваного ресурсу, ідентифікатор ресурсу(URI-Uniform Resource Identifier), і використовувану версію HTTP-протоколу. У розглянутому нами випадку, клієнтська частина застосовує методи запиту POST і GET. Метод POST використовується для запиту серверу, щоб той прийняв інформацію, включену в запит, щовідноситься до ресурсу, зазначеному ідентифікатором ресурсу. Метод GET використовується для одержання будь-якої інформації, ідентифікованої ідентифікатором ресурсу в HTTP запиті.

Для WWW-сервера стандарту NCSA прикладні програми чи CGI-модулі, що обробляють потік даних від клієнта чи(і) формуючи зворотний потік даних можуть бути написані на таких мовах програмування як:

C/C++;

Любою UNIX shell;

Fortran;

Perl;

Visual Basic;

TCL;

AppleScript;

 

 

Специфікація CGI

CGI визначає 4 інформаційних потоки:

Змінні оточення

Стандартний вхідний потік

Стандартний вихідний потік

Командний рядок

Малюнок 2-2. CGI-інтерфейс.

Змінні оточення

Змінні оточення умовно поділяються на два типи:

загальні для всіх типів запитів (встановлюються для всіх типів)

залежні від методу запиту

До змінних першого типу відносяться наступні змінні:

SERVER_SOFTWARE містить інформацію про WWW сервер (назва/версія)

SERVER_NAME містить інформацію про ім'я машини, на якій запущений WWW сервер, символічне ім'я чи IP адреса відповідні URL.

GATEWAY_INERFACE містить інформацію про версію CGI(CGI/версія)

Наступні змінні є специфічними для різних типів запитів і значення цим змінним привласнюються перед викликом cgi-модуля.

CONTENT_LENGTH значення цієї змінної відповідає довжині стандартного вхідного потоку в символах.

CONTENT_TYPE ця змінна специфікована для запитів утримуючих додаткову інформацію, таких як HTTP POST і PUT, і містить тип даних цієї інформації.

SERVER_PROTOCOL ця змінна містить інформацію про ім'я і версію інформаційного протоколу (протокол/версія).

SERVER_PORT значення змінної містить номер порту, на який був відісланий запит.

REQUEST_METHOD методзапиту, щобуввикористаний "POST","GET","HEAD" іт.д.

PATH_INFO значеннязмінноїміститьотриманийвідклієнтавіртуальнийшляхдо cgi-модуля.

PATH_TRANSLATED значеннязмінноїміститьфізичнийшляхдо cgi-модуля, перетворенийзізначення PATH_INFO.

SCRIPT_NAME віртуальний шлях до модуля, що виконується, який використовується для одержання URL.

QUERY_STRING значення цієї змінної відповідає рядку символів наступної за знаком "?" у URL відповідному даному запиту. Ця інформація не декодується сервером.

REMOTE_HOST містить символічне ім'я віддаленої машини, з якою був зроблений запит. У випадку відсутності даної інформації сервер привласнює порожнє значення і встановлює змінну REMOTE_ADDRESS.

REMOTE_ADDRESS містить IP адресуклієнта.

AUTH_TYPE якщо WWW-серверпідтримуєаутентифікацію (підтвердженнядійсності) користувачіві cgi-модульєзахищенимвідсторонньогодоступуто, значеннязмінноїспецифікуєметодаутентифікації.

REMOTE_USER містить ім'я користувача у випадку аутентифікації.

REMOTE_IDENT містить ім'я користувача, отримане від сервера (якщо сервер підтримує аутентифікацію згідно RFC 931).

HTTP_ACCEPT список типів MIME відомих клієнту. Кожен тип у списку повинний бути відділений комами відповідно до специфікації HTTP (тип/підтип,тип/підтип і т.д.).

HTTP_USER_AGENT назва програми перегляду яку використовує клієнт при посилці запиту.

Стандартний вивід

СGI - модуль виводить інформацію в стандартний вихідний потік. Цей висновок може являти собою документ, згенерований cgi-модулем, чи інструкцію серверу, де одержати необхідний документ. Звичайно cgi-модуль робить свій висновок. Перевага такого підходу у тому, що cgi-модуль не повинен формувати повний HTTP заголовок на кожен запит.

Заголовок вихідного потоку
В деяких випадках необхідно уникати обробки сервером висновку cgi-модуля, і посилати клієнту дані без змін. Для відмінності таких cgi-модулів, CGI вимагає, щоб їхні імена починалися на nph-. У цьому випадку формування синтаксично правильної відповіді клієнту cgi-модуль бере на себе.

Заголовки із синтаксичним розбором
Висновок cgi-модуля повинний починатися з заголовка утримуючого визначені рядки і завершуватися двома символами CR(0x10).

Будь-які рядки не є директивами сервера, посилаються безпосередньо клієнту. На даний момент, CGI специфікація визначає три директиви сервера:

Content-type

MIME чи тип документа, що повертається
Наприклад: Content-type: text/html <CR><CR> повідомляє серверу, що наступні за цим повідомленням дані - є документ у форматі HTML.

Location
Вказує серверу, що повертається не сам документ, а посилання на нього.

Якщо аргументом є URL, то сервер передасть вказівку клієнту на перенаправлення запиту. Якщо аргумент являє собою віртуальний шлях, сервер поверне клієнту заданий цим шляхом документ, як якби клієнт запитував цей документ безпосередньо.

Наприклад: Location: http://host/file.txt приведе до того, що WWW сервер видасть file.txt, як якби він був викликаний клієнтом. Якщо cgi-модуль повертає посилання на gopher сервер, наприклад на gopher://gopher.ncsa.uiuc.edu/. Висновок буде наступний:

Location: gopher://gopher.ncsa.uiuc.edu/

*Status
задає серверу HTTP/1.0 рядок-статус, що буде послана клієнту у форматі: nnn xxxxx

де: nnn - 3-х цифровий код статусу

ххххх - рядок причини

Наприклад: HTTP/1.0 200 OK

Server: NCSA/1.0a6

Content-type: text/plain

<динамічно згенерований текст повідомлення>

У даному випадку, клієнту буде повідомлено про успішне виконання запиту.

Стандартний вхідний потік

У випадку методу запиту POST дані передаються як вміст HTTP запиту. І будуть послані в стандартний вхідний потік.

Дані передаються cgi-модулю в наступній формі:

name=value&name1=value1&...&name=value

 де name - ім'язмінної,

value - значення змінної,

N - кількість змінних.

 

На файловий дескриптор стандартного потоку введення посилається CONTENT_LENGTH байт. Так само сервер передає cgi-модулю CONTENT_TYPE (типданих). Сервер не посилає символ кінця файлу після передачі CONTENT_LENGTH байт даних чи після того, як cgi-модуль їх прочитає. Змінні оточення CONTENT_LENGTH і CONTENT_TYPE встановлюються в той момент, коли сервер виконує cgi-модуль. Таким чином, якщо в результаті виконання форми з аргументом тега FORM - METHOD="POST" сформований рядок даних firm=МММ&price=100023, то сервер встановить значення CONTENT_LENGTH рівним 21 і CONTENT_TYPE у application/x-www-form-urlencoded, а в стандартний потік вводу посилається блок даних.

У випадку методу GET, рядок даних передається як частина URL.
Т.б. наприклад:
http://host/cgi-bin/script?name1=value1&name2=value2

У цьому випадку змінна оточення QUERY_STRING приймає значення
name1=value1&name2=value2

Аргументи командного рядка

СGI-модуль у командному рядку від сервера одержує:

залишок URL після імені cgi-модуля як перший параметр (перший параметр буде порожній, якщо було присутнє тільки ім'я cgi-модуля), список ключових слів як залишок командного рядка для скрипту пошуку,  імена полів, що чергуються, форми з доданим знаком рівності і відповідних значень змінних.

Ключові слова, імена і значення полів форми передаються декодованими (з HTTP URL формату кодування) і перекодованими відповідно до правил кодування Bourne shell так, що cgi-модуль у командному рядку одержить інформацію без необхідності здійснювати додаткові перетворення.

Послідовність дій для обробки вхідних даних cgi-модуля для різних методів запиту GET і POST

Виходячи з різниці методів запитів GET і POST, можна визначити послідовність дій для обробки вхідних даних cgi-модуля для різних типів запитів.

Для методу GET

Одержати значення змінної QUERY_STRING

Декодувати імена і їхні значення (з огляду на те, що всі пробіли при декодуванні сервером були замінені символом "+" і всі символи з десятковим кодом більше 128 перетворені в символ "%" і наступним за ним шістнадцятковим кодом символу.)

Сформувати структуру відповідності "ім'я - значення" для подальшого використання в cgi-модулі

Для методу POST

Одержати зі стандартного вхідного потоку CONTENT_LENGTH символів.

Декодувати імена і їхні значення (з огляду на те, що всі пробіли при декодуванні сервером були замінені символом "+" і всі символи з десятковим кодом більше 128 перетворені в символ "%" і наступним за ним шістнадцятковим кодом символу.)

Сформувати структуру відповідності "ім'я - значення" для подальшого використання в cgi-модулі

Очевидно, що відмінність тільки в джерелі даних. Тому, у принципі, можливе створення єдиного модуля для методів POST і GET. Необхідно тільки додати в початок перевірку значення змінної REQUEST_METHOD для визначення методу запиту. Після формування структури "ім'я-значення" можна приступити до рішення задач, заради яких, власне, створювався cgi-модуль. Зрозуміло, що задачі, розв'язувані cgi-модулем, можуть бути дуже різноманітними (одержання й обробка пошти, доступ до баз даних, гостьова книга і т.д.).

Наступним важливим моментом є динамічне формування cgi-модулем HTML-документа (оформлення результату роботи модуля). Наприклад, таблиці вибірки з бази даних.

Для цього cgi-модуль повинний видати в стандартний вихідний потік заголовок, який складається з рядка:
Content-type: text/html і порожнього рядка (двох символів CR)

Після цього заголовка можна давати будь-який текст у форматі HTML.

Приклади cgi-модулів

Як приклад розглянемо роботу тестових програм, що поставляються разом із програмним забезпеченням сервера НТТР стандарту NCSA.

Для тестування роботи форм поставляються програми :
post-query - для тестування роботи форм із методом запиту POST
query - для тестування роботи форм із методом запиту GET
util.c - опис функцій для обробки вхідного потоку (використовується query і post-query).

Розглянемо простий приклад форми мовою HTML програму, що використовує, query.

<HTML>
<HEAD>
<TITLE>Приклад використання CGI</TITLE>
</HEAD>

 <BODY>
<FORM ACTION="http://iceman.cnit.nsu.ru/cgi-bin/post-query" METHOD="POST">
<B>Уведіть своє ім'я<I>(Прізвище Ім'я По батькові)</I>:</B>
<INPUT name=RealName type=text size=40 maxlength=60 value="Петров Іван Сидорович"><P>
Підлога: <INPUT name=Sex type=Radio value="Чоловічий" CHECKED>- чоловічий <INPUT name=Sex type=Radio value="Жіночий">-жіночий<P>
<INPUT name=Submit type=submit value="Надіслати запит"><BR>
<INPUT name=Reset type=reset value="Скидання">

 </FORM>
</BODY>
</HTML>

Після ініціації форми шляхом натискання кнопки "Надіслати запит" WWW сервер обробляє потік даних від форми (заміняє всі пробіли в іменах і значеннях на символ "+", заміняє всі символи з десятковим кодом великим 128 на символ "%" і наступним за ним шістнадцятковим кодом символу (наприклад "І" у %З8)).
Вихідний потік прийме наступний вид:

RealName=%CF%E5%F2%F0%EE%E2+%C8%E2%E0%ED+%D1%E8%E4%EE%F0
 %EE%E2%E8%F7&Sex=%CC%F3%E6%F1%EA%EE%E9&Submit= %CF%EE%F1
 %EB%E0%F2%FC+%E7%E0%EF%F0%EE%F1

У момент передачі керування модулю post-query сервер привласнює значення змінним оточення й аргументам командного рядка:

argc = 0. argv =
 SERVER_SOFTWARE = NCSA/1.5.1
 SERVER_NAME = iceman.cnit.nsu.ru
 GATEWAY_INTERFACE = CGI/1.1
 SERVER_PROTOCOL = HTTP/1.0
 SERVER_PORT = 80
 REQUEST_METHOD = POST
 HTTP_ACCEPT = image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,*/*
 PATH_INFO =
 PATH_TRANSLATED =
 SCRIPT_NAME = /cgi-bin/test-cgi
 QUERY_STRING =
 REMOTE_HOST = fwa.cnit.nsu.ru
 REMOTE_ADDR = 193.124.209.74
 REMOTE_USER =
 AUTH_TYPE =
 CONTENT_TYPE = application/x-www-form-urlencoded
 CONTENT_LENGTH = 142

Результат роботи post-query:
 <H1>Query Results</H1>You submitted the following name/value pairs:<p>
<ul>
<li> <code>RealName = Петров Іван Сидорович</code>
<li> <code>Sex = Чоловічий</code>
<li> <code>Submit = Надіслати запит </code>
</ul>

І на екрані браузера
 Query Results
 You submitted the following name/value pairs:
 RealName = Петров Іван Сидорович
Sex = Чоловічий
Submit = Надіслати запит

Нижче приведений вихідний текст програми post-query.

#include <stdio.h>
#ifndef NO_STDLIB_H
#include <stdlib.h>
#elsechar *getenv();
#endif
#define MAX_ENTRIES 10000

typedef struct {
char *name;
char *val;
} entry;

char *makeword(char *line, char stop);
char *fmakeword(FILE *f, char stop, int *len);
char x2c(char *what);
void unescape_url(char *url);
void plustospace(char *str);

main(int argc, char *argv[])

{
entry entries[MAX_ENTRIES];
register int x,m=0;
int cl;
printf("Content-type: text/html%c%c",10,10);
if(strcmp(getenv("REQUEST_METHOD"),"POST"))
{ printf("This script should be referenced with a METHOD of POST.\n");
printf("If you don't understand this, see this "); printf("<A HREF=\"http://www.ncsa.uiuc.edu/SDG/Software/Mosaic/Docs/fill-out-forms/overview.html\"> forms overview</A>.%c",10);
exit(1);
} if(strcmp(getenv("CONTENT_TYPE"),"application/x-www-form-urlencoded"))
{printf("This script can only be used to decode form results. \n");
exit(1);
}
cl = atoi(getenv("CONTENT_LENGTH"));
for(x=0;cl && (!feof(stdin));x++)
{m=x;entries[x].val = fmakeword(stdin,'&',&cl); plustospace(entries[x].val);
unescape_url(entries[x].val);
entries[x].name = makeword(entries[x].val,'=');
}
printf("<H1>Query Results</H1>");
printf("You submitted the following name/value pairs:<p>%c",10);
printf("<ul>%c",10);
for(x=0; x <= m; x++)
printf("<li> <code>%s = %s</code>%c",entries[x].name, entries[x].val,10);
printf("</ul>%c",10);
}

Треба відзначити, що post-query не обробляє імена, тому в прикладі вони дані англійською мовою. Якщо Ви використовуєте російські назви імен, то ви повинні обробити імена також як і значення, тобто замінити всі символи "+" на пробіли і перетворити шістнадцяткові коди кириличних символів у сам символ.
Приведемо також вихідний текст функцій, які використовуються post-query.

char *makeword(char *line, char stop) {
/* Призначена для виділення частини рядка, обмеженого "стоп-символами"*/
int x = 0,y;
char *word = (char *) malloc(sizeof(char) * (strlen(line) + 1));
for(x=0;((line[x]) && (line[x] != stop));x++)
word[x] = line[x];
word[x] = '\0';
if(line[x]) ++x;
y=0;

 while(line[y++] = line[x++]);
return word;
}

 char *fmakeword(FILE *f, char stop, int *cl) {
/* Призначена для виділення рядка, обмеженого "стоп-символом" stop, з потоку f довжиною cl.
*/
int wsize;
char *word;
int ll;

 wsize = 102400;
ll=0;
word = (char *) malloc(sizeof(char) * (wsize + 1));

 while(1) {
word[ll] = (char)fgetc(f);
if(ll==wsize) {
word[ll+1] = '\0';
wsize+=102400;
word = (char *)realloc(word,sizeof(char)*(wsize+1));
}
--(*cl);
if((word[ll] == stop) || (feof(f)) || (!(*cl))) {
if(word[ll] != stop) ll++;
word[ll] = '\0';
return word;
}
++ll;
}
}


 char x2c(char *what) {
/* Призначена для перетворення шістнадцяткового коду символу в код символу
*/
register char digit;

 digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
digit *= 16;
digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
return(digit);
}

 void unescape_url(char *url) {

 register int x,y;

 for(x=0,y=0;url[y];++x,++y) {
if((url[x] = url[y]) == '%') {
url[x] = x2c(&url[y+1]);
y+=2;
}
}
url[x] = '\0';
}

 void plustospace(char *str) {
/*заміна символів "+" на символ "пробіл"*/
register int x;

 for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' ';
}

Длядемонстраціїреалізаціїформизметодомзапиту GET скористаємосятієюжсамоюформою, щоідляметоду POST іпрограмою query. Дляцьогозмінимозначенняатрибутів ACTION і METHOD утезі FORM.

<FORM action="http://iceman.cnit.nsu.ru/cgi-bin/query" METHOD=GET>

Після ініціації форми сервер встановить наступні значення для змінних оточення й аргументів командного рядка:

argc = 0. argv is =
SERVER_SOFTWARE = NCSA/1.5.1
SERVER_NAME = iceman.cnit.nsu.ru
GATEWAY_INTERFACE = CGI/1.1
SERVER_PROTOCOL = HTTP/1.0
SERVER_PORT = 80
REQUEST_METHOD = GET
HTTP_ACCEPT = image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
PATH_INFO =
PATH_TRANSLATED =
SCRIPT_NAME = /cgi-bin/test-cgi
QUERY_STRING = RealName=%CF%E5%F2%F0%EE%E2+%C8%E2%E0%ED+%D1%E8
%E4%EE%F0%EE%E2%E8%F7&Sex=%CC%F3%E6%F1%EA%EE%E9&Submit=%CF%EE %F1%EB%E0%F2%FC+%E7%E0%EF%F0%EE%F1
REMOTE_HOST = fwa.cnit.nsu.ru
REMOTE_ADDR = 193.124.209.74
REMOTE_USER =
AUTH_TYPE =
CONTENT_TYPE =
CONTENT_LENGTH =

Якмибачимо, вихіднийпотіквідформиз'явивсявзначеннізмінної QUERY_STRING.

Результат роботи query цілком збігається з результатом роботи post-query.

 

 

Джерела:

http://www.citforum.ru/internet/cgi/

http://www.citforum.ru/database/cnit/index.shtml

 

 

 

Hosting Directory
Хостинг от uCoz