- Lektsia - бесплатные рефераты, доклады, курсовые работы, контрольные и дипломы для студентов - https://lektsia.info -

Переменные заголовка HTTP-запроса



За исключением тех строк из заголовка HTTP-запроса,которые были включены в другие переменные, сервер приделывает строкам префикс HTTP_и заменяет знаки «-»на «_»:

HTTP_ACCEPT

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

Отсюда скрипту поступает список тех MIME, которые браузер готов принять в качестве ответа от сервера.

Пример: HTTP_ACCEPT=text/html,text/plain,image/gif

HTTP_USER_AGENT

Браузер обычно посылает на сервер и информацию о себе, чтобы базируясь на знании особенностей и недостатков конкретных браузеров CGI-скрипт мог выдать информацию с учетом этого.

Например, разные браузеры могут поддерживать или не поддерживать какие-то HTMLтэги.

Пример:

HTTP_USER_AGENT=Mozila/2.01 Gold(Win95;I)

HTTP_HOST

Имя хоста к которому обращается браузер. Так как физически на одном сервере может находиться сразу много серверов (Виртуальные Хосты), то должен быть способ сообщить серверу к какому именно идет обращение.

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

Пример:

HTTP_HOST=www.nnov.city.ru

Начнем применять на практике усвоенные уроки.

#!/usr/bin/perl

#vars.cgi

sub urldecode{ #очень полезная функция декодирования

local($val)=@_; #запроса,будет почти в каждой вашей CGI-программе

$val=~s/\+/ /g;

$val=~s/%([0-9A-H]{2})/pack(‘C’,hex($1))/ge;

return $val;

} print "Content-Type: text/html\n\n";

print "<HTML><HEAD><TITLE>CGI-Variables</TITLE></HEAD>\n"; print "<BODY>\n";

print "Enter here something:<ISINDEX><BR>\n"; print "Your request is:$ENV{‘REQUEST_STRING’}<BR>\n"; print "Decoded request is:urldecode($ENV{‘REQUEST_STRING’}) <BR>\n";

print "<HR>\n";

print "Variables:<BR>\n";

print "<I><B>REQUEST_METHOD</B></I>=$ENV{‘REQUEST_METHOD’}

<BR>\n";

print "<I><B>QUERY_STRING</B></I>=$ENV{‘QUERY_STRING’} <BR>\n";

print "<I><B>CONTENT_LENGTH</B></I>=$ENV{‘CONTENT_LENGTH’}<BR>\n";

print "<I><B>CONTENT_TYPE</B></I>=$ENV{‘CONTENT_TYPE’}<BR>\n";

print "<I><B>GATEWAY_INTERFACE</B></I>=$ENV{‘GATEWAY_INTERFACE’}

<BR>\n";

print "<I><B>REMOTE_ADDR</B></I>=$ENV{‘REMOTE_ADDR’}<BR>\n";

print "<I><B>REMOTE_HOST</B></I>=$ENV{‘REMOTE_HOST’}<BR>\n";

print "<I><B>SCRIPT_NAME</B></I>=$ENV{‘SCRIPT_NAME’}<BR>\n";

print "<I><B>SCRIPT_FILENAME</B></I>=$ENV{‘SCRIPT_FILENAME’}

<BR>\n";

print "<I><B>SERVER_NAME</B></I>=$ENV{‘SERVER_NAME’}<BR>\n";

print "<I><B>SERVER_PORT</B></I>=$ENV{‘SERVER_PORT’}<BR>\n";

print

"<I><B>SERVER_PROTOCOL</B></I>=$ENV{‘SERVER_PROTOCOL’}<BR>\n";

print

"<I><B>SERVER_SOFTWARE</B></I>=$ENV{‘SERVER_SOFTWARE’}<BR>\n";

print "<I><B>HTTP_ACCEPT</B></I>=$ENV{‘HTTP_ACCEPT’}<BR>\n";

print

"<I><B>HTTP_USER_AGENT</B></I>=$ENV{‘HTTP_USER_AGENT’}<BR>\n";

print "<I><B>HTTP_HOST</B></I>=$ENV{‘HTTP_HOST’}<BR>\n";

print "<HR>\n";

print "All enviroment:<BR>\n";

foreach $env_var (keys %ENV){

print "<I>$env_var=$ENV{$env_var}</I><BR>\n"; }

print "</BODY></HTML>\n";

Так как все ваши .cgi-файлыдолжны быть исполняемыми, то чтобы облегчить себе жизнь заведите в директории cgi-binкомандный файл mkcgi, содержащий:

#!/bin/sh

#mkcgi

chmod +x *.cgi

Сделайте его в свою очередь исполняемым chmod +x mkcgi— он сильно упростит вам жизнь.

Ну, а теперь запускайте скрипт...

Изучив информацию, выдаваемую данным скриптом, вы сможете лучше ориентироваться в переменных окружения CGI.

Заголовки запросов и ответов

Даже если вы и знаете кое-что о HTTP,все равно не лишнее будет вспомнить о том, как это все работает, тем более на эту информацию придется ориентироваться при написании CGIскриптов.

Этапы соединения

Первый этап— это когда HTTP— клиент (браузер) соединяется с сервером. Для этого он использует протокол TCP/IP.Соединение происходит к известному клиенту TCP-порту (80— номер порта HTTP(другие сервисы сидят на других портах, например FTPи SMTPна 21и 25)).

На втором этапеидет запрос клиента: клиент передает заголовок запроса и возможно (в зависимости от метода) тело сообщения запроса.

В заголовке обязательно указывается метод URIи версия HTTPи может быть еще несколько необязательных полей.

Третий этап— ответ сервера, который опять таки состоит из заголовка, в котором сервер указывает версию HTTPи код статуса, который может говорить об успешном или неуспешном результате и его причинах. Далее идет тело ответа.

На четвертым этапепроисходит разрыв TCP/IP-соединения.

полей.

HTTP-запрос

Запрос состоит из Строки запроса(она обязательна) и остальных.

Синтаксис строки:

МЕТОД <SP> URI <SP> HTTP/версия <CRLF>

Где <SP>— пробел, <CRLF>— переход на новую строку.

Методы HTTP

GET

Самый часто применяемый метод, в протоколе HTTP/0.9был единственным методом и применялся для извлечения информации по заданному URI.Может быть условным, если в заголовке указано поле If-Modified-Since:

HEAD

Почти идентичен GET,но отличается тем, что сервер не возвращает тело объекта, а только его заголовок (метаинформацию).

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

POST

Передает данные для обработки их программой, указанной в URI.Здесь обязательно указывается поле Content-Length:Существуют и другие, реже применяемые методы, например PUT— для сохранения передаваемых данных в указанном URIи DELETEдля удаления ресурса.

Поля заголовка запроса

После строки запроса идут поля заголовка запроса. Поля общего (general-header) заголовка (он общий как для запросов, так и для ответов):

Date:

Указывает дату запроса, например: Date: Sun, 20 Nov 1994 08:12:31 GMT

MIME-version:

Указывает версию MIME(по умолчанию 1.0): MIME-version: 1.0

Pragma:

Содержит указания для таких промежуточных агентов, как прокси и шлюзы:

Pragma: no-cache

Поля, относящиеся к запросу (Request-Header):

Authorization:

Содержит информацию аутентификации: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

From:

Браузер может посылать адрес пользователя серверу: From: quake@doom.ru

If-Modified-Since:

Используется при методе GET, ресурс возвращается, если он был изменен с указанного момента, может использоваться при кэшировании.

If-Modified-Since:Mon 15 Jul 1997 00:15:24 GMT

Referer:

Содержит URLпредшествующего ресурса. Referer: http://www.uic.nnov.ru/~paaa/index.html

User-Agent:

Программное обеспечение клиента. User-Agent: Mozilla/3.0

Заголовок информации сообщения (Entity-Header) применяется как в запросах, так и в ответах (при этом некоторые поля только в ответах).

Allow: (в ответе сервера)

Список методов, поддерживаемых ресурсом. Allow: GET, HEAD

Content-Encoding:

Идентифицирует метод кодировки, которым был закодирован ресурс.

Content-Encoding: x-gzip

Content-Length:

Длина тела сообщения. Content-Length: 102

Content-Type:

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

Content-Type: text/html; charset=utf-8

Expires: (в ответе сервера)

Дата окончания действия ресурса применяется в кэшировании для запрета кэширования устаревших ресурсов (в ответе).

Expires: Tue, 24 Sep 1998 23:00:15 GMT

Last-Modified: (в ответе сервера)

Время последнего обновления ресурса. Last-Modified: Tue, 23 sep 1998 13:48:40 GMT Другие поля:

Поля Accept:

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

Accept: Accept: Accept:

text/html

text/plain

image/gif

Поле Host:

Служит для того, чтобы указать, к какому хосту идет обращение. Данное поле не входит в число обязательных. Однако оно является необходимым в тех случаях, когда одному физическому серверу соответствует несколько виртуальных хостов. Тогда в этом поле указывается какой из виртуальных хостов имеется в виду.

Host: www.nnov.city.ru

Примеры запросов

Простейший запрос: GET /index.html HTTP/1.0 Посложнее:

GET /somedir/somedoc.html HTTP/1.0 User-Agent: Mozilla/2.0 Accept: text/html Accept: text/plain Accept: image/gif

Передача данных CGI-скрипту через метод GET:

GET /~paaa/cgi-bin/test.cgi?

name=Dmitry&organization=%D3%ED%E8%E2%E5%F0%F1%E8%F2%E5%F2+%CD%E8%

E6%ED%E5%E3%EE+%CD%EE%E2%E3%EE%F0%EE%E4%E0 &Name=&email=&comment=

HTTP/1.0

User-Agent: Mozila/2.0

Accept: text/html

Accept: image/gif

Используя метод POST, данные передаются в теле сообщения за-

проса:

POST /~paaa/cgi-bin/test.cgi HTTP/1.0

User-Agent: Mozila/2.0

Accept: text/html

Accept: image/gif

Content-Type: application/x-www-form-urlencoded

Content-Length: 131

name=Lesha

&organization=%D3%ED%E8%E2%E5%F0%F1%E8%F2%E5%F2+%CD%E8

%E6%ED%E5%E3%EE+%CD%EE%E2%E3%EE%F0%EE%E4%E0&Name=

&email=

&comment=

Ответ HTTP-сервера

Ответ идет от сервера. Состоит он из строки состояния и поля ответа. Общий заголовок (General-Header), заголовок тела сообщения (Entity-Header), которые уже описаны при обсуждении запроса и заголовок ответа (Response-Header).

Строка состояния имеет следующий формат:

HTTP/version <SP> Status-Code <SP> Status-Phrase

Где HTTP/version— версия, Status-Code— 3-х значный код, и Status-Phrase— текстовая фраза, поясняющая код.

Например:

HTTP/1.0 200 Ok

200— код, означающий успешную обработку запроса, что и поясняет «Ok»

Заголовок ответа состоит из полей:

Location:

Содержит URIресурса, может быть использован для переключения клиента в другое место, если например ресурс был перемещен в другое место или на другой сервер.

Location: http://www.uic.nnov.ru/newlocation/index.html

Server:

Информация о программном обеспечении сервера.

Server: Apache/1.1 WWW-Autenticate:

Параметры аутентификации.

WWW-Autenticate: Basic realm="doomsday"

Коды ответов HTTP

Более подробное описание всех кодов можно найти в RFC-1945.

Несколько примеров:

HTTP/1.0 200 Ok

Date: Wed, 25 Sep 1998 23:00:00 GMT

Server: Apache/1.1

MIME-version: 1.0

Last-Modified: Mon 15 Nov 1996 15:20:12 GMT

Content-Type: text/html

Content-Length: 2000

<HTML><HEAD><TITLE>Hello</TITLE></HEAD> <BODY bgcolor="green" text="yellow">

......

</HTML>

А вот такое сервер выдаст в неудачном случае:

HTTP/1.0 404 Not Found

CGI-заголовок

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

Заголовок (CGI-Header) состоит из полей:

Content-Type:

Должно обязательно присутствовать, если есть тело. Content-Type: text/html

Location:

Содержит URLресурса, на который скрипт перенаправляет запрос. Как правило, если присутствует это поле, больше ничего не указывается.

Location: http://www.idsoftware.com/index.html

Status:

Позволяет CGIскрипту вернуть статус обработки, если это поле не задано, то сервер подразумевает «200 Ok»

Status: 404 Not found

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

Примеры:

Обычно такое выдает скрипт:

Content-Type: text/html

<HTML><HEAD> .......

Но иногда такое (когда он служит для перенаправления): Location: http://www.mustdie.ru/ А вот пример возврата статуса:

Content-Type: image/gif

Status: 190 Its seems great like a playing doom! WOW!

GIF89a ........

Nph-скрипты

Иногда возникает необходимость чтобы CGI-скрипт сам отвечал напрямую клиенту, минуя разбор заголовка. Это во-первых уменьшает нагрузку на сервер и во-вторых, что самое главное, такой прямой ответ клиенту позволяет скрипту полностью контролировать транзакцию.

Для этого существуют nph-скрипты (Not Parse Header), имя скрипта должно начинаться с префикса «nph-».

Например: «nph-animate.cgi».

Такие скрипты сами формируют HTTP-ответ клиенту, что полезно при анимации:

#!/usr/bin/perl #nph-animate.cgi $times = 20;

#Заготовьте несколько небольших gif-файлов для этой программы @files = qw(img0.gif img1.gif img2.gif img3.gif); select (STDOUT); $|=1; #autoflush mode on #Generate header print "HTTP/1.0 200 Okay\n";

print "Content-Type: multipart/x-mixed-replace; boundary=mybound-ary\n\n";

print "—myboundary\n"; for ($num=1;$num<=$times;$num++) { foreach $file (@files) {

print "Content-Type: image/gif\n\n";

open(PIC,"$file");

print <PIC>;

close(PIC);

print "\n—myboundary\n";

sleep(3);

} } print "\n—myboundary—\n";

Этот пример выдаст вам анимацию, составленную из нескольких .gif-файлов.

Права доступа

Сидя в пределах своей домашней директории и качая с Интернета всякую фигню, вы возможно и не задавались некоторыми вопросами, а зря. Ведь немного надо, чтобы попортить нервы начинающему CGI-программисту. Одна из таких вещей — это права доступа. Начнем с того, что в системе Unix каждый пользователь имеет свой идентификатор — число, уникально идентифицирующее его в этой системе (например, логин paaa, а ему соответствует число 1818). Это число внутреннее для операционной системы, для пользования оно представлено как логин, который и соответствует пользователю.

Только не надо думать о пользователе как о конкретном человеке, сидящим за клавиатурой, пользователем может быть и какой-нибудь процесс. Важно отметить что пользователь — это определенная область прав доступа, которая ему соответствует.

Вы, например, не можете обычно писать и удалять файлы из каталога другого пользователя. Это и дает возможность стабильной работы всей системы.

Итак, есть идентификатор пользователя. Также имеется идентификатор группы. Группа служит для выделения пользователей по группам. Например, у пользователей группы users (обычные пользователи) не такие права, как у группы wheels (административная группа). Каждый процесс, который вами запущен (будь то Netscape, терминал, или текстовый редактор), получают ваши идентификаторы пользователя и группы. Таким образом они исполняются от вашего имени.

Теперь рассмотрим повнимательнее файловую систему.

В Unix с файлом связано много характеристик. Во-первых — в системе нет «ничьих» файлов, все файлы имеют владельца-пользователя и владельца-группу.

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

Следующее новшество, по сравнению с DOS — это права доступа к файлу. Их может сменить только тот пользователь, которому принадлежит файл, или супервизор (это в отличии от DOS, где каждая дрянь типа вируса может снять атрибут readonly читать и писать все файлы).

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

В каждой такой категории выделяются 3 права:

Все права и атрибуты очень наглядно показываются командой ls с ключом l.

Так как исполнять каталоги бессмысленно, то право на исполнение для них означает право обращаться к файлам из этого каталога.

Изменяются права командой chmod, ее синтаксис такой:

chmod [u|g|o]{+|-}{r|w|x} file chmod number file

Где:

Примеры:

chmod +r file.txt #разрешает всем право на чтения файла

chmod u+w file.txt #устанавливает для владельца файла право на

запись в него

chmod +x gbook.cgi #право на исполнение для всех, как для

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

chmod 0777 cgi-bin #Разрешает самые широкие права доступа для

cgi-bin

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

В том случае, если у процесса нет достаточных прав, система возвращает ошибку. Следует заметить, что для супервизора rootправа доступа не проверяются.

Можно выполнить скрипт, только если есть права на его исполнение. Вот почему следует давать chmod +x *.cgi, иначе ваши скрипты станут просто недоступными.

Но и это еще не все.

Ваш скрипт может обращаться к вашим файлам (например ведет базу данных гостевой книги). Все выглядит нормально, но только ничего не работает, файл в который вы намеревались писать, не открывается. Знакомая проблема?

Так вот, чтобы вы не мучились в догадках ваш скрипт не может получить доступ к вашим файлам, потому что он выполняется не вами (не с вашим идентификатором), а от имени nobody (непривилегированный пользователь).

Эта мера предосторожности направлена на то, чтобы скрипты, взбесившись из-за неправильно переданных параметров (или вообще от глюков), не могли ничего повредить ценного и важного на сервере.

Поэтому тем файлам, к которым скрипт по смыслу должен обращаться, нужно присвоить самые широкие права доступа 0777.

Например в случае гостевой книги:

chmod 0777 guestbook.dat

Если также важно чтобы скрипты могли заводить новые файлы в cgi-bin,то нужно дать также права на это:

chmod 0777 cgi-bin

Если вы видите, что ваш скрипт не может обратиться к какому-то файлу, то это в 99% случаях происходит из-за вашей забывчивости!

На самый крайний случай воспользуйтесь setuid-скриптами (к этому делу, если вы на это решились, отнеситесь очень серьезно, так как целые тома по безопасности в Unixпосвящены именно setuid-программам). Для общего развития имейте в виду следующую информацию: «Кроме указания прав доступа существуют специальные биты у файла. Это биты установки пользователя и группы. Когда процесс выполняется (простой процесс), то его реальный и эффективный идентификаторы пользователей совпадают, идентификаторы групп тоже».

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

Нельзя ли их как-то изменить, когда уж совсем нужда заставит? Можно!

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

Самый классический пример setuid-программ — это программа passwd, предназначенная для смены пароля пользователя.

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

Так как это системный файл, то открыть к нему доступ на запись всем — значит подвергнуть всю систему риску, ведь любое неправильное изменение его повлечет катастрофические последствия (в конце концов бывает просто хулиганство).

Поэтому доступ к этому файлу закрыт для всех пользователей. А что, если надо сменить пароль?

Запускаем программу passwd!

Если глянуть на ее атрибуты, то видно, что она принадлежит root-супервизору, и еще имеет установленный бит setuid. Так корректно обходится эта проблема.

Если вы все же решили попытаться, то знайте, что сделать программу setuidможно командой:

chmod +s myprogramm

Примерчик напоследок: эта программа выдает содержимое вашей директории public_htmlв том случае, если она доступна для чтения и для каждого файла указывает: можно ли его читать, писать и исполнять. Попробуйте сделать ее setuidи посмотрите как изменится результат.

#!/usr/bin/perl

#listmydir.cgi

print "Content-Type: text/html\n\n";

if(!(-r ‘..’)){

print ".. is not allowed for reading ;)))))\n";

} else{

@list=glob(‘../*’);

foreach(@list){

print "<A href=\”$_\”>$_</A>";

print "&nbsp;readable" if -r;

print "&nbsp;writable" if -w;

print "&nbsp;executable" if -x;

print "<BR>\n";

}

}

Генерация ответа

Стандарт MIME

Стандарт MIMEпоявился в электронной почте (e-mail) потому, что остро стала проблема пересылки по e-mailразличных данных в различных форматах.

Так как HTTPтоже работает с различными типами данных, то тоже использует MIMEдля своих нужд.

Типы MIMEсостоят из Типа и подтипа.

Например: text/plain

где text— указывает на наличие текстового содержимого, а plain— уточняет его, как простой текст.

Приведенный ниже список (он далеко не полон, типов MIMEогромное количество), описывает некоторые часто встречающиеся типы:

Информация о MIMEвозможно пригодится вам и в том случае, если вы собираетесь работать из ваших скриптов с электронной почтой, но и для WWWона не повредит. Особенно знание Content-Type:

Content-Type:

Состоит из типа и подтипа. Типы могут быть как стандартные так и экспериментальные, начинающиеся с префикса «x-»:

text— текстовые данные. Первый подтип, который включен сюда — это plain, что значит простой текст. Сюда же включен самый ходовой формат html.

У типа textкак и у многих типов могут быть параметры, главным из них является charset.Он как раз и указывает на раскладку символов, которая применена в тексте, так что если вы хотите указать браузеру какую раскладку применять, то просто укажите charset:

Content-Type: text/plain; charset=us-ascii Content-Type: text/html; charset=utf-8 Content-Type: text/html; charset=koi8-r

multipart— данные, которые могут состоять из нескольких частей различных типов данных. Поэтому параметром multipartслужит boundary, позволяющий указать разделитель.

Каждый фрагмент в многочастевом сообщении имеет свой Content-Type:(он может быть также multipart, т.е. допускаются вложенные multipart, главное чтобы boundaryбыли разными).

В электронной почте применяется больше multipart/mixed(основной подтип) и multipart/alternative(он отличается тем, что показывается одна из альтернатив, например сообщение шлется в простом и HTMLформатах, и почтовая программа показывает часть, которую она способна отобразить).

В WWW-программировании распространен x-mixed-replace, который означает, что следующая часть должна заменить предыдущую после подгрузки, что применяется для анимации.

Теперь о разделителе. Его надо выбирать так, чтобы он не встретился где-то в данных (т.е. что-то вроде «diUr344rnmvforgefvrg923rghyj2»).

Когда вы задали разделитель, например boundary=«boundary»,то когда закончилась одна часть, вы должны выдать строку — boundary, последняя часть — boundary, причем эти разделители должны быть на отдельной строке, а не сливаться с текстом.

Пример:

MIME-Version: 1.0

Content-Type: multipart/alternative;

boundary="w23renff491nc4rth56u34-9449"

—w23renff491nc4rth56u34-9449

Content-Type: text/plain; charset="koi8-r"

Hello,World!!

—w23renff491nc4rth56u34-9449

Content-Type: text/html; charset="us-ascii"

<H1>Hello,Word!!</H1>

<HR>

<FONT size=+1 color=red>Hello people!</FONT>

—w23renff491nc4rth56u34-9449—

message— представляет инкапсулированное почтовое сообщение. Используется в e-mail, а не в WWW.

image— некоторое графическое изображение (чаще всего image/ gif или image/jpeg).

audio— аудиоданные.

video— видеоданные.

application— бинарные данные какого-нибудь приложения. В том случае, если данное приложение может быть запущено, браузер запускает его.

Например при поступлении данных application/mswordбраузер спросит: нужно ли запустить Wordдля просмотра документа. При отсутствии нужного приложения браузер спросит: в каком файле сохранить данные.

Подтип octet-streamкак раз и означает поток байт информации, который и используется по умолчанию (к сожалению не все так гладко, известен глюк в Netscape Navigator’е, который вместо того, чтобы сохранить application/octet-stream,пытается его показать как text/plain,и тогда если это сгенерировано из CGI— ни к чему хорошему не приводит)

Что касается application, то вы можете тут смело извращаться, используя x-типы данных. Например:

application/x-fuck-to-netscape-navigator

Часто используемый параметр nameпозволяет указать имя файла.

Например:

Content-Type: application/msword; name="readme.doc" Что полезно при получении файлов через HTTP, причем этот параметр может применяться и для других типов, таких как imageили audio.

Например:

Content-Type: image/gif; name="myfoto.gif"

Content-Transfer-Encoding:

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

Например:

7bit 8bit quoted-printable base64 binary x-типы

MIME-Version:

Указывает версию MIME.

Браузеры

Теперь поговорим о разных браузерах. Вы знаете, что браузеры бывают разные: разных версий, на разных платформах, поддерживают разные тэги и глюки у них тоже разные.

Это могло попортить много нервов WEB-дизайнерам и конечно же — CGI-программистам.

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

Если вы используете JavaScriptдля своих страничек, то вы уже наверно использовали (или хотя бы вам в голову приходила мысль использовать) свойства navigator.AppName navigator.AppCodeName navigator.app Version navigator.userAgent:

<SCRIPT language="JavaScript"> if(navigator.AppName=="Netscape"){ /*Сделать что-нибудь специфичное для Netscape*/ }

else if(navigator.AppName=="Microsoft Internet Explorer"){

/*Сделать что-нибудь специфичное для Explorer*/

}

else{

/*Не делаем специфичных вещей-хрен его знает с каким браузером мы имеем дело*/

} </SCRIPT> или

<SCRIPT language="JavaScript"> if((navigator.AppName=="Netscape")&&(parseFloat (navigator.appVersion)<3.0)){

document.writeln(”Пользуетесь слишком старым браузером”);

} </SCRIPT>

Ну не волнуйтесь вы так!

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

В запросе он указывает User-Agent:,которое и попадает на сервере в переменную среду HTTP_USER_AGENT, которую и можно использовать.

Например, если в ней содержится Mozilla/3.01Gold (Win95;I),то значит вы имеете дело с Netscape(Mozilla— кодовое название Netscape Navigator’а), версии 3.01Gold.

Далее после имени и версии может следовать необязательная информация, как в приведенном примере о платформе Win95и о том, является ли версия U— для США (USA) или I— международной (International).

Такая информация необязательна (то есть, если браузер послал информацию User-Agent:,то гарантировано рассчитывать вы можете только на Название/Версия).

Допустим, ваш скрипт генерирует какие-то тэги, которые слишком старые браузеры не поддерживают, причем без них не обойдешься, они составляют всю «изюминку» сайта.

#!/usr/bin/perl

#oldbrowser.cgi

print "Content-Type: text/html\n\n";

if(defined ($ENV{‘HTTP_USER_AGENT’})){

$browser=$ENV{‘HTTP_USER_AGENT’};

($vers)=($browser=~/\/(\d+\.\d+)/);

if(($browser=~/mozilla/i)&&($vers<=2.0)){

print "<HTML><HEAD><TITLE>Too old!</TITLE></HEAD>";

print "<BODY bgcolor=\"red\" text=\"black\">";

print "<CENTER><H1>Ваш Netscape Слишком старый для этого сайта";

print "(старость не радость;))</H1></CENTER>";

print "</BODY></HTML>";

exit;

}

if(($browser=~/msie/i)&&($vers<=3.0)){

print "<HTML><HEAD><TITLE>Too old!</TITLE></HEAD>";

print "<BODY bgcolor=\"red\" text=\"black\">";

print "<CENTER><H1>Ваш Explorer устарел";

print "(а не пора ли сделать апгрейд хотя бы до 4.0 версии)</H1></CENTER>";

print "</BODY></HTML>";

exit;

}

}

print "<HTML><HEAD> ......... ";

Ну, уже почувствовали, насколько это здорово?!

А вот еще примерчик. Это из разряда того, что тэги бывают разные. Например, в Explorerесть тэг BGSOUND,предназначенный для проигрывания музыки на страничке (в Netscapeэтого тэга нет, поэтому для втыкания музыки приходится использовать подключаемые модули plugin).

Только не забывайте, что если вы не получили информацию о клиенте (так может быть, если например ваш скрипт вызвала какая-нибудь поисковая машина), то в этом случае не надо делать никаких предположений, а просто пусть ваш скрипт продолжает делать то, что должен был делать.

Этот пример позволит выбирать из списка файлов и загружать то, что пользователь хочет.

#!/usr/bin/perl #download.cgi sub urldecode{

local($val)=@_;

$val=~s/\+/ /g;

$val=~s/%([0-9a-hA-H]{2})/pack(‘C’,hex($1))/ge;

return $val;

} @Filelist=qw(index.html readme.txt jmj00.mid gunshot.wav foto.gif); @Sel_list=();

if($ENV{‘REQUEST_METHOD’} eq ‘GET’){$query=$ENV{‘QUERY_STRING’};} elsif($ENV{‘REQUEST_METHOD’} eq

‘POST’){sysread(STDIN,$query,$ENV{‘CONTENT_LENGTH’});} if($query eq ‘’){

#Если никаких данных не подано на обработку,то сгенерируем форму, которую и предложим заполнить пользователю.

print "Content-Type: text/html\n\n";

print "<HTML><HEAD><TITLE>File Downloading</TITLE></HEAD>";

print "<BODY bgcolor=\"white\">";

print "Выберите файлы которые вы хотите загрузить:<BR>";

print "<FORM METHOD=\"POST\">";

print "<SELECT NAME=\"file\" size=4 multiple>";

foreach(@Filelist){

print "<OPTION value=\"$_\">$_";

}

print "</SELECT><BR>";

print "<INPUT TYPE=\"Submit\" value=\"Download!\">";

print "</FORM>";

print "</BODY></HTML>"

} else{

@formfields=split(/&/,$query);

foreach(@formfields){

if(/^file=(.*)/){push(@Sel_list,urldecode($1));}

}

unless(@Sel_list){

print "Content-Type: text/html\n\n";

print "<HTML><BODY><CENTER><H1>Вы должны выбрать что-то из списка";

print "</H1></CENTER></BODY></HTML>";

}

else{

print "Content-Type: multipart/mixed;boundary= \"bhy3e23r4t34tne-htpo7678nneu4232y213vdg\"\n\n";

print "—bhy3e23r4t34tnehtpo7678nneu4232y213vdg\n";

foreach(@Sel_list){ print "Content-Type: application/x-qwerty; name=\"$_\"\n\n"; open(F,"$_"); print <F>;

close(F);

print "\n—bhy3e23r4t34tnehtpo7678nneu4232y213vdg\n";

} print "Content-Type: text/html\n\n"; print "<HTML><H1>Thats all folks!</H1></HTML>"; print "\n—bhy3e23r4t34tnehtpo7678nneu4232y213 vdg—\n"; } }

Обработка форм

Пришло время перейти к очень важной теме — обработке форм.

При всей простоте (кажущейся) — это едва ли не самое главное предназначение всего стандарта CGI. Куда бы вы не зашли, на любой уважающий себя сайт, везде вы встретите формы, которые вам предложат заполнить.

В этом деле можно положится только на CGI, так как Java и JavaScript, выполняющиеся на страничке у клиента не имеют доступа к серверу, на котором находится сайт.

Коротко вспомним о том, что происходит при рассматриваемом процессе поближе.

Итак, браузер требует у сервера определенный URL (это может быть как простой документ, так и сгенерированный CGI). В этом документе может содержаться форма. Отображая такой документ браузер также выводит элементы формы (кнопки, поля ввода, поля ввода пароля, переключатели, радио-кнопки, списки, текстовые области, скрытые поля).

И со всем этим добром пользователь может взаимодействовать. К форме естественно имеет доступ и встроенный язык программирования JavaScript — он может как использовать форму для своих нужд, не передавая CGI, так и помогать пользователю в заполнении формы.

После того, как пользователь заполнил форму он нажимает кнопку Submit, которая говорит, что форму надо отправить на сервер.

Браузер собирает все имена и значения элементов формы, кодирует их методом urlencodeи в зависимости от указанного в тэге FORMметода вызывает GETили POSTс указанным URL, передавая ему данные.

На сервере CGI-скрипту это попадает (в зависимости от метода) либо в переменную QUERY_STRING,либо на STDIN.

Скрипт может проверить данные, занести их в какую-нибудь базу данных, может как yahooвыполнить какой-нибудь поиск, может что-нибудь вычислить... Да мало ли что он может, все зависит только от нашей фантазии.

В конце концов скрипт выдает браузеру ответ, который он и отображает.

В этом ответе может содержаться все, что вашей душе угодно — от сообщения об удачном или неправильном запросе, до таких ответов, по сравнению с которыми yahooи altavistaповиснут от зависти, главное чтобы вам и тем, кто посещает ваш сайт, это нравилось.

А теперь немного о синтаксисе элементов форм, их описании и самое главное особенностях при обработке CGI-скриптом.

Итак небольшой экскурс в HTML.

FORM

<FORM action="http:// ...... cgi" method="GET"|"POST"

enctype="encodingType"

name="formName" target="windowName" onSubmit="Handler"> </FORM> Атрибуты:

action

Как раз и задает тот URL, который будет обрабатывать форму, если он опущен, то текущий URLдокумента (а он-то может быть сгенерирован нашим скриптом).

method

Задает метод GETили POST.

enctype

Обычно не задается, для форм он application/x-www-form- urlencod-ed— по умолчанию, и поддерживается всеми CGIскриптами.

Но если вы уж очень хотите, чтобы браузер послал вам данные в другом формате (например text/plain), то можете указать этот тип кодировки, только потом не жалуйтесь, что ваш скрипт не может разделить поля, или вообще начинает глючить, когда пользователь ввел какой-то спецсимвол.

name

Задается для JavaScript, чтобы обращаться к форме по имени, а не по номеру. Для CGIне играет никакой роли.

target

Может определять в какой фрейм отправить полученную информацию. Имеет значение во фреймосодержащих документах. Прозрачен для CGIобработки данных.

onSubmit

Определяет Java Script— обработчик активизации формы. Применяется для проверки Java Script’ом правильности заполнения. Опять таки прозрачен для CGI.

Пример типичной формы:

<FORM action="http://www.uic.nnov.ru/~paaa/ cgi-bin/test.cgi" method="POST">

......... Поля формы .........

</FORM>

Форма может содержать элементы. Элементы имеют имена, которые используются для кодирования пар имя=значение. Некоторые элементы не передаются CGI, а используются Java Script для управления, например кнопки. Некоторые поля передаются только в тех случаях, когда в них что-то выбрано, например списки и переключатели. Остальные поля передаются всегда, даже когда они пустые.

Например:

<FORM action="http://www.doom/cgi-bin/test.cgi">

Your Name:<INPUT name="Name"><BR>

E-Mail:<INPUT name="Email"><BR>

Are you doomer:<INPUT type="checkbox" name="doomer" value="Yes">

<INPUT type="submit" value="Send Form!">

</FORM>

Допустим, вы ввели имя leshaи адрес paaa@uic.nnov.ru, при этом выбрали переключатель. После нажатия кнопки будет отправлен вот такой запрос:

http://www.doom/cgi-bin/test.cgi? Name=lesha&Email=paaa@uic.nnov.ru&doomer=Yes

Если же вы не выбрали переключатель, то запрос будет таким:

http://www.doom/cgi-bin/test.cgi? Name=lesha&Email=paaa@uic.nnov.ru

Как видите, элемент doomerне вошел в строку запроса Теперь попробуйте оставить поля редактирования пустыми: http://www.doom/cgi-bin/test.cgi?Name=&Email= Эти элементы (Nameи Email) присутствуют и сообщают что они пустые.

Кнопка (button)

<INPUT type="button" name="buttname" value="Текст На Кнопке" onClick="Handler">

В форме изображается кнопка, при нажатии которой вызывается JavaScript-обработчик,заданный атрибутом onClick.Атрибут nameслужит для JavaScript-именованиякнопки, а не для передачи CGI. Так как значение кнопки не передается CGI, valueзадает текст, изображаемый на кнопке.

<FORM onSubmit="return false;">

<INPUT type="button" value="Просто Кнопочка"