Internet и CGI

§1. Загрузка ресурса IMG.

Задача.

Вы хотите, чтобы на вашей HTML-странице, случайным образом, загружались бы графические изображения, перечень которых может постоянно изменяться и пополняться.

Решение.

Используем атрибут SRC эленента IMG для загрузки CGI-ресурса:

.
Надо отметить, что этот прием достаточно часто используется в Web-программировании. Все сложности сводяться к написанию премлемого CGI-скрипта, который отвечал бы поставленной задаче. В качестве наглядного примера приведем давольно простой скрипт, обеспечивающий случайный вывод изображения из ограниченного списка графических файлов:
#!/usr/bin/perl $path = "c:/apache/htdocs"; @pic=('c0.gif','c1.gif','c2.gif','c3.gif','c4.gif','c5.gif','c6.gif','c7.gif','c8.gif'); srand; $id=int(rand(9)); $gif=$path.'/images/'.$pic[$id]; print "Content-Type: image/gif\n\n"; open G,$gif; binmode( G ); binmode( STDOUT ); print ; close G; exit;
Для полноты картины приведем и текст HTML-страницы:
Copyright 2002 The Web Production TOP Text text ......


BOTTOM Text text ......


В этом примере видно, что дизайн может быть реализован отдельно от загружаемых изображений (но, с учетом их ширины и высоты) и может изменяться независимо от CGI-сценария и независимо от CGI-программиста. Более сложный вариант CGI-программы, обеспечивающий случайный выбор из переменного списка изображений, должен включать код открытия обособленной директории файлов изображений:
... ... ... $DIR_PICTURES = $path.'/picturies'; opendir (ETC, $DIR_PICTURES)die "no directory!: $!"; $i=0; foreach $filename (readdir(ETC)) { if($i > 1){ $k=$i-2; $my_pic[$k]=$filename;} $i++;} close (ETC); $leng=@my_pic; srand; $id=int(rand($leng)); $gif=$path.'/images/'.$my_pic[$id]; ... ... ...
В этом скрипте, имена файлов заносяться в массив @my_pic, причем для пропуска точек (. и ..) в верхей части директории, выполняется условый оператор в зависимости от значения счетчика (переменная $i).
В заключение отметим, что элемент IMG и сответствующий CGI-сценарий часто используются в качестве скрытых счетчиков посетителей страниц. Такие скрипты могут также содержать процессы MAIL, которые позволяют, в сою очередь, известить владельца о посещении страницы визитером, имеющего такой-то IP-адрес, зашедший с такой-то страницы и т.п.

§2. Загрузка CGI-сценариев в контейнере IFRAME.

Основной привлекательностью использования элемента IFRAME является возможность выделения в произвольном месте HTML-страницы прямоугольной области произвольных размеров. Этот прием часто применяется для внедрения в статическую HTML-страницу, новостной, часто меняющейся информации, как текстовой, так и графической. При этом, как правило, новостная вставка, сама представляет собой HTML-страницу (файл). Применение, в качестве загрузочного ресурcа CGI-сценария позволит автоматизировать процедуру смены новостей.
Задача.

Автоматизировать процесс смены новостной информации (текстовой) в поле статической HTML-страницы.
Решение.
CGI программа считывает текстовый файл с диска, или выполняет выборку из базы данных (БД), генерирует на лету документ HTML и загружает ресурс в элемент IFRAME:
.
Отметим, что элемент IFRAME представляет собой встроенный элемент Internet Explorer (IE). Начнем рассмотрение с CGI-скрипта:
#!/usr/bin/perl print "Content-Type: text/html\n\n"; $path = "c:/apache/htdocs"; $FILE_NEWS=$path.'/txt/bulletin.txt'; open(LIST,"<$FILE_NEWS"); @lines=; close(LIST); #-------HTML-include: print <

@lines

EOT exit;
В процессе загрузки в броузер новостной HTML-страницы, управление передается CGI-скрипту, который считывает обычный текстовый файл bulletin.txt с диска сервера, создает на лету HTML-вставку и выводит ресурс в элемент IFRAME. Для наглядности, HTML-вставка размещена в теле CGI-скрипта. В реальной ситуации, HTML-вставка существует в виде шаблона в каталоге шаблонов на сервере или находиться в таблице шаблонов в БД. Ниже представлена собственно новостная HTML-страница (news.html):

§3. CGI-JavaScript программирование.

В рассмотренных выше примерах вставка возвращаемых данных осуществлялась, со стороны броузера, пассивно. Активное взаимодействие сценариев CGI и скриптов языка JavaScript в значительной степени расширяет возможности представления и обработки этих данных. В качестве примера, рассмотрим задачу охраны авторского права на публикуемую новостную информацию.
Задача.
Автоматизировать процесс смены новостной информации (текстовой) в поле статической HTML-страницы и обеспечить при этом сохранение информации об авторских правах на публикацию.
Решение.
В новостной HTML-странице располагается некоторая подпрограмма-функция test_copyright() языка JavaScript, ненсущая инфорацию об авторских правах. Выполнение этой подпрогаммы возлагается на сценарий CGI. CGI программа считывает файл новостей с диска, сравнивает информацию об авторских правах с образом copyright и генерирует на лету свой JavaScript скрипт test2( ), который непосредственно и выполняет вывод в окно броузера.
Загрузка CGI ресурса выполняется с помощью атрибута SRC элемента SCRIPT:
.
Расссмотрим сначала код HTML-страницы:

News Bulletin


Examples examples examples examples examples examples ......




Как видим в теле HTML-страницы располагается подпрограмма-функция test_copyright( ). Возвращаемый параметр этой функции содержит copyright. Образ этого copyright находится в одной из переменных сценария CGI:

#!/usr/bin/perl print "Content-Type: text/html\n\n"; $path = "c:/apache/htdocs"; $FILE_NEWS=$path.'/txt/bulletin.txt'; open(LIST,"<$FILE_NEWS"); @lines=; close(LIST); $txt=join("",@lines); $txt=~s/\n//g; $param='Copyright 2002 The Web Production'; #patteren print "function test2(){". "var m1= test_copyright();". "if(m1==\"$param\"){document.write(\"$txt\");". "document.write(\"

$param
\")}else{err();}}"; exit;

Функция test2( ) языка JavaScript, размещается в теле CGI-скрипта, а вызывается на выполнение при загрузке HTML-страницы. В процессе выполнения сценария CGI, последний анализирует информацию, возвращаемую подпрограммой-функцией test_copyright( ) и сравнивает ее, с помощью скрипта test2( ), с образом copyright в переменной $param. В зависимости от совпадения или несовпадения образа с copyright, новостная информация публикуется или возвращается ошибка JavaScript. Ошибка JavaScript, равносильная запрету на публикацию, возникает в двух случаях: 1) если вы измените или удалите copyright из тела HTML-страницы, 2) если вы попытаетесь удалить подпрограмму-функцию.

Отметим, что как и прежде, реализуется идеология разделения CGI-программы и дизайна (Рис.2).

§3. CGI-JavaScript программирование.


Рис. 2.

Из кода HTML видно, что атрибуты текста новостей могут быть изменены независимо от сценария CGI и независимо от скрипта языка JavaScript. Разумеется, мы здесь представили упрощенный вариант скриптов, преследуя цель наглядности излагаемого материала. Понятно, что техника CGI-JavaScript программирования, позволяет создать весьма изощеренные средства защиты и ограничения доступа.

§4. CGI-Java программирование.

Пожалуй CGI-Java технология является наиболее перспективной и наиболее интересной в области Web-программирования. Несмотря на выросшую популярность языка Java, CGI остается фактическим стандартом взаимодействия в модели клиент/сервер. Непоколибимость CGI объясняется просто. Во-первых протокол HTTP является основным механизмом передачи информации. Этот механизм положен в основу WWW. Во-вторых, это мощность языка Perl, который чаще всего используется в CGI-программировании. Perl изначально задумывался как высокоуровневый кросс-платформенный язык системного программирования [4]. Perl 5 работает практически везде: Unix, Linux, BSD, Windows, .NET, Sun, Macintosh. В Perl поддерживается интерфейс к базам данных SQL и постреляционным СУБД типа Cache' [5]. При желании в Perl можно написать программу, в которой будет реализован практически весь арсенал приемов объекто-ориентированного программирования [6]. И наконец, в третьих, программе на Java можно передать всю необходимую информацию от CGI-сценария с помощью потоков ввода-вывода. И наоборот, программы на Java сами могут передавать информацию в сценарии CGI, например, можно "заставить" апплет записывать информацию, полученную из формы в файл на диск.
Рассмотрим примеры CGI-Java программирования в рамках обозначенной темы настоящей публикации.
Задача.
Автоматизировать процесс смены новостной информации (текстовой) в поле статической HTML-страницы и обеспечить вывод текта в графическом виде в целях противодействия несанкционированному копированию новостной информации.
Решение.
В новостной HTML-странице располагается контейнерный элемент APPLET, где атрибут CODE задает имя загружаемого ресурса Java-программы (апплета). Java-апплет, в процессе выполнения, с помощью метода DataInputStream( ) создает поток ввода для ресурса CGI-сценария. CGI-сценарий выполняет выборку текстовой информации из SQL БД, построчно форматирует полученный текст и в потоке вывода построчно передает апплету. Апплет с помощью методов paint( ) и dawString( ) также построчно отображает текст в окне броузера в графической форме.
Включение апплета в документ HTML реализуется следующим образом:

,

где codebase - имя каталога, в котором содержится файл cgi_java.class.
Рассмотрим сначала Java-апплет:

import java.awt.*; import java.net.*; import java.io.*; import java.awt.Graphics; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.lang.*; import java.net.MalformedURLException; public class cgi_java extends java.applet.Applet{ private TextArea fileArea; Font titleFont; FontMetrics titleFontMetrics; String line; int num; String text[ ] = new String[100]; public void init() { fileArea = new TextArea(); try { URL file = new URL(getDocumentBase(), "/cgi-bin/java_cgi.cgi"); BufferedInputStream my_buffer = new BufferedInputStream(file.openStream()); DataInputStream in = new DataInputStream(my_buffer); num=0; while ((line = in.readLine()) != null){ fileArea.appendText(line); fileArea.appendText("\n"); text[num]=line; num++; } in.close(); } catch(MalformedURLException e) { } catch(IOException e) { } } public void paint(Graphics g) { titleFont = new java.awt.Font("Courier", Font.BOLD, 80); titleFontMetrics = getFontMetrics(titleFont); g.setColor(Color.white); g.fillRect(0,0,600,500); g.setColor(Color.cyan); g.setFont(titleFont); g.drawString("Copyright",0, 80); g.drawString("© 2002",30, 180); g.drawString("TheWeb",30, 280); g.drawString("Production",0, 380); titleFont = new java.awt.Font("Courier", Font.BOLD, 12); titleFontMetrics = getFontMetrics(titleFont); g.setColor(Color.black); g.setFont(titleFont); int j, x=10, y=40, dy=15; for (j=0; j < num; j++){ g.drawString(text[j], x, y); y=y+dy; } } }

В этом апплете, для реализации построчного чтения выходного потока CGI-сценария, мы использовали метод языка Java - readLine( ). Для того, чтобы этот метод работал, необходимо предварительно определить собственно поток DataInputStream(my_buffer), а перед этим для чтения информации из открытого потока ввода, следует сначала создать буффер: my_buffer. В процессе чтения строк, последние заполняют массив text[ ]. Графический вывод строк в броузер обеспечивает метод Paint( ) с помщью оператора g.drawString(text[j], x, y).

CGI программа как всегда лаконична:


#!/usr/bin/perl print "Content-type: text/plain\n\n"; require 'globalvar.pm'; $table_name="news_arjive"; $dbh = DBI->connect("DBI:mysql:$database:$host:$port",$login,$pass); $sth = $dbh->prepare("SELECT text_news FROM $table_name WHERE id = 1"); $sth->execute; $txt = $sth->fetchrow_array (); $sth->finish; $dbh->disconnect; $txt=~s/\n/ /g; $txt.=" "; $start=0; $my_leng=64; $str="-"; while($str){ $str=substr($txt,$start,$my_leng); $etc_leng=rindex($str," "); $str=substr($txt,$start,$etc_leng); $start=$start + $etc_leng; print "$str\n";} exit;

Переменные $database, $host, $port, $login и $pass, для доступа и открытия БД, загружаются в сценарий с помощью команды require из модуля globalvar.pm. Длинна строк $my_leng не превышает 64 символов. Образом для выделения подстрок является пробельный символ " ". Циклическое формирование строк и создание стандартного потока вывода, осуществляется всего шестью строками кода. На Java такое трудно реализовать. Зато на Java легко реализуется графическая форма представления текста. Графический вывод текстовой информации, преследует цель противодействовать несанкционированному копированию новостных данных. Чтобы усилить защиту против копирования, текст выводиться на фоне "водяных знаков" - рис. ... . Теперь потенциальному взломщику потребуется выполнить копирование экрана, обработку полученного изображения в PhotoShop, преобразование графики в текст с помощью FineReader, редактирование текста (FineReader выполняет распознавание текста не на все 100% ).

В заключение этого раздела приветем код HTML-страницы:

TheWebProduction

Result of CGI-Java programming:


Copyright (c) 2002 TheWebProduction


Последний HTML-код показывает независимость CG-Java программы от дизайна (Рис.3).

§4. CGI-Java программирование.


Рис. 3.

§5. CGI-Flash программирование.

Появление технологии Flash компании Macromedia в значительной степени преобразило мир Web. Flash, без особой шумихи (что было свойственно распространению в Web языка Java), быстро занял свою нишу и эволюционировал от внешних plug-in к встроенным в броузеры IE и NN и является в настоящее время полноценной частью инструментов Web-дизайна и Web-программирования. Применение Macromedia Flash избавляет от проблемы совместимости между броузерами, Flash одинаково работает как в IE, так и в NN. В Macromedia Flash 5 применяется специальный событийно-управляемый язык, который поддерживает условные переходы, циклы, массивы, функции и классы, которые можно наследовать. Процедуры ввода-вывода данных, как мы увидим ниже, могут быть реализованы с помошью всего лишь одного оператора этого языка.
Задача.
Создать в статической HTML-странице on-line котировщик валюты EUR/USD рынка Forex и обеспечить запрос на обновление информации через каждые N секунд без перезагрузки HTML-страницы. В качестве ресурса котировок валют использовать ресурс партнерского сервера (www.my_partner.com).
Решение.
Создать файл Flash, обеспечивающий прием информации от сценария CGI. CGI-сценарий должен создавать сокет и устанавливать соединение для приема информации от удаленного сервера. Этот сервер содержит ресурс котировок валют рынка Forex (ресурс обновляется каждые 5 секунд). Из полученного ресурса (HTML-страницы) извлечь численные значения спроса (bid) и предложения (ask) EUR/USD. Полученные значения передать в поток вывода. Использовать элемент OBJECT для включения Flash в статическую HTML-страницу:
TheWebProduction

CGI-Flash programming:


Any text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text .......
Copyriht © 2002 TheWebProduction


Как мы видим (рис.6), файл Flash finan.swf выводит в HTML-страницу информационную вставку и идеология разделения CGI-программы и дизайна не нарушена.

§5. CGI-Flash программирование.


Рис. 4.

Программа CGI показана ниже:

#!/usr/bin/perl use IO::Socket; print "Pragma: no-cache\nCache-control: no-cache\nContent-type: text/plain\n"; $port="80"; my $host="www.my_forex.com"; my $file; $valuta="EURUSD"; $file = "/rate/simulation.html"; socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp')); $iaddr = inet_aton($host); $paddr = sockaddr_in($port, $iaddr); connect(SOCK, $paddr); send (SOCK, "GET $file HTTP/1.0\nHOST:$host\n\n", 0); @data=; close(SOCK); $answer = join("",@data); ($etc,$iter1)=split(/$valuta/,$answer); $met=''; @iter2=split(/$met/,$iter1); @iter3=split(/>/,$iter2[0]); @iter4=split(/>/,$iter2[1]); ($sec, $min, $hour, $day, $month, $year, $wday, $yday, $isdst) = gmtime(time); if($sec < 10){$sec="0".$sec;} if($min < 10){$min="0".$min;} if($hour < 10){$hour="0".$hour;} $my_time=$hour.":".$min.":".$sec; print "\nmy_bid=".$iter3[3].'&my_ask='.$iter4[3].'&my_time='.$my_time; exit;

Функция socket() создает сокет, тип которого определен как потоковый - SOCK_STREAM. После конвертации имени сокета (более удобного представления для сервера), создается соединение с помощью функции connect( ). Предварительно с помощью переменных $host и $file задаются имя сервера и имя ресурса, соответственно. Для передачи данных через сокет используется функция send( ), а для приема данных через сокет - известная процедура: @data=. После закрытия сокета идет разбор принятых данных ресурса simulation.html:

Rates My Forex Group
RateBid AskTime
EURUSD 0.9807 0.9812 07/31/02 11:03:04
USDJPY 119.65 119.70 07/31/02 11:03:04
GBPUSD 1.5628 1.5633 07/31/02 11:03:54
USDCHF 1.4815 1.4820 07/31/02 11:03:52



Для разбора HTML- кода используется образ EURUSD, который задается переменной $valuta. Данные относящиеся к спросу (bid) помещаются в переменную $iter3[3], а данные относящиеся к предложению (ask) в переменную $iter4[3]. В конце программы фиксируется местное время и заносится в переменную $my_time. И наконец создается стандартный вывод, эмулирующий метод POST протокола HTTP (необходимо для Flash).

Теперь обратимся к Flash. Прежде всего создадим форму с тремя полями ввода - рис.5.

§5. CGI-Flash программирование.


Рис. 5.

Создадим два слоя и растянем их на 20 кадров. При скорости кадров 8 fps, полное время цикла из 20 кадров составит 2.5 секунды, т.е. этот промежуток времени будет равен половине времени обновления ресурса курса валют. Затем в оконе Text Options (рис.6) зададим имя текстовой переменной Variable равой my_bid.

§5. CGI-Flash программирование.


Рис. 6.

Аналогичную процедуру выполним для создания имен двух других текстовых переменных: my_ask и my-time. Для верхнего слоя (рис.5) откроем окно Frame Actions и займемся программированием:

loadVariablesNum ("http://www.mycgi.com/cgi-bin/java_applet/finan.cgi", 0);

Процедура loadVariablesNum загрузит CGI-ресурс, а полученные данные от CGI-скрипта разместит в текстовых переменных: my_bid, my_ask и my-time, соответственно.

Аннотация.

Настоящая публикация посвещена ряду вопросов CGI-программирования на языке Perl, а именно нетипичным ситуациям генерации выходных данных в среде броузера. Рассматриваются варианты возврата выходных данных с помощью контейнерных HTML элементов: APPLET, SCRIPT, OBJECT, IFRAME (ILAYER для Netscape), а также элемента IMG. Представлены примеры программирования реальных ситуаций (выборка новостной информации из БД, защита copyright и защита от копирования, вывод курса валют on-line без перезагрузки страницы) с использованием Perl, JavaScript, Java, событийно-управляемого языка Flash 5. Показано, что активное взаимодействие сценариев CGI и программ на языках JavaScript, Java, скриптов Flash в значительной степени расширяет возможности представления и обработки выходной информации, позволяет автоматизировать процессы вывода и сократить накладные расходы. Особое внимание уделено разделению CGI программы и HTML-кодирования, что позволяет дизайнерам модифицировать HTML-страницы независимо от CGI-скрипта и независимо от CGI-программиста.

Действия помещаем в Command

Для того, чтобы было легко раздать работу по написанию Ваших контроллеров нескольким программистам, я предлагаю Вам использовать шаблон проектирования под названием Command. Определяем интерфейс MyCommand и наследуем от него все наши команды (контроллеры). Таким образом, Вы налагаете на всех программистов обязанность следовать единому стилю. Шаблон Command изображен на рис.5
Действия помещаем в Command

Рис.5
Некоторые программисты пишут один большой Servlet и каждый контроллер определяют просто методом, как делает, например, один мой знакомый. У него получается большой единственный файл с кучей методов (каждый метод - это контроллер). А теперь давайте представим, что ему не хватает времени реализовать все контроллеры. Мы даем в помощь 3-х программистов и они все вместе начинают редактировать один его большой Servlet. Думаю, ничего хорошего из этого не выйдет. Давно уже пора понять, что времена "сам все напишу" прошли, что проекты делаются группой разработчиков и каждый разработчик должен писать не как ему хочется, а как нужно, с учетом потребностей всей группы программистов.

Используем Mapper

На самом деле, выше описанный диспетчер у нас получился не очень хороший. Недостаток на лицо. В случае изменения сценария взаимодействия с Web, необходимо лезть руками в Dispatcher и исправлять код. Я привел такую реализацию Dispatcher'а только для того, чтобы продемонстрировать логику его работы. Кстати, вышеупомянутый знакомый реализует диспетчер именно так.
Существует более изящное решение этой проблемы. Мне его подсказал Антон Патрушев (mcgregor@mail.ru). Необходимо определить текстовой файл, в котором хранятся имена наших контроллеров, которые мы указываем в action, commit и rollback. И каждому имени контроллера указать имя класса, который его реализует. А потом воспользоваться java.lang.ClassLoader. ClassLoader позволяет загружать класс по имени и делать с него объект. Если думать дальше, то в такой конфигурационный файл можно не только зашить соответствия контроллер - класс, но и описание всего сценария. Так вот, механизм отображения контроллер - класс называется Mapper.

Используем MVC

Существует популярный шаблон проектирования Model - View - Controller. Идея его заключается в следующем. Существует модель (Model) предметной области приложения, например структура базы данных. Существуют способы отображения (View) этой модели для пользователя, например HTML-странички. Существует набор действий (Controller), которые производятся с моделью (например, изменение состояния модели, т.е. содержимого БД, или генерация HTML-кода). Обычно View реагирует на внешние события, такие как метод Post или Get из браузера пользователя, и на каждое такое внешнее воздействие определяет свою команду (Controller). На самом деле MVC немного сложнее, но в нашем случае мы возьмем только выше описанные идеи. Шаблон MVC изображен на рис.1
Используем MVC

Рис.1
В нашем случае событие model_was_changed не может произойти, так как события между броузером и CGI-скриптом (Controller) идут только в сторону CGI-скрипта, т.е. CGI-скрипт не может в любой момент времени сообщить пользователю о том, что была изменена модель. Не работающая связь изображена на рис.2.
Используем MVC

Рис.2

Оптимизация процесса разработки

Автор: Евгений Игумнов
Геокад Плюс (Новосибирск)

Домашняя страничка:

Редактор:

Версия текста: 1.0 (10.05.2001)












  • Получаем Content, трансформируя XML

    Иногда необходимо иметь гибкий механизм смены способов представления информации. Для этого необходимо разделить способ представления информации и саму информацию. Для этого удобно использовать XML. Давайте представим, что Ваши сервлеты будут формировать не HTML, а XML, который вы жёстко декларируете. И этот XML будете обрабатываться другим скриптом, который содержит в себе правила преобразования вашего XML в HTML-код. И если Вам захочется поменять внешний вид своего сайта, Вам будет достаточно изменить правила отображения XML в HTML. Вы также сможете написать свой апплет, который будет общаться с сервером, получать от него XML и отображать его другим способом в браузере клиента. Другими словами, Вы формируете один и тот же XML, а он может представляться в HTML и в виде апплета. Положительные дополнительные эффекты, которые приносит использование XML, не помешаютсистеме, которая имеет разнородные клиентские приложения.

    Применяем Decorator

    Программы желательно писать так, чтобы не повторять один и тот же код, а повторно его использовать. И потом можно будет менять код в одном месте, а не во многих, как чаще всего происходит. Предлагаю применять шаблон Decorator. Диаграмма классов изображена на рис.7.
    Применяем Decorator

    Рис.7
    Идея заключается в том, что у вас есть какой-то визуализируемый элемент и вы хотите его дополнить некоторым набором расширений. Причём в разных ситуациях вы планируете навешивать различные "фенечки". Обычно приводят пример кнопочки, потом к ней добавляют перечеркивающую линию, а затем мигание. И в случае необходимости мигание или линию можно убрать.
    Идея заключается в следующем. Определяем интерфейс View с методом, который рисует наш компонент. От интерфейса наследуем наш компонент и его декораторы. И в конструкторах определяем возможность передачи ссылки на вложенный объект, который надо декорировать, так сказать. Получается следующая картина на рис.8.
    Применяем Decorator

    Рис.8
    Сначала объясню, что делает каждый декоратор. Border - просто рисует обрамление вокруг любого элемента. WindowHead - рисует подобие заголовка как у всех приложений Windows с кнопочками свернуть и закрыть. NewsHead - рисует серенькое поле, а в нем пишет название новости. NewsBody - рисует саму новость. Другими словами, заходите на сайт, а там новости оформлены в виде Windows окошек. Некоторые новостные сайты такое практикуют. А теперь вернемся к рис.8. Как видно b1 обрамляет wh1, wh1 содержит b2, b2 обрамляет nh, nh содержит b3, а b3 обрамляет nb. Как работает весь этот механизм изображено на рис.9.
    Применяем Decorator

    Рис.9
    Приведу пример кода: Border b1= new Border( new WindowsHead( new Border ( new NewsHead ( new Border ( new NewsBody() ) ) ) )); b1.doPost(...);

    Пример простого сценария

    Теперь перейдем от теории к практике. Разберем маленький пример, используя введенные выше обозначения. Пример изображен на рис.4.
    Пример простого сценария

    Рис.4
    Это пример простой гостевой книги. Гостевая книга представляет собою один скрипт. Ему обычно передаются параметры action, commit и rollback. В action указывается контроллер, который необходимо запустить. Если указан параметр commit, то в случае успешного выполнения контроллера в action, следующим запускается контроллер в commit, а если порождается исключение при выполнении контроллера из action, то запускается контроллер, указанный в rollback. Если action не указан, то выполняется контроллер, определенный по умолчанию. Используя такой подход, Вы можете заранее нарисовать весь сценарий взаимодействия пользователя с web и по полученным кружочкам понять, сколько Вам необходимо написать контроллеров для своей задачи.

    Проблема

    По моим наблюдениям, основная масса программистов пишут CGI-скрипты как попало, и эффективность такой системы их совсем не заботит - "лишь бы работало". И чаще всего программист начинает писать скрипты, даже точно не зная, что у него в конечном итоге получится. Например, если Вы попросите добавить или изменить имеющуюся web-систему, то программист, скорее всего, будет просто несчастлив, так как ему придется перерывать гору исходников, в которых "чёрт ногу сломит". К тому же возникает проблема при распараллеливании работ по созданию сайта между несколькими программистами. К сожалению,раньше и я был в числе таких горе-программистов, но теперь предпочитаю сначала все спроектировать, а уже потом браться за реализацию (как в одиночку, так и командой).
    В данной статье я излагаю свой опыт и демонстрирую примеры на технологии Servlet, но описанные здесь идеи легко можно использовать в Perl или PHP.

    Собираем Content с помощью Composite

    Шаблон Composite похож на Decorator, можно сказать, что Decorator является частным случаем Composite . У декоратора чистая рекурсивная вложенность, а у композиции присутствует как рекурсия, так и очередь. Даже можно сказать, что в компоненты, построенные по шаблону композиции можно добавлять несколько других подобных компонентов. Диаграмма шаблона Composite изображена на рис.10.
    Собираем Content с помощью Composite

    Рис.10
    В шаблоне Decorator мы посредством конструктора передавали ссылку на вложенный объект, а здесь мы используем для этого метода AddComponent. Естественно, не все компоненты могут добавлять в себя другие компоненты.
    Предположим, что у нас есть набор страничек со стандартным комплектом: баннер сверху, ниже заголовок, слева меню с кнопочками навигации, снизу служебная информация с датой последнего изменения страницы. В виде объектов это можно представить так.
    Собираем Content с помощью Composite

    Рис.11
    Как видно, у нас есть один компонент (назовем его контейнер), в котором по очереди хранятся другие вложенные компоненты. Механизм работы изображен на рис. 12.
    Собираем Content с помощью Composite

    Рис.12
    Создаём наш контейнер, потом помещаем туда нужные элементы и заставляем его нарисовать себя, т.е. сформировать HTML-код.

    В основу берем Dispatcher

    Желательно иметь один класс, который бы занимался только тем, что управлял сценарием работы нашего web-сайта. Пример работы такого диспетчера изображен на рис.6.
    В основу берем Dispatcher

    Рис.6
    Как видно из рисунка, Servlet передает управление диспетчеру, а тот, в свою очередь, анализирует содержимое переменной action и вызывает соответствующий контроллер. Если после выполнения контроллера порождается исключение, то он вызывает контроллер, определенный в rollback, а в случае успешного выполнения контроллера из action диспетчер вызывает контроллер, определенный в commit.

    Вид отделяем от действий

    Самая существенная ошибка, которую совершают программисты - это совмещение кода, изменяющего базу данных и генерация HTML. Предлагаю это разделить и назвать соответствующие программные модули контроллером взаимодействия с БД и котроллером генерации HTML. Ниже показано это разделение.
    Вид отделяем от действий

    Рис.3
    Первый овал - это код, который меняет состояние таблиц БД, т.е. Controller. Второй генерирует HTML, т.е. опять Controller. Третий овал с черточкой - это представление HTML в браузере на стороне пользователя, т.е. View. А Model является структурой БД, как и говорилось выше.

    и не претендую на то,

    Я постарался поделиться с Вами своим опытом и не претендую на то, что идеи, изложенные выше, являются самыми-самыми, но думаю, что знание, которое Вы получили при прочтении моей статьи заставят Вас обдумать свои взгляды на процесс разработки и проектирования сценариев взаимодействия с Web-компонентами.
    Copyright (C) 2001 Eugene Igumnov. Все права защищены.

    Обзор статьи "Extended Relational DBMSs: The Technology, Part 1" DBMS, vol.10, N 6, June 1997,

    Judith R. Davis, principal with InfoIT Inc.,

    Обзор подготовлен С. Кузнецовым,
    Развивающийся интерес к Internet и World Wide Web как платформам
    приложений оказал значительное влияние на рынок реляционных
    систем управления базами данных (РСУБД). Поначалу казалось, что
    феномен Web сузил профиль приложений РСУБД. Производители стали
    обращать большее внимание на параллельное выполнение операций над
    базами данных, склады данных и репликацию данных. Однако
    пользователи, разрабатывающие Internet/Web-приложения быстро
    осознали потребность в масштабируемой и надежной среде хранения,
    манипулирования и управления динамическими мультимедиа данными и
    другими сложными типами данных. Для связывания СУБД с приложением
    Web посредством Web-сервера требуется эффективная поддержка
    трехзвенной архитектуры.
    Еще до прихода Web большинство производителей РСУБД работало над
    расширением возможностей СУБД для поддержки более сложных данных
    и приложений; появление Web приложений потребовало
    незамедлительного решения этой задачи.
    В этой статье рассматриваются требования приложений, которые
    влияют на расширение функций РСУБД для управления сложными
    данными. Во второй части статьи, которая должна появиться в июле,
    будут обсуждаться текущие результаты и планы пяти ведущих
    производителей РСУБД: Informix Software Inc., IBM Corp.,
    Microsoft Corp., Oracle Corp. и Sybase Inc.
    Пользователи всегда хотели иметь унифицированный доступ ко всем
    данным организации, т.е. иметь возможность интегрированного
    поиска данных. Однако РСУБД по-настоящему понимали только сильно
    структурированные алфавитно-цифровые данные. Текст и картинки
    можно было хранить в виде BLOB'ов (Binary Large OBjects), но сама
    РСУБД ничего не знала о содержимом этих BLOB'ов. Пользователям
    приходилось использовать специализированные серверы или
    встраивать соответствующую обработку в логику самого приложения.
    Кроме того, усложнилась организация корпоративных информационных
    систем, в которых стремятся интегрировать функции оперативной

    системы, склада данных и Web-ориентированной системы.

    Для удовлетворения подобных требований многие организации ищут

    единую платформу баз данных, обладающую свойствами

    масштабируемости, поддержки целостности данных, учета

    бизнес-правил и т.д., которые применимы к сложным данным так же,

    как и к традиционным. Поскольку РСУБД широко используются в

    традиционных приложениях, имеет смысл расширить их возможности

    для работы с более широким набором типов данных, связанной с

    приложениями семантикой, осмысленным связыванием сложных данных.

    Такие расширенные РСУБД называются "расширенными реляционными"

    или "объектно-реляционными" (поскольку теперь РСУБД может

    понимать "богатые типы данных" или "объекты", которые

    представляют сложные внутренние структуры, атрибуты, поведение и

    требуют новых методов поиска). Общими терминами для продуктов

    этого класса являются "универсальный сервер" или "универсальная

    база данных".

    Другим фактором, стимулирующим усилия по расширению РСУБД,

    является извечное желание повысить продуктивность разработчиков.

    Одним из связанных с этим аспектов является потребность в едином

    API (Application Programming Interface) для доступа ко всем

    данным. Текущий вариант распространенного языка SQL не очень

    подходит на роль такого API, и в мире объектно-реляционных СУБД

    питают надежды использовать SQL-3. С другой стороны, компания

    Microsoft хотела бы видеть в качестве универсального API свой

    общий объектный интерфейс OLE DB. Другим аспектом, связанным с

    продуктивностью разработки, является погружение методов

    моделирования объектов в сам сервер баз данных. Эти методы

    включают возможность инкапсуляции данных и связанных с ними

    методов в виде объектов и повторного использования кода на основе

    наследования и полиморфизма.

    По этому поводу следует сделать два замечания. Во-первых, многие

    путают объекты с типами данных. Объекты инкапсулируют и данные, и

    методы. Добавление новых типов данных к РСУБД - это только один


    шаг к поддержке истинных объектов. Требуется возможность

    определения новых методов и привязки этих методов к

    соответствующим типам данных. Во-вторых, нужно различать

    объектно-реляционный и объектный подходы к организации СУБД.

    Объектно-реляционные СУБД поддерживают некоторые объектные

    свойства, но пока еще не обеспечивают возможности инкапсуляции и

    наследования на том же уровне, который свойственен объектным

    СУБД. Кроме того, маловероятно, что объектно-реляционные СУБД

    будут полностью поддерживать возможности явной навигации по

    указателям и тесной интеграции с языками

    объектно-ориентированного программирования. Видимо,

    объектно-реляционные продукты будут развиваться с тем, чтобы

    позволить использовать преимущества объектного подхода насколько

    это возможно, но объектные свойства будут реализовываться

    по-своему, на основе табличных структур.

    Для создания расширенной архитектуры управления данными

    используются три основных подхода: подход универсального сервера,

    подход промежуточного программного обеспечения и подход

    объектного уровня. Эти подходы не являются взаимно исключающими,

    и при построении полностью расширяемой среды управления данными

    следует принимать во внимание все три компонента.

    Между этими подходами имеется два ключевых отличия. Первое

    определяется тем, как и где происходит управление данными.

    Являются ли данные тесно интегрированными и управляемыми одним

    сервером баз данных, или же данные слабо интегрированы и

    управляются несколькими серверами? Второе отличие связано с тем,

    где происходит оптимизация запросов и насколько она эффективна.

    Выполняется ли оптимизация сервером баз данных (подход

    универсального сервера) или же промежуточным программным

    обеспечением (подход OLE DB)? Хорошая оптимизация особенно важна

    в среде, в которой данные могут храниться в одной таблице, в

    нескольких таблицах, в нескольких базах данных или во внешних

    файлах.

    При использовании подхода "универсального сервера" возможности

    РСУБД расширяются для понимания и хранения сложных данных и


    управления ими на уровне самого сервера. Этот подход реализуют

    Informix, IBM и Oracle (Informix Universal Server, IBM DB2

    Universal Server и Oracle 8). В будущем компания Sybase

    собирается добавить ограниченные возможности работы со сложными

    данными в Sybase SQL Server. Подход предполагает, что все данные

    физически хранятся в базе данных.

    Существует идея "расширенного универсального сервера", которая

    основывается на том, что могут быть вполне разумные причины к

    тому, чтобы не хранить все данные в базе данных, непосредственно

    управляемой СУБД (например, соображения эффективности). Тогда

    СУБД должна быть в состоянии обеспечить эффективный доступ к

    данным, хранимым во внешних файлах. Данные большого объема

    (например, картинки) могут храниться во внешних файлах, а в

    столбце таблицы базы данных останется только указатель. Конечно,

    от СУБД потребуется дополнительная возможность поддержки

    целостности внешних данных. Пока соответствующие средства

    планирует разработать только IBM, но ожидается, что в ближайшие

    12 месяцев этим заинтересуются и другие производители.

    Борцы за чистоту объектного подхода критикуют расширенный

    реляционный подход за то, что СУБД должна производить

    декомпозицию объектов в реляционные таблицы для их хранения и

    снова собирать объекты для предъявления их пользователям.

    Производители расширенных реляционных систем очень заботятся об

    эффективности и стараются по мере возможности избегать

    соединений. Реально, только опыт использования расширенных РСУБД

    покажет, насколько успешно можно использовать реляционную модель

    для поддержки сложных данных.

    Другим подходом является использование промежуточного

    программного обеспечения для координации и выполнения заявок

    между несколькими разнородными серверами (РСУБД, сервер

    текстового поиска, система хранения картинок и плоские файлы).

    Промежуточное программное обеспечение обеспечивает единообразное

    представление данных, производит глобальную оптимизацию запросов

    и выполняет глобальное управление транзакциями. В расширенной


    архитектуре управления данными используются два типа

    промежуточного программного обеспечения. В обоих типах

    используется SQL и обеспечиваются драйверы для доступа к каждому

    поддерживаемому серверу. К первому типу относится промежуточное

    программное обеспечения баз данных (например, IBM DataJoiner и

    Sybase OmniConnect), поддерживающее интегрированный доступ к

    неоднородным данным.

    С другой стороны, OLE DB и DCOM компании Microsoft и другие

    брокеры объектных заявок представляют собой промежуточное

    программное обеспечение приложений. OLE DB разбивает функции СУБД

    на компоненты, которые могут выполняться в пространстве

    промежуточного программного обеспечения или в операционной

    системе. OLE DB будет внутреннем компонентом операционных систем

    и серверов Microsoft, что порождает вопрос: а нужны ли будут

    после этого СУБД как отдельные продукты? Вопрос станет особенно

    интересным, если SQL-3 не оправдает возлагаемых на него надежд.

    Успех подхода промежуточного программного обеспечения будет

    определяться несколькими факторами: уровнем интеграции

    компонентов, умением эффективного использовать возможности

    поддерживаемых серверов, эффективностью взаимодействий между

    компонентами и т.д.

    Подход объектного уровня обеспечивает интегрированное объектное

    представление и соответствующую функциональность на уровне

    приложений. Подход включает управление кэшем на стороне клиента,

    навигацию в пространстве объектов, локальное выполнение функций и

    локальную оптимизацию запросов. Конечно, для использования этого

    подхода более пригодны объектные СУБД. При применении же РСУБД

    должна иметься возможность отображения объектов приложения в

    объекты базы данных, чтобы реляционные данные могли быть

    материализованы в виде "родных" объектов Си, Си++, Java и т.д.

    Преимуществом подхода является более тесная интеграция между

    менеджером данных и языком разработки приложений и потенциально

    лучшая производительность. IBM планирует обратиться к подходу

    объектного уровня в своем проекте объектной среды разработки


    клиентских приложений. Некоторые черты этого подхода можно найти

    в Oracle 8. Конечно же, объектный уровень представления данных

    обеспечивает и OLE DB.

    Рассмотрим коротко, каким образом производители расширенных РСУБД

    удовлетворяют потребности пользователей. Многие, хотя и не все

    возможности, включены в проект стандарта SQL-3 (см. ниже).

    Расширяемая системы типов. Расширенные РСУБД должны поддерживать

    определяемые пользователями типы данных (UDT - User-Defined

    Types) на уровне столбцов и строк. UDT уровня столбцов могут быть

    уточненными или абстрактными. Уточненные типы расширяют

    существующие базовые типы данных столбцов. В строго

    типизированной системе пользователю не разрешается прямо

    сравнивать значения типов с разными именами даже если у них общие

    базовый тип и длина. Абстрактные типы данных более сложные, со

    специальными внутренней структурой и атрибутами. Внутренняя

    структура абстрактного типа данных скрывается от пользователя;

    выборка данных и манипулирование ими могут производиться на

    основе набора внешних атрибутов и функций. Абстрактные типы

    данных определяются с использованием языка SQL или традиционных

    языков программирования.

    Строчный тип описывает строку целиком или набор столбцов таблицы,

    что дает возможность представить иерархические "сущности" и

    идентифицировать связанные столбцы. Ссылочные типы могут

    определить связи между строчными типами и идентифицировать строку

    во всей базе данных. Ссылки позволяют пользователям заменить в

    запросах определения сложных соединений на намного более простые

    выражения пути. Дополнительные возможности получает оптимизатор

    запросов.

    Коллекции являются конструкторами типов, используемыми для

    определения коллекций других типов - массивов, списков и

    множеств. С использованием коллекций можно хранить несколько

    значений в одном столбце таблицы; в частности, таблица может

    содержать другую таблицу.

    Важным аспектом является наследование, при котором подтипы

    наследуют атрибуты и поведение своих супертипов.


    Определяемые пользователями функции (UDF - User-Defined

    Functions) служат для определения методов для манипулирования

    данными и являются важным дополнением к UDT. Расширенная РСУБД

    должна обладать существенной гибкостью, позволяя UDF возвращать

    сложные значения (например, таблицы), которыми потом можно

    манипулировать. Пользователи должны иметь возможность выбора

    между безопасностью и эффективностью при выполнении функций. Для

    упрощения процесса разработки приложений должна допускаться

    перегрузка имен функций.

    Структуры индексов. В традиционных РСУБД для ускорения доступа к

    скалярным данным используются индексы со структурой B-дерева. При

    наличии возможности определять более сложные типы данных для

    эффективного доступа к данным требуются специализированные

    индексные структуры. В некоторых расширенных РСУБД начинают

    применять дополнительные типы индексов, например, R-деревья для

    быстрого доступа к двух- или трехмерным данным. Кроме того,

    допускается применять индекс к результату функции. Максимальный

    уровень гибкости обеспечивается механизмом подключения любой

    определенной пользователем индексной структуры.

    Оптимизатор. Хороший оптимизатор является основой эффективности

    РСУБД. Возможности оптимизаторов запросов должны быть расширены с

    целью обеспечения эффективной работы с UDT с учетом новых

    индексных структур, новых способов преобразования запросов,

    навигации по ссылкам.

    Другие расширения. К числу важных расширений относятся поддержка

    хранения больших объектов в базе данных или во внешних файлах,

    возможность применения бизнес-правил и ограничений целостности к

    новым типам данных, поддержка рекурсивных запросов, расширенная

    языковая поддержка на стороне сервера. Последний аспект является

    залогом гибкости и переносимости. Расширенная РСУБД должна

    поддерживать стандарт SQL-3 (находящийся пока в стадии проекта),

    а также дополнительные языки для написания UDT и хранимых

    процедур (3GL и Java). К сожалению, в стандарте SQL-3 не

    рассматриваются некоторые вопросы расширяемости, поэтому такие


    важные аспекты, как информирование оптимизатора о UDT и новые

    индексные структуры будут разными в разных продуктах. Видимо,

    потребуются дополнительные стандарты.

    Производители расширенных РСУБД должны обратить внимание на два

    фактора, влияющих на привлечение покупателей к новым продуктам.

    Первый фактор состоит в предоставлении солидного набора

    предопределенных расширений в качестве строительных блоков при

    разработке приложений (так поступает Informix со своим набором

    DataBlades). Наиболее гибким решением является предоставление

    встроенных расширений совместно с поддержкой (возможно,

    конкурирующих) расширений, поставляемых третьими компаниями.

    Второй фактор связан с тем, что покупатели хотят получить

    расширенные возможности, но не хотят потерять то, что уже имеют

    (например, хорошую производительность при выполнении существующих

    приложений). Требуется интегрировать расширенные возможности с

    параллельной обработкой, средствами восстановления, поддержкой

    целостности данных и репликацией данных. В некоторых случаях это

    возможно.

    Статья написана на основе отчета компании InfoIT

    "Object-Relational DBMSs". Полный текст доступен на сервере

    Сводка объектно-реляционных расширений

    Свойство Включено ли в SQL-3
    Расширенная система типов Да
    Поддержка строгой типизации Да
    Поддержка иерархии типов и наследования Да
    Поддержка репликации для UDT Нет
    Определенные пользователем функции Да
    Перегрузка функций Да
    Нахождение функции по нескольким атрибутам Да
    Расширяемая система индексации Нет
    Расширяемый оптимизатор запросов Нет
    Поддержка больших объектов Да
    Поддержка внешних данных Нет
    Интегрированный поиск Да
    Расширенная языковая поддержка Да
    SQL-3 и SQL/Мультимедиа Да
    3GL Да для хранимых процедур
    4GL Нет
    Java Нет
    Объектно-ориентированные языки Нет
    Доступные предопределенные расширения Нет
    Средства для добавления расширений (API, инструменты) Нет
    Язык приложений, поддерживающий расширения Нет
    Поддержка управления системой для внесения расширений Нет

    Чего мы не сделали

    Вот и все. Служба WebMail готова (см. рисунок). Как несложно заметить, мы всего лишь соединили готовые части - все наши шесть файлов не насчитывают и двух сотен строк - и получили почти настоящую почтовую программу. Конечно, с ней связаны и некоторые проблемы. Например, такие.
  • Что будет, если с помощью нашей WebMail попробовать читать почту одновременно с двух компьютеров? При удалении письма на одном компьютере нумерация "съедет", и на втором начнут происходить весьма неприятные вещи.

  • Как уже было замечено, на сервере с не полностью контролируемым содержанием использование нашей программы может привести к попаданию паролей в нечистые руки.

  • Нет поддержки кодировок, отличных от KOI8-R, для почты (при том, что для Web-браузера благодаря Russian Apache поддерживаются все распространенные кодировки).

  • Нет поддержки вложений.

  • Нет записной книжки.

  • Нет поддержки папок.

  • Впрочем, мы и не ставили себе целью создать программу, способную сравниться с Eudora или Pegasus (но, кстати, программа Imap webMail Program - см. - очень близка к этому).
    Сделаем еще несколько замечаний по поводу возможных решений первой и второй проблемы. Эти проблемы связаны с обеспечением безопасности, а значит, требуют особого внимания. Для решения первой проблемы можно передавать в дополнение к номеру письма еще и идентификатор сообщения (MessageID); это, однако, повлечет существенное усложнение программы, так как нужно будет организовать подробное "разбирательство" в случае несовпадения идентификаторов, а также корректное обновление списка писем.
    Вторая проблема решается путем добавления еще одной формы на входе и передачи имени пользователя и пароля от сценария к сценарию через URL - так, как сейчас передается номер письма. Но тогда пароль будет появляться в адресной строке браузера. С этим можно бороться двумя способами - либо зашифровывая пароль перед посылкой и расшифровывая при получении, либо создав еще два фрейма: первый не используется никак (или в нем размещается реклама, что, в общем, то же самое), во втором происходит вся работа. Можно и скомбинировать названные способы. Дерзайте!
    ЛИСТИНГ 1 Файл counter.php3 (счетчик числа посещений Web-страницы)

    Число посещениий:



    ЛИСТИНГ 2 Файл index.php3 (идентификация пользователя, установление контакта с почтовым сервером и создание набора фреймов для вывода списка писем и текста текущего письма)


    Authorization Required\n"; exit; else: if(!($imap_stream=@imap_open("{127.0.0.1:143}Inbox","$PHP_ AUTH_USER","$PHP_AUTH_PW",OP_READONLY))): Header( "WWW-Authenticate: Basic realm=\"$REALM\""); Header( "HTTP/1.0 401 Auth Required"); echo "

    Authorization Required

    \n"; exit; else: imap_close($imap_stream); ?> <body> Sorry, but your browser does not support frames...<br> </body>

    ЛИСТИНГ 3 Файл top.php3 (формирование списка писем)

    "; endfor; imap_close($imap_stream); ?>
    NNSubjectFromDate
    Nmsgs;$i++): $header=imap_header($imap_stream,$i,300,300,0); echo "
    ".$i. "".$header->Subject. "".$header->fromaddress. "".$header->Date. "



    ЛИСТИНГ 4 Файл main.php3 ( проверка наличия писем и вывод кнопок)

    New message =1)):?>| > Reply| target= _top>Delete

    =1)): $imap_stream=imap_open("{127.0.0.1:143}Inbox","$PHP_AUTH_USER", "$PHP_AUTH_PW",OP_READONLY); $inbox=imap_mailboxmsginfo($imap_stream); $body=imap_fetchbody($imap_stream,$mail,1,0); print(nl2br(eregi_replace( "(http|https|ftp)://([-=%_ a-zA-Z0-9./~?:]+)", "\\1://\\2", htmlspecialchars($body)))); imap_close($imap_stream); endif; ?>

    ЛИСТИНГ 5 Файл mail.php3 (создание письма)

    =1)): $imap_stream=imap_open("{127.0.0.1:143}Inbox","$PHP_AUTH_USER", "$PHP_AUTH_PW",OP_READONLY); $header=imap_header($imap_stream,$mail,300,300,0); endif; ?>
    Your name :
    From : " SIZE=60 maxlength=70>
    Subject : Subject); endif;?>" SIZE=60 maxlength=70>
    To : fromaddress); endif;?>" SIZE=60 maxlength=70>
    Message body :

    =1)):imap_close($imap_stream);endif; ?>


    ЛИСТИНГ 6 Файл del.php (удаление письма)

    =1)): $imap_stream=imap_open("{127.0.0.1:143}Inbox","$PHP_AUTH_USER", "$PHP_AUTH_PW",OP_READONLY); $header=imap_header($imap_stream,$mail,300,300,0); endif; ?>
    Your name :
    From : " SIZE=60 maxlength=70>
    Subject : Subject); endif;?>" SIZE=60 maxlength=70>
    To : fromaddress); endif;?>" SIZE=60 maxlength=70>
    Message body :

    =1)):imap_close($imap_stream);endif; ?>


    ЛИСТИНГ 7 Файл send.php3 (отправка письма)

    \nContent-Type: text/plain; charset=KOI8-R"); ?> Message was succesfully sent.

    1 Сценарий намеренно предельно упрощен: он не заботится о блокировке файлов, не обрабатывает ошибки записи на диск и т. п. Заметим, что наш счетчик, в отличие от большинства других, написан без использования тега . Это позволяет сократить трафик и решает проблему неграфических браузеров (например, браузеров для слепых), но может создать проблемы, если нам нужен единый счетчик для нескольких "зеркал" основного узла. "Традиционная" реализация счетчика в PHP также не представляет проблемы, поскольку он имеет множество функций для порождения GIF-файлов (включая даже получение красивых надписей, выполненных шрифтами TrueType).

    2 Число 401 взято не с потолка, а из описания протокола HTTP.

    3 К сожалению, если в каком-либо другом подкаталоге указать realm="Web mail", то находящиеся в нем сценарии также получат соответствующую информацию. Поэтому данный метод неприемлем, если вы не можете контролировать содержимое всего узла.

    В конце статьи мы обсудим, как обойти это ограничение.

    Другие файлы

    Теперь нам нужно создать файлы top.php3 и main.php3, на которые имеются ссылки в index.php3. Начнем с top.php3 (листинг 3). В нем мы строим таблицу, в которой каждая строка соответствует одному письму и содержит его порядковый номер, тему, имя (адрес) отправителя и дату отправки. Вся информация извлекается из соответствующих полей заголовка письма.
    При оформлении ссылок (HREF) мы должны не забыть передать номер письма (как при работе с обычным CGI-сценарием). Файл main.php3 (листинг 4) проверяет, установлена ли переменная $mail. Если нет, то выводится только надпись New message с соответствующей ссылкой, а если да, добавляются еще две ссылки - Reply и Delete. Кроме того, ссылки, встречающиеся в тексте письма, заменяются на ссылки HTML. Файлы mail.php3, del.php3 и send.php3 (листинги 5-7) устроены предельно просто, однако, так как файлы del.php3 и send.php3 содержат вызовы функции Header(), они начинаются со скобки

    HotMail своими руками, или Что может PHP

    Виктор Хименко

    МИР ПК #02/99
    В этой статье я хотел бы продемонстрировать, что создание работоспособных и полезных динамических Web-узлов вовсе не такая сложная задача, как может показаться при чтении текстов, описывающих интерфейс CGI. На самом деле подобная работа (по крайней мере, при использовании Linux) больше всего напоминает игру The Incredible Machines, где нужно кремнем высечь искру, чтобы свет упал на солнечную батарею, от которой срабатывает сверхсовременный компьютер. Здесь тоже все, как правило, собирается из компонентов, которые оказались под рукой.
    В качестве примера мы рассмотрим создание почтовой службы на Web-сервере - WebMail - на основе языка PHP3 с максимальным использованием готовых компонентов. За кадром останутся создание новых пользователей, их регистрация в базе данных и т. п. Наша почта, кроме того, не будет обрабатывать письма с вложениями и в кодировках, отличных от KOI8-R, поддерживать записные книжки и т. п. - все это лишь увеличило бы размеры сценариев, не дав ничего принципиально нового.

    Компоненты

    Вначале о "готовых компонентах". Я уже упомянул о том, что все описанное ниже рассчитано на использование Linux. Однако существующие дистрибутивы этой ОС настолько различаются по комплектации, что вряд ли удастся построить пример, который годился бы для любой версии. Поэтому я ограничусь версией, с которой лучше всего знаком, - KSI-Linux 2.0 (). Если у вас другой вариант Linux, например RedHat 5.2 или Debian 2.0, то вам, скорее всего, потребуется получить некоторые компоненты из Internet или перекомпилировать уже имеющиеся.
    Нам понадобятся Web-сервер Russian Apache 1.3.x rus/PL 27.4, PHP 3.0.6 с поддержкой протокола IMAP4r1 и почтовый сервер IMAP4r1. Благодаря Russian Apache мы будем, по крайней мере отчасти, избавлены от проблем с кодировками: независимо от кодировки, используемой клиентом, данные на сервер всегда будут посылаться в KOI8-R (это настройка Russian Apache по умолчанию). Сервер IMAP4r1 позволит нам манипулировать почтовым ящиком, не описывая в явной форме права доступа (связываясь с этим сервером, вы указываете свое имя и пароль, и он сам решает все проблемы с правами). И наконец, PHP 3.0.6 с поддержкой протокола IMAP4r1 даст возможность работать с сервером IMAP4r1, не реализуя самостоятельно соответствующий протокол, который весьма и весьма нетривиален.
    Здесь многие зададут вопрос: а почему не perl? Дело в том, что, хотя язык perl - стандартная "рабочая лошадка" для создания Web-узлов, у него есть определенные недостатки. Интерпретатор perl существенно сложнее интерпретатора PHP и требует для работы больше ресурсов компьютера. Кроме того, это не интерпретатор в точном смысле слова: он компилирует программу в специализированный псевдокод, который затем немедленно исполняется. Если в программе есть циклы, такой подход может дать существенный выигрыш, но если нет (как это часто бывает в простых сценариях для Web-сервера - например, во всей нашей реализации WebMail будет всего один цикл), накладные расходы не окупаются. Применение mod_perl - дополнительного модуля Apache - позволяет избежать многократной перекомпиляции сценариев, но при этом к памяти сервера предъявляются еще более высокие требования.

    Коротко о PHP

    Теперь несколько слов о том, что же такое PHP. Это интерпретируемый язык для создания активных Web-страниц. Программа на PHP, подобно тексту на JavaScript, VBScript или ASP, вставляется в HTML-файл. Начало и конец программы отмечаются специальными скобками . Текст вне этих скобок PHP не интерпретирует: он передается Web-браузеру "как есть". В листинге 1 приведена реализация на PHP "вечного" примера - счетчика. Как видите, это совершенно обычный HTML-файл, однако в том месте, где должно стоять количество посещений, стоит сценарий на PHP3, который в качестве результата своей работы выводит число посещений страницы.
    Синтаксис PHP основан на синтаксисе языков Си, Java и perl и довольно подробно описан в руководстве, которое входит в комплект поставки (его также можно взять на узле ). Способы заставить сервер правильно реагировать на HTML-файлы со вставками на PHP, вообще говоря, различны для разных серверов, но чаще всего бывает достаточно дать имени файла расширение .php3.
    Итак, мы хотели бы иметь возможность читать и отправлять почту с помощью Web-браузера. Видимо, будет разумно сделать интерфейс похожим, скажем, на Netscape Messenger: окно разделено по горизонтали на две части, в верхней находится список писем в нашем почтовом ящике на сервере, в нижней - текущее письмо. Но перед тем как показать пользователю HTML-файл с описанием фреймов, мы потребуем от него ввести свое имя и пароль (при неправильно введенном пароле он получит файл, содержащий сообщение об ошибке). Эту функцию будет осуществлять файл index.php3, показанный в листинге 2. Давайте посмотрим на него поближе.
    Прежде всего стоит обратить внимание на то, что скобка Дальнейшее очевидно: мы смотрим на введенное пользователем имя и пароль, пытаемся связаться с сервером IMAP4r1 и, если все прошло успешно, просто выдаем информацию о наборе фреймов. Однако теперь во все сценарии в том подкаталоге, где размещается наш стартовый файл index.php3, будет передаваться информация об имени и пароле!
    Заметим также, что перед именем функции imap_open стоит символ "@". Он означает, что сообщения о возможных ошибках при работе функции должны не выводиться в текст на HTML (поведение PHP по умолчанию), а сохраняться в специальной переменной. Это необходимо для того, чтобы воспользоваться функцией Header(): она не будет работать, если в тело документа уже выведен какой бы то ни было текст, в том числе и сообщение об ошибке.

    Ссылки

    Apache:
    Russian Apache:
    PHP3:
    IMAP4r1 library and daemons:
    IMP:

    Status-Code это код результата

    (Chapter 6.1. 1 Status Code and Reason Phrase from )

    ,

    Status-Code это код результата попытки понять и выполнить запрос. Состоит из трех цифр. Полные определения значений Status-Code приведены в главе 10. Reason-Phrase - короткое текстовое описание Status-Code. Status-Code предназначен для программ, Reason-Phrase - для человека.

    Первая цифра Status-Code определяет класс кода. Две следующие цифры никак не категоризируются. Существует пять классов кодов:

  • 1xx: Информационные - Запрос принят, продолжение процесса


  • 2xx: Коды успеха - Запрос был успешно принят, распознан и выполнен


  • 3xx: Редирект - Дальнейшее действие должно быть перенаправлено для завершения запроса


  • 4xx: Ошибка клиента - Неверный синтаксис запроса или недостаточно клиентских данных для выполнения запроса


  • 5xx: Ошибка сервера - Невозможность выполнить полностью появившийся запрос


  • Ниже представлена таблица возможных значений Status-Code и соответствующих им значений Reason-Phrase для HTTP/1.1.

    Status-CodeReason-Phrase"100""101""200""201""202""203""204""205""206""300""301""302""303""304""305""400""401""402""403""404""405""406""407""408""409""410""411""412""413""414""415""500""501""502""503""504""505"
    Continue (продолжение)
    Switching Protocols (переключение протоколов)
    OK (ну, тут понятно)
    Created (создано)
    Accepted (принято)
    Non-Authoritative Information (неавторизованная информация)
    No Content (нет содержимого)
    Reset Content (обновить содержимое)
    Partial Content (часть содержимого)
    Multiple Choices (возможность выбора)
    Moved Permanently (удалено)
    Moved Temporarily (временно удалено)
    See Other (смотри другое)
    Not Modified (не изменялось)
    Use Proxy (использовать прокси)
    Bad Request (неправильный запрос)
    Unauthorized (неавторизованный запрос)
    Payment Required (требуется заплатить)
    Forbidden (запрещено)
    Not Found (не найдено)
    Method Not Allowed (метод)
    Not Acceptable (не принято)
    Proxy Authentication Required (требуется аутентификация прокси)
    Request Time-out (тайм-аут запроса)
    Conflict (конфликт)
    Gone (пойди дальше)
    Length Required (требуется правильная длина message-body)
    Precondition Failed (нарушено согласование)
    Request Entity Too Large (поле Entity запроса слишком велико)
    Request-URI Too Large (поле URI запроса слишком велико)
    Unsupported Media Type (неподдерживаемый media-тип)
    Internal Server Error (внутренняя ошибка сервера)
    Not Implemented (невыполнимо)
    Bad Gateway (неправильный шлюз)
    Service Unavailable (недоступный сервис)
    Gateway Time-out (тайм-аут шлюза)
    HTTP Version not supported (неподдерживаемая версия HTTP)
    extension-code

    extension-code = 3 цифры

    Reason-Phrase = *<текст, исключая символы CR, LF>

    Status-Code расширяемы. От HTTP-приложений не требует обязательное понимание всех зарегистрированных Status-Code. Однако приложения ОБЯЗАНЫ понимать класс любого Status-Code (первая цифра) и если встречен непонятный Status-Code, то приложения должны отреагировать на такой код как на x00. К примеру, если принят нераспознаваемый код со значением 431 - приложение должно поступить как будто бы был принят код 400. В таких случаях приложениям СЛЕДУЕТ возвращать пользователю нераспознанный код, причем желательно в human-readable формате.

    CGI файл данных

    Сервер передает данные CGI программам через Windows "private profile"
    afqk, в формате "параметр-значение" (windows INI файл). CGI программа
    может прочитать данный файл и получит все данные, передаваемые ей из формы,
    а также автоматически генерируемые броузером данные.
    CGI файл данных состоит из следующих секций:









  • Другие заголовки

    Другие заголовки передаются клиенту в том виде, в котором они представлены.

    Командная строка

    Сервер должен вызывать CGI программу выполняя функцию CreateProcess()
    с командной строкой следующего формата:
    WinCGI-exe cgi-data-file
    WinCGI-exe
    Полный путь к исполняемой CGI программе. Сервер не зависит от "текущего
    каталога" или переменной окружения PATH. Примите к сведению, что "исполняемая"
    не обязательно означает .EXE файл. Это может быть документ, ассоциирующийся
    с реально исполняемой программой, описанной в WIN.INI или System Registry.
    cgi-data-file
    Полный путь к .

    Метод вызова

    Сервер использует CreateProcess() для запуска процесса, не
    имеющего главного окна. Вызванный процесс не будет отображаться каким либо
    образом на мониторе сервера.
    Некоторые сервера поддерживают режим отладки CGI программ и скриптов,
    что позволяет серверу запускать CGI программу как обычный процесс с созданием
    главного окна и отображением информации на мониторе сервера. Данный способ
    весьма удобен на стадии отладки CGI программ.

    Обработка результата

    CGI программа возвращает результат работы, отвечающий (явно или неявно)
    целям запроса. Сервер кодирует результат работы в соответствии со стандартом
    HTTP и использует HTTP для отправки результата клиенту. Это означает, что
    сервер добавляет необходимый HTTP заголовки в сообщение, формируемое CGI
    программой.
    Результат работы CGI программы состоит из двух частей: заголовка
    и тела сообщения. Заголовок состоит из одной или более строк текста,
    отделенных от тела пустой строкой. Тело сообщения содержит данные, представленные
    в MIME формате, указанном в заголовке.
    Сервер не изменяет тело документа, что означает, что сервер передает
    сформированный CGI программой ответ "как он есть".T

    Обзор

    Большое количество World Wide Web приложений основано на использовании
    внешних программ, управляемых Web сервером. Использование данных программ
    позволяет строить Web приложения с динамически обновляемой информацией,
    хранящейся в базах данных или генерирующейся в зависимости от бизнес-правил
    решаемых задач. Для связи между Web сервером и вызываемыми программами
    широко используется Common Gateway Interface (CGI), имеющий реализации
    как для Windows-ориентированных программ, так и для приложений, функционирующих
    в среде Unix. Данный документ описывает Windows-модификацию интерфейса
    CG, иначе называемую Windows CGI интерфейсом.

    Пример декодированных значений формы

    В данном примере форма содержит небольшое поле, SELECT MULTIPLE с 2-мя
    небольшими секциями, поле длиной 300 символов, поле, содержащее специальные
    символы и поле длиной 230KB.
    [Form Literal]

    smallfield=123 Main St. #122

    multiple=first selection

    multiple_1=second selection

    [Form External]

    field300chars=C:\TEMP\HS19AF6C.000 300

    fieldwithlinebreaks=C:\TEMP\HS19AF6C.001 43

    [Form Huge]

    field230K=C:\TEMP\HS19AF6C.002 276920

    Прямой возврат

    Сервер позволяет конечному приложению осуществлять прямой возврат результата запроса клиенту. Это осуществляется посредством включение в заголовок возвращаемого сообщения его информационного протокола. Это позволяет CGI программам формировать непосредственный ответ клиенту с указанием HTTP заголовка без предварительной обработки его сервером..
    Сервер анализирует результат запроса, помещаемый CGI программой в выходной файл (Output File), и, если первая строка "HTTP/1.0",
    он предполагает, что сообщение содержит полный HTTP ответ и отсылает его
    клиенту без упаковки.

    Разбор данных HTML-форм

    Windows CGI требует, чтобы Web сервер декодировал данные из HTML форм,
    если они переданы при помощи POST метода запроса. Он не требует от сервера
    декодирования параметров, если они переданы в качестве строки запроса ("query
    string"), являющейся частью URL.
    Существует два способа, которыми данные из форм могут быть переданы
    серверу броузером:
    URL-Encoded
    Это наиболее используемый формат данных, передаваемых из форм. Содержимое
    полей формы выделяются из формы и передаются согласно спецификации HTML
    1.0, а затем собираются в одну строку, где отделяются друг от друга символом
    амперсанда. Тип содержания сообщения устанавливается броузером в application/x-www-form-urlencoded.
    Multipart Form Data
    Данный формат разработан для эффективной загрузки файлов на сервер
    с использованием форм. Содержимое полей формы передается как многостраничное
    MIME сообщение. Каждое поле содержится в одной странице. Тип содержания,
    устанавливается броузером в multipart/form-data.
    "Грамотные" серверы должны уметь обрабатывать оба типа
    данных из форм.


    Секция [Accept]

    Данная секция содержит типы данных, посылаемых клиентом, найденные в
    заголовке запроса в виде
    Accept: type/subtype {parameters}
    Если данные параметры присутствуют (например, "q=0.100") ,
    они передаются как значения параметра Accept. Для каждого типа передаваемых
    данных заводится свой параметр Accept.

    Секция [CGI]

    Данная секция содержит большинство специфических CGI параметров (тип
    доступа, тип запроса, дополнительные заголовки, определенные в других секциях
    и т.п.). Каждое значение представлено в виде символьной строки. Если значение
    является пустой строкой, значит данный параметр был опущен. Список параметров
    данной секции представлен ниже:
    Request Protocol
    Название и модификация информационного протокола, использованного для
    передачи данного запроса. Формат: протокол/модификация. Пример: "HTTP/1.0".
    Request Method
    Метод, который использовался для данного запроса. Для HTTP это "GET",
    "HEAD", "POST" и т.д.
    Executable Path
    Логический путь к исполняемой CGI программе, необходимый для ссылки
    CGI программе на саму себя.
    Logical Path
    Запрос также может указывать к ресурсам, необходимым для выполнения
    данного запроса. Данный параметр содержит путь в том виде, который был
    получен сервером без мэпирования его на физический путь на диске.
    Physical Path
    Если запрос содержит информацию о логическом пути, сервер преобразует
    его к физическому пути (например, к пути к файлу на диске) доступа согласно
    синтаксическим правилам операционной системы.
    Query String
    Информация, размещающаяся после ? в URL вызываемой CGI программы. Сервер
    оставляет эту информацию без изменений в том виде, в котором она была помещена
    в URL.
    Request Range
    Byte-range спецификация получаемая вместе с запросом (если есть). Смотри
    текущий Internet Draft (или RFC), описывающий расширение HTTP для получения
    более полной информации. Сервер должен поддерживать работу CGI программ
    в byte-ranging.
    Referer
    URL документа, содержащего ссылку на данную CGI программу. Надо заметить,
    что некоторые броузеры закрывают данную возможность и не дают ее использовать.
    From
    E-mail адрес пользователя броузера. Надо заметить, что данный параметр
    присутствует с спецификации HTTP, но не используется большинством броузером
    из соображений секретности.
    User Agent
    Строка, описывающая программное обеспечение броузера. Не генерируется

    большинством броузеров.

    Content Type

    Данный параметр содержит MIME-тип данных, посланных клиентом вместе

    с полями из формы, если эти данные были посланы. Формат: type/subtype.

    Content Length

    Для запросов, с которыми посланы дополнительные данные в это поле заносится

    длина посланных данных в байтах.

    Content File

    Для запросов, содержащих дополнительные данные, посланные пользователем,

    этот параметр содержит имя файла, в которое WEB-сервер записывает эти данные.

    В дальнейшем, пользовательская программа может считать эти данные. Параметр

    содержит полный путь к файлу данных.

    Server Software

    Название и версия серверного программного обеспечения, обработавшего

    запрос и вызвавшего CGI-программу. Формат: name/version.

    Server Name

    Сетевое имя сервера или псевдоним, необходимый для ссылающихся на себя

    URL Этот параметр (в комбинации с параметром ServerPort) может быть использован

    для вычисления полного URL к серверу.

    Server Port

    Номер порта, по которому работает сервер.

    Server Admin

    E-mail адрес администратора сервера. Данный параметр необходим для генерации

    сообщений об ошибках и отправки данных сообщений администратору сервера

    или для генерации форм с URL "mailto:".

    CGI Version

    Версия спецификации CGI. Формат: CGI/версия. Для данной версии, "CGI/1.2

    (Win)".

    Remote Host

    Сетевое имя хоста клиента, если доступно. Данный параметр может быть

    использован для опознавание клиента.

    Remote Address

    Сетевой (IP) адрес клиента. Данный параметр может быть использован

    для проверки пользователя если отсутствует сетевое имя.

    Authentication Method

    Если используется защищенный вызов CGI программы, это протокол-зависимый

    метод аутентификации, используемый для аутентификации пользователя.

    Authentication Realm

    Если используется защищенный вызов CGI программы, это протокол-зависимый

    сервис, используемый для аутентификации пользователя. Список пользователей

    для полученного вида сервиса проверяется для аутентификации пользователя.

    Authenticated Username

    Если используется защищенный вызов CGI программы, это имя пользователя,

    которое клиент использует для аутентификации при доступе к CGI-программе.

    Секция [Extra Headers]

    Данная секция содержит "дополнительные" заголовки, которые
    включены в запрос в виде "параметр=значение". Сервер должен раскодировать
    как параметр, так и его значение прежде чем они будут помещены в файл данных
    CGI.

    Секция [Form External]

    Если размер декодированной строки превышает 254 символа или декодированная строка содержит управляющие символы, такие, как перевод строки, возврат
    каретки, двойные кавычки и т.д., то сервер помещает данное значение в отдельный
    временный файл, а в секцию [Form External] помещает строку в виде:
    параметр=путь длина
    где путь - это полный путь и имя временного файла, содержащего
    декодированное значение параметра, а длина - длина в байтах этого файла.

    Секция [Form File]

    Если запрос пришел в виде multipart/form-data, то он может
    содержать один или несколько загруженных с клиента файлов. В этом случае
    каждый загруженный файл размещается в специальном временном файле, а в
    секции [Form File] строки имеют тот же формат, что и секции [Form External].
    каждая строка параметра в этом случае выглядит так:
    параметр=[полный_путь_к_файлу] длина тип ссылка [имя_файла]

    где полный_путь_к_файлу - это путь к временному файлу, содержащему
    загруженный файл, длина - длина в байтах загруженного файла, тип
    - тип MIME загруженного файла, ссылка - способ кодировки загруженного
    файла и имя_файла - исходное название загруженного файла. Использование
    квадратных скобок обязательно, поскольку имя файла и путь могут содержать
    символы пробела.

    Секция [Form Huge]

    Если общая длина строки с кодированными параметрами превышает 65,535
    байт, то сервер не выполняет декодирование, а оставляет данный в Content
    File, а в секцию [Form Huge] помещает строки в виде:
    параметр=смещение длина
    где смещение - это смещение от начала Content File по которому
    находится требуемый параметр, а длина - длина в байтах значения
    выбранного параметра. Вы можете использовать смещение для выполнения
    поиска начала значения выбранного вами параметра и использовать длину для
    чтения значения выбранного параметра. Не забывайте, что если параметр закодирован,
    то вам необходимо раскодировать его перед использованием.

    Секция [Form Literal]

    Если запрос от клиента пришел в виде HTTP POST из HTML формы (с типом
    содержимого application/x-www-form-urlencoded или multipart/form-data),
    то сервер раскодирует данные из формы и поместит их в секцию [Form Literal].
    Для URL-кодированных данных формы, строка передаваемых параметров выглядит как "параметр=значение&параметр=значение&...", где значения
    находятся в url-кодированном формате. Сервер разделяет "параметр=значение"
    по символу '&', затем разделяет собственно "параметр" и "значение",
    декодирует "значение" и помещает результат в виде "параметр=раскодированное_значение"
    в секцию [Form Literal].
    Для многостраничных данных строка данных представляется в многостраничном MIME формате, где каждое поле представлено как отдельная часть (файл).
    Сервер декодирует имена и значение каждой части и размещает их в формате
    "параметр=значение" в секции [Form Literal].
    Если форма содержит какие-либо элементы SELECT MULTIPLE, то
    будет создано несколько строк с вида "параметр=значение" с одинаковым
    именем "параметра". В этом случае генерирует нормальную строку
    "параметр=значение" для первого встречающегося элемента, а каждый
    следующий представляет в виде "параметр_X=значение", где "X"
    - увеличивающийся счетчик.

    Секция [System]

    Данная секция содержит параметры, специфические для Windows реализации
    CGI:
    GMT Offset
    Количество секунд, которое необходимо добавить к времени по Гринвичу
    для вычисления локального времени клиента.
    Debug Mode
    Данный параметр имеет значение "Yes" если включен режим "CGI/script
    tracing" на сервере.
    Output File
    Полный путь к файлу, в который необходимо поместить данные, отсылаемые
    сервером клиенту после завершения работы программы.
    Content File
    Полный путь к файл у в котором содержится дополнительная информация,
    поступающая вместе с запросом.

    Специальные строки заголовка

    Сервер распознает следующие строки заголовка в выходном потоке:
    Content-Type:
    Указывает на MIME тип тела сообщения. Значение этого параметра должно
    быть в формате type/subtype.
    URI: (value enclosed in angle brackets)
    Данное значение указывает на полный URL или ссылку на локальный файл,
    сообщение из которого будет возвращено клиенту в теле сообщения. Если значение
    является локальным файлом, сервер отсылает его как результат запроса, как
    будто клиент воспользовался методом GET при генерации запроса. Если значение
    является полным URL, то сервер возвращает сообщение "401 redirect"
    для обеспечения прямой загрузки указанного объекта.
    Location:
    То же самое, что и URI, но данная форма сейчас не используется. Параметр
    value НЕ должен быть взят в угловые скобки.

    Вызов CGI программ

    Сервер использует функцию CreateProcess() для вызова CGI программ.
    Сервер синхронизируется с CGI программой, поскольку он должен определить
    момент завершения CGI программы. Это достигается использованием функции
    Win32 WaitForSingleObject(), ожидающей получения сигнала завершения
    CGI программы.


        Работа с информацией: Cистемы - Технологии - Рынок