Ціль даної глави познайомити користувача з тією частиною 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 визначає 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-модуль у командному рядку одержить інформацію без необхідності здійснювати додаткові перетворення.
Виходячи з різниці методів запитів GET і POST, можна визначити послідовність дій для обробки вхідних даних cgi-модуля для різних типів запитів.
Одержати значення змінної QUERY_STRING
Декодувати імена і їхні значення (з огляду на те, що всі пробіли при декодуванні сервером були замінені символом "+" і всі символи з десятковим кодом більше 128 перетворені в символ "%" і наступним за ним шістнадцятковим кодом символу.)
Сформувати структуру відповідності "ім'я - значення" для подальшого використання в cgi-модулі
Одержати зі стандартного вхідного потоку CONTENT_LENGTH символів.
Декодувати імена і їхні значення (з огляду на те, що всі пробіли при декодуванні сервером були замінені символом "+" і всі символи з десятковим кодом більше 128 перетворені в символ "%" і наступним за ним шістнадцятковим кодом символу.)
Сформувати структуру відповідності "ім'я - значення" для подальшого використання в cgi-модулі
Очевидно, що відмінність тільки в джерелі даних. Тому, у принципі, можливе створення єдиного модуля для методів POST і GET. Необхідно тільки додати в початок перевірку значення змінної REQUEST_METHOD для визначення методу запиту. Після формування структури "ім'я-значення" можна приступити до рішення задач, заради яких, власне, створювався cgi-модуль. Зрозуміло, що задачі, розв'язувані cgi-модулем, можуть бути дуже різноманітними (одержання й обробка пошти, доступ до баз даних, гостьова книга і т.д.).
Наступним важливим моментом є динамічне формування cgi-модулем HTML-документа (оформлення результату роботи модуля). Наприклад, таблиці вибірки з бази даних.
Для цього cgi-модуль повинний видати в
стандартний вихідний потік заголовок, який складається з рядка:
Content-type: text/html і порожнього рядка (двох символів CR)
Після цього заголовка можна давати будь-який текст у форматі HTML.
Як приклад розглянемо роботу тестових програм, що поставляються разом із програмним забезпеченням сервера НТТР стандарту 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