См. архив на Уране.
Клиент может запросить у веб-сервера как документ-файл с диска, так и документ, динамически формируемый некоторой внешней программой (как правило - в зависимости от данных, предоставленных пользователем при заполнении формы). Интерфейс CGI представляет собой спецификацию взаимодействия веб-сервера и внешней программы, которую веб-сервер запускает для обработки запроса. (Внешняя программа, вне зависимости от своей природы, часто называется CGI-скриптом.)
CGI определяет каким образом данные, предоставленные клиентом в запросе, передаются программе, как программа возвращает сгенерированный HTML-контент серверу, и какие переменные окружения устанавливаются сервером при запуске программы. Переменные окружения несут дополнительную информацию о сервере и запросе (например, тип сервера, IP-адрес клиента и др.).
Данные из заполненной клиентом HTML-формы могут передаваться на сервер двумя методами: GET и POST, это определяется параметром method соответствующего тэга <form method=... action=...>. В первом случае (GET) данные присоединяются после вопросительного знака в конец URL, указанной в параметре action, во втором случае - передаются в теле запроса - в секции, предназначенной для данных (следует после всех заголовокв и пустой строки). В обоих случаях данные кодируются одинаково - см. след. пункт.
При вызове CGI-программы все, что поступило в теле запроса, подается программе на стандартный ввод, а все, что находится в URL после вопросительного знака, помещается в переменную окружения QUERY_STRING. Веб-сервером данные запроса никак не интерпретируются и не преобразуются, эти задачи возложены на CGI-программу.
CGI-программа выдает содержимое ответа (как правило, HTML-контент) на свой стандартный вывод, который перехватывается веб-сервером с тем, чтобы отослать эти данные клиенту. Предварительно CGI-программа должна напечатать заголовок "Content-Type" и отделить его от данных пустой строкой. Например, вывод CGI-программы, генерирующей HTML, может выглядеть следующим образом:
Content-Type: text/html <HTML> <BODY> <H1>Hello, world</H1> </BODY> <HTML>
Для того, чтобы Apache воспринимал все файлы, находящиеся в некотором каталоге как CGI-скрипты, нужно использовать директиву
ScriptAlias /виртуальный/путь/ /путь/к/каталогу/ ScriptAlias /cgi-bin/ /usr/local/www/cgi-bin/Это означает, что для обработки запроса URL вида http://your.server.com/cgi-bin/dir/script будет взят не файл script из каталога DocumentRoot/cgi-bin/dir/, а запущена программа /usr/local/www/cgi-bin/dir/script.
Для смешанного хранения файлов, подлежащих просмотру, и CGI-скриптов в одном каталоге внутри дерева DocumentRoot следует присвоить CGI-скриптам одинаковые расширения (например, ".cgi") и указать серверу, что интерпретировать такие файлы следует как CGI-скрипты:
AddHandler cgi-script .cgiДиректива AddHandler может быть использована в любом контексте конфигурации Apache.
Для работы CGI-программ важное значение имеют части URL, называемые PATH_INFO и QUERY_STRING. Рассмотрим запрос с URL вида
http://my.server.com/cgi-bin/dir/prog/a/b?A=1&B=qwerty
Используя директиву ScriptAlias, приведенную в предыдущем пункте, сервер определяет что произошло обращение к CGI-программе и для поиска этой программы заменяет начальное /cgi-bin/ на /usr/local/www/cgi-bin/. Следуя запрошенному URL, сервер обнаруживает в этом каталоге подкаталог dir, однако подкаталога prog в каталоге /usr/local/www/cgi-bin/dir не обнаружено. В таком случае сервер предполагает, что prog - имя CGI-программы, подлежащей выполнению. Если программа /usr/local/www/cgi-bin/dir/prog не найдена или не может быть исполнена, сервер возвращает клиенту ошибку 403, 404 или 500. В противном случае программа prog запускается, а оставшаяся часть пути из URL - /a/b - передается программе prog в переменной окружения PATH_INFO. Таким способом можно передать в CGI-программу дополнительные параметры.
Все, что находится после вопросительного знака - A=1&B=qwerty - передается программе prog в переменной окружения QUERY_STRING. Это могут быть данные из заполненной пользователем формы, отправленные на сервер методом GET, либо какая-то другая информация (сервер не делает никаких предположений об интерпретации данных в QUERY_STRING, это задача вызываемой программы).
Данные из полей формы, заполненной пользователем - независимо от метода (POST или GET), которым они пересылаются на сервер - кодируются следующим образом:
имя_поля=значение_поля&имя_поля=значение_поля...
Пары имя-значение разделяются амперсандом. Алфавитно-цифровые символы и некоторые знаки препинания, не имеющие специального значения (тире, подчеркивание) передаются как есть. Остальные символы кодируются в виде "%NM", где NM - двузначный шестнадцатеричный код символа. Пробел может передаваться как "%20" или как символ "+". Кириллические символы также должны кодироваться указанным способом. Кодировка производится броузером при отправке полей заполненной формы.
Например:
http://my.server.com/cgi-bin/dir/prog?birthday=11%2F05%2F73&name=John+Smithозначает, что в поле birthday пользователь внес "11/05/73", а в поле name - "John Smith".
Декодирование данных формы является задачей CGI-программы.
При пересылке данных формы, закодированных вышеописанным способом, методом POST клиент должен установить заголовок запроса Content-Type следующим образом:
Content-Type: application/x-www-form-urlencoded
При запуске CGI-скрипта веб-сервер устанавливает дополнительные переменные окружения:
Переменная | Значение |
---|---|
AUTH_TYPE | Метод аутентифицирования, использованный для опознания пользователя. См. также REMOTE_USER и REMOTE_IDENT. |
CONTENT_LENGTH | Длина данных запроса в байтах, переданных CGI-скрипту через стандартный ввод. |
CONTENT_TYPE | MIME-тип данных запроса. |
DOCUMENT_ROOT | Корневой каталог дерева документов веб-сервера (определяется директивой DocumentRoot). |
GATEWAY_INTERFACE | Используемая версия CGI. |
HTTP_ACCEPT | Список MIME-типов данных, которые клиент может принять. |
HTTP_FROM | Адрес электронной почты пользователя, сделавшего запрос (многие броузеры не передают такие данные). |
HTTP_REFERER | URL документа, в котором находилась ссылка, вызвавшая настоящий запрос. |
HTTP_USER_AGENT | Броузер клиента. |
PATH_INFO | PATH_INFO (если есть) - см. выше "Структура URL и кодирование данных запроса" |
PATH_TRANSLATED | PATH_INFO, преобразованное в полный путь в файловой системе сервера (PATH_INFO, добавленное к DOCUMENT_ROOT). |
QUERY_STRING | Данные запроса, переданные в составе URL вслед за вопросительным знаком - см. выше "Структура URL и кодирование данных запроса". |
REMOTE_ADDR | IP-адрес клиента. |
REMOTE_HOST | Имя DNS клиента. |
REMOTE_USER | Аутентифицированное имя пользователя. |
REQUEST_METHOD | Метод запроса (GET, POST, HEAD и т.д.). |
SCRIPT_NAME | Виртуальный путь (например, /cgi-bin/program.pl) к исполняемому CGI-скрипту. |
SERVER_NAME | DNS-имя сервера или, при невозможности определить имя, его IP-адрес. |
SERVER_PORT | Номер порта сервера. |
SERVER_PROTOCOL | Имя и версия протокола, через который был сделан запрос (например, HTTP/1.1). |
SERVER_SOFTWARE | Тип и номер версии ПО веб-сервера. |
С Apache поставляется стандартный тестовый скрипт test-cgi, выводящий занчения переменных окружения CGI.
Основной проблемой при написании интерактивных CGI-скриптов, т.е. скриптов, чьи последовательные вызовы одним пользователем логически связаны друг с другом, является проблема сохранения состояния. Дело в том, что в протокол HTTP рассматривает все поступающие на сервер запросы как независимые друг от друга. Соответственно, после обработки каждого вновь поступившего запроса CGI-скрипт полностью завершает свою работу, а для обработки следующего запроса, неважно относится ли он к тому же логическому сеансу работы пользователя или нет, скрипт начинает свою работу с нуля без всякой информации о предыстории.
Примерами ситуаций, когда требуется сохранение состояния, являются: процесс последовательной регистрации, когда регистрант должен заполнить несколько форм, причем очередная форма зависит от результата заполнения предыдущей; шоппинг он-лайн, когда пользователь собирает покупки в корзину по мере своего движения по сайту; тесты и викторины, когда пользователь последовательно отвечает на вопросы.
Существует несколько методов сохранения состояния:
Два последних метода реализуют сохранение состояния на стороне сервера.
База данных
В качестве параллельно работающей базы данных может выступать любая из имеющихся СУБД, для обращения к которой язык программирования скрипта имеет интерфейс (в Перле есть библиотеки, обеспечивающие взаимодействие со всеми распространенными СУБД).
Также существует решение в виде демона, который запускается параллельно с http-сервером, и сохраняет требуемую информацию в своей оперативной памяти в виде переменная=значение. Для записи или извлечения данных скрипт соединяется с демоном по заранее оговоренному порту TCP или UDP, идентифицирует себя и использует набор простых команд типа "save name=value" и "extract name" (возвращается value).
Интересно, что несмотря на сложность реализации, такое решение (или использование СУБД с возможностью доступа по сети) позволяет разделять данные между скриптами работающими на различных серверах (если реализуется какая-то сложная распределенная интерактивная веб-система), при этом не вовлекается сохранение данных на стороне пользователя.
Файл
Основным недостатком сохранения данных в файле, кроме использования дискового пространства и накладных расходов на файловые операции, является операция записи на диск как таковая. Запись на диск может быть источником серьезных проблем в плане безопасности, так как работа CGI-скрипта фактически управляется воздействием внешних пользователей, которые могут иметь враждебные намерения. Путем посылки каких-либо специальных данных неаккуратно написанному скрипту можно вызвать серьезный сбой в работе сервера. Если же скрипт имеет право записи на диск, то последствия могут быть гораздо серьезнее, поэтому обычно CGI-скрипты, как и сам веб-сервер, работают с минимальными привилегиями от имени пользователя nobody без права записи на диск.
Сохранение состояния на стороне пользователя
Сохранение данных состояния на стороне пользователя (cookies и, технически, скрытые поля) существенный недостаток: пользователь имеет полный доступ к сохраняемым данным и может их несанкционированно изменить (например, прочитать правильный ответ теста или изменить идентификатор пользователя). Достоинством является простая реализация.
Cookies - это данные вида имя=значение, которые, будучи получены от сервера, сохраняются броузером на диске пользователя для их возврата серверу при последующих запросах к этому или другому URL. Поскольку данные сохраняются на диске, они могут быть использованы после перезапуска броузера.
Сервер передает cookie через специальное поле заголовка HTTP-ответа "Set-Cookie". Броузер возвращает cookie также через специальное поле в загловке HTTP-запроса - "Cookie". На стороне сервера cookie формируется, как правило, скриптом, который просто выводит в STDOUT соответствующий заголовок. Передача данных, полученных через cookie, от броузера в скрипт производится сервером через установку переменной окружения HTTP_COOKIE, которая доступна внутри скрипта и содержит пары имя=значение, которые броузер передал внутри поля "Cookie" в заголовке своего запроса.
Формат поля Set-Cookie (HTTP-ответ)
Set-Cookie: имя=значение; Max-Age=секунды; Comment=текстовый_комментарий; Path=URI_или_часть_URI; Domain=домен_сервера; Secure ; Version=1
Все элементы, кроме имя=значение и Version, не являются обязательными. В заголовке одного ответа сервера может содержаться несколько полей Set-Cookie.
Пример: при обращении на "http://s.vvsu.ru/a/b/c" сервер выдал ответ с установленным полем в заголовке:
SetCookie: X=5; Version=1Это значит, что cookie должно возвращаться броузером при обращении на все URL вида "http://s.vvsu.ru/a/b/какое-то_имя_файла".
SetCookie: X=5; Domain=.vvsu.ru; Path=/a/; Version=1то броузер должен присоединять это cookie ко всем запросам URL вида: "http://имя_без_точки.vvsu.ru/a/b/некий_путь_или_никакого".
Формат поля Cookie (HTTP-запрос)
Cookie: имя=значение; Path=URI_или_часть_URI; Domain=домен_сервера; Version=1
Параметры Path и Domain включаются только если они были установлены в заголовке Set-Cookie. Если несколько cookie удовлетворяют параметру Path, то они указываются в одном заголовке Cookie друг за другом (через точку с запятой) в следующем порядке: первыми передаются cookie с более длинным параметром Path. Порядок следования при равенстве параметров Path не определяется.
Скрытые поля
Скрытое поле создается внутри формы с помощью тега
<input type=hidden name=name1 value=value1>
Когда броузер получает документ с этой формой, содержимое полей типа "hidden" не отображается и пользователь не знает об их существовании (если только не посмотрит в HTML-текст присланного документа). После того, как пользователь отправляет форму на сервер, пара "name1=value1" присоединяется к данным формы, которые будут обработаны вновь запущенным скриптом. Таким образом скрипт может получить данные о предыстории своей работы с пользователем. Например, при электронном шоппинге в скрытых полях может сожержаться список товаров, выбранных для покупки в других отделах, которые пользователь уже посетил в данном сеансе работы.
Недостатком этого метода (кроме указанной выше возможности доступа и изменения данных) является то, что данные сохраняются только во время одного сеанса работы броузера. Если броузер будет перезапущен, все данные будут утеряны и процесс взаимодействия со скриптом начнется с нуля.
SSI представляет собой механизм разбора HTML-документов на стороне сервера с целью обнаружения в документе и выполнения директив, добавляющих в документ дополнительную информацию.
Все директивы вставляются внутрь тэгов HTML-комментариев, что позволяет клиенту, в случае, если сервер не поддерживает SSI, игнорировать эти директивы. Директивы имеют следующий формат:
<!--#директива параметр(ы)="значение"-->
Ниже следует список основных директив SSI и их параметров.
<H1>Вы пришли на сервер, находящийся по адресу <!--#echo var="SERVER_NAME"-->...</H1>
<!--#include file="stuff.html"--> <!--#include virtual="/personal/stuff.html"-->Эта директива очень удобна для создания стандартных шапок и подвалов веб-страниц.
Размер файла archive.zip - <!--#fsize file="/archive.zip"--> bytes.
Дата последнего изменения: <!--#flastmod file="/this_dir/this_file.html"-->bytes.Формат вывода даты и времени может быть специфицирован параметром timefmt директивы config.
<!--#exec cmd="/bin/finger $REMOTE_USER@$REMOTE_HOST"--> На эту страницу заходили <!--#exec cgi="/cgi-bin/counter.pl"--> раз.В первом примере используется подстановка значений переменных окружения (см. CGI-переменные).
<!--#config errmsg="Файл не найден"-->
<!--#config sizefmt="abbrev"--> Размер файла archive.zip - примерно <!--#fsize file="/archive.zip"--> bytes.
Ниже приведены переменные SSI, которые можно использовать в директиве echo в дополнение к переменным CGI.
Вы читаете файл под названием: <!--#echo var="DOCUMENT_NAME"-->
Ссылка на этот документ: <!--#echo var="DOCUMENT_URL"-->
Сейчас <!--#echo var="DATE_LOCAL"-->
Этот файл был последний раз изменен <!--#echo var="LAST_MODIFIED"-->
Написать CGI-скрипт для игры в виселицу (угадывание слова по буквам).
Правила игры
Сервер загадывает слово из словаря и показывает его пользователю в замаскированном виде (буквы заменены звездочками). Пользователь имеет некоторое число попыток; во время каждой попытки он может угадать одну букву. Если пользователь правильно угадывает букву или называет букву, которую он уже использовал, попытка не засчитывается. Иначе число попыток уменьшается на единицу.
Если пользователь правильно угадывает букву, сервер демаскирует в отображении слова все вхождения угаданной буквы. В любом случае сервер добавляет предложенную пользователем букву в список использованных букв, который демонстрируется во время каждой попытки для удобства пользователя. Также демонстрируется число оставшихся попыток.
Игра прекращается, если число попыток стало равным нулю (пользователь проиграл) или если угаданы все буквы в слове (пользователь выиграл).
Если пользователь на какой-либо попытке предлагает более одной буквы, считается, что пользователь пытается угадать слово целиком. При верной догадке пользователь выигрывает, иначе проигрывает независимо от числа оставшихся попыток.
Реализация
Слова выбираются случайным образом из заданного текстового файла.
Соотношение числа попыток и длины слова разумно определяется программистом. Например, число попыток есть заданная функция от длины слова; число попыток жестко привязано к каждому слову в словаре; длина слов в словаре и число попыток являются константами; при определении числа попыток используется заявленный пользователем уровень сложности.
При первом обращении к скрипту выдается заставка и регистрационная форма вида:
Обсуждение
При написании скрипта следует непрерывно помнить следующее основное правило CGI-программиста:
С каждым новым HTTP-запросом скрипт начинает свою работу с нуля. В данном случае: в игру может играть несколько пользователей одновременно; каждый из них последовательно формирует несколько HTTP-запросов; но для обработки каждого очередного запроса, поступившего на сервер, неважно к какому этапу игры или пользователю он относится, связан ли он с предыдущими или другими поступившими одновременно запросами, скрипт запускается с нуля и не имеет никаких знаний о своих предыдущих запусках и о состоянии игорного процесса.
В связи с вышесказанным требуется решить две проблемы при обработке каждого вновь поступившего запроса (или, что тоже, при каждом запуске программы):
Если такую информацию получить не удается, считается, что это первое обращение пользователя и ему предлагается регистрационная форма.
О методах сохранения состояния в CGI-программировании см. соответствующий раздел выше.
В результате анализа информации о состоянии игры и содержимого заполненной пользователем формы (если таковое имеется в поступившем запросе) программа разветвляется для формирования одного из следующих ответов (HTML-текстов):
Дополнительное задание
Скрипт имеет методику для подсчета очков, заработанных в игре, (алгоритм подсчета - на усмотрение программиста) и ведет таблицу чемпионов (формируется скриптом по нажатию специальной кнопки, которая демонстрируется в регистрационной форме).
Также для каждого пользователя ведется таблица его результатов; слова, уже предлагавшиеся пользователю, независимо от того, угадал он их или нет, исключаются из списка предложений.
Предварительные требования: знание языка С, работа в Unix, понятие о регулярных выражениях. И, естественно, темы 5 и 6 курса "Технологии Интернет".
Архивы самого языка Перл и его библиотек распространяются свободно и доступны через сайты CPAN. Библиотеки Перла называются модулями (стандартное расширение .pm), а сами программы - скриптами. И модули, и скрипты являются текстовыми файлами.
Для установки языка Перл необходимо иметь в системе компилятор С (желательно, gcc). Распаковать архив с языком (на текущий момент 27.11.99 это perl5.005_03.tar.gz). В каталоге, куда распаковался архив, выполнить следующие команды:
./configure # configure задаст много вопросов, на все можно ответить нажатием Enter (т.е. принять значение по умолчанию) make # перейти в режим суперпользователя make install
По умолчанию perl установится в /usr/local (исполнимые файлы - в /usr/local/bin/, библиотеки - в /usr/local/lib/perl5/, документы справочника man - в /usr/local/man (по самому языку) и в /usr/local/lib/perl5/5.00503/man (по библиотекам). Следует добавить соответстующие пути в переменные окружения PATH и MANPATH (в файле .profile).
Для запуска программ, написанных на Перле надо подать команду
perl имя_файла_с_программойили просто ввести имя файла с программой, если в первая строка этого файла выглядит:
#!/usr/local/bin/perl
Перл поставляется с несколькими стандартными модулями. Для работы с CGI требуется взять из CPAN и установить следующие библиотеки (в указанном порядке):
Установка каждой библиотеки производится командой
perl Makefile.PL && make && make test && make installподанной в каталоге, куда распаковаля архив библиотеки. Для выполнения последней части команды (make install) требуются привилегии суперпользователя.
Библиотеки устанавливаюися в дерево /usr/local/lib/perl5/. Документы man по установленным библиотекам находятся в /usr/local/lib/perl5/5.00503/man.
В этом разделе дана минимальная необходимая информация по тем элементам Перла, которые чаще всего используются при написании скриптов. Перл здесь рассматривается как С-подобный язык, поэтому акцентируются отличия от С. Этот раздел ни в коей мере не претендует на описание языка Перл (многие моменты сознательно опущены); для этой цели обратитесь к Camel Book, Llama Book или книге Маслова.
Комментарий "отсюда и до конца строки" - символ "#".
Переменные бывают трех типов: скаляры, списки и хэши. Описывать переменные не нужно, если только вы не используете директиву "use strict"; иначе каждая переменная должна быть описана с помощью "my имя_переменной".
Скаляры
Скаляр содержит одно значение; это может быть строка или число, Перл сам определяет тип значения по контексту проводимых со скаляром операций. Например, при попытке конкатенировать два скаляра операцией "." они будут рассмотрены как строки, а при попытке сложить их значения операцией "+" - как числа. При проведении числовых операций над строками, значение которых не может быть интерпретировано как число, числовое значение такого скаляра считается нулевым (см. также ниже п. Операторы, выражения и операции).
Повтор специально для программистов на С: строковые значения не оканчиваются на символ "\0". Строковых значений как таковых вообще не существует, строка - это один из двух способов интерепретации значения скаляра, который сам по себе ни на что особенное не заканчивается.
Имена всех скалярных переменных обязаны начинаться на $ ($x, $my_variable_1).
my $x; # $x создается, равно undef $x; # ложно defined($x); # ложно $x=0; $x; # ложно defined($x); # истинно $x=5; $x; # истинно defined($x); # истинно $x=undef; # опять undef!
Списки и массивы
Списком (list) называется упорядоченная последовательность скалярных значений; порядковые номера (индексы) начинаются с нуля. Отдельно стоящие списки заключаются в скобки:
($x, "abc", 15)Обращение к элементу списка осуществляется путем указания индекса этого элемента в квадратных скобках:
$y=($x, "abc", 15)[1]; # $y="abc"
Переменная, значением которой является список, называется массивом (array). Имена всех массивов обязаны начинаться с @ (@array). При обращении к элементу массива знак @ заменяется на $:
$array[0] $array[$x] $array[-2] # второй элемент с конца(смысл: элемент массива - это скаляр, следовательно он начинается с $). Примеры формирования массивов:
@array=($x, "abc", 15); @array=($x, $y, @another_array); @array=(); #пустой списокКоличество элементов в массиве: scalar @array (см. также п. "Контексты" ниже); индекс последнего элемента: $last_index = $#array.
Извлечение части массива:
($a, $b, $c) = @array[3,4,5]; @sublist=@array[3,4,5];Соответственно, наоборот:
@array[3,4,5]=($a,$b,"xyz");производит присвоение значений части массива @array (если элементы @array[0-2] ранее отсутствовали, они создаются со значениями undef).
Двух- и более мерные массивы в явном виде не поддерживаются.
Функции для работы с массивами:
@list = reverse @array
@list = reverse список
push @array, $x
push @array, список
$x = pop @array
$x = shift @array
unshift @array, $x
unshift @array, список
@list = splice @array, $from, $length, список
@list = splice @array, $from, $length
@list = splice @array, $from
Хэши
Переменная-хэш представляет собой массив, элементы которого индексируются строками (ассоциативный массив). Порядок хранения элементов в хэше не определен. Имена всех переменных-хэшей обязаны начинаться на % (%my_hash). При обращении к элементу списка знак % заменяется на $ и используются фигурные скобки:
$hash{"key_A"} $hash{$x}Хэши могут быть сформированы с помощью объединения четного числа скаляров в скобки:
%hash=("abc", $x, "def", 15); # $hash{abc}=$x и $hash{def}=15 #запятые для удобо читаемости могут быть заменены на "=>" %hash=("abc" => $x, "def" => 15); %hash=(); #пустой хэшВообще, любой список (массив) может быть рассмотрен в качестве хэша, т.е. %hash=@array. При этом нечетные элементы списка станут ключами хэша, а четные - соответствующими значениями. Если число элементов в списке нечетное, то последний элемент сконструированного таким образом хэша будет существовать со значением undef ("неопределенность"). Если в списке есть одинаковые элементы на нечетных позициях, то в хэш будет внесено значение, ключом которого является последний из одинаковых нечетных элементов списка.
@array=('a',1,'b',2,'c'); # для 'c' нет пары %hash=@array; # получилось: $hash{c}=undef %hash=('a' => 1, 'b' => 2, 'a' => 3); # получилось: $hash{a}=3
Верно и обратное: любой хэш может быть рассмотрен как список. Так например работает инверсия хэша с помощью функции reverse, которая принимает в качетсве аргумента список и возвращает тот же список задом наперед (см. ниже).
Функции для работы с хэшами:
@array = keys %hash
@array = values %hash
($key, $value) = each %hash
exists $hash{$key}
delete $hash{$key}
%rev_hash = reverse %hash
Операторы, выражения и операции Перла во многом аналогичны языку С. Ниже рассмотрены отличия.
Каждое выражение в Перл вычисляется в списочном, либо скалярном контексте. То есть в списочном контексте предполагается, что значение выражения должно быть списком, а в скалярном - скаляром. Контекст определяется, например, типом переменной, стоящей в левой части оператора присвоения. Многие функции определяют контекст, в котором они были вызваны и возвращают разные результаты в зависимости от контекста. Например, функция localtime (текущее местное время) в скалярном контексте возвращает строку вида "Fri Nov 26 20:36:33 1999", а в списочном контексте - список из чисел: секунды, минуты, часы, день месяца, месяц и т.д. Для того чтобы форсировать скалярный контекст, можно использовать оператор scalar.
$x = localtime; # скалярный контекст @time = localtime; # списочный контекст ($sec,$min,$hour) = localtime[0,1,2]; # списочный контекст print scalar localtime; # скалярный контекст
Значением списка в скалярном контексте является длина списка:
$x=@array; # в переменную $x помещается длина списка @arrayЗначением скаляра в списочном контексте является список, состоящий из одного этого скаляра.
Значения выражений и операции
# открыть файл myfile.txt, присвоив ему дескриптор FILE, # или выйти из программы с сообщением об ошибке open (FILE, "myfile.txt") or die ("Cannot open file"); # вывести сообщение в случае существования элемента хэша exists $hash{"mykey"} and print "My key exists!"; # присвоить переменной $x значение 5, если $x имеет неопределеное или нулевое значение $x ||= 5;
$x = "Now is " . scalar localtime; $x .= " (time is local)."; # то же что $x = $x . " (time is local).";Сравнения скаляров как строк выполняются следующими операциями:
$a eq $b | Истинно, если $a и $b одинаковы |
$a ne $b | Истинно, если $a и $b неодинаковы |
$a lt $b | Истинно, если $a раньше по алфавиту, чем $b |
$a gt $b | Истинно, если $a позже по алфавиту, чем $b |
$a le $b | Истинно, если $a раньше по алфавиту или совпадает с $b |
$a ge $b | Истинно, если $a позже по алфавиту или совпадает с $b |
$a cmp $b | -1, если $a раньше по алфавиту, чем $b; 0, если $a и $b одинаковы; 1, если $a позже по алфавиту, чем $b |
Внимание, распространенная ошибка! Операции "== != > < >= <=" выполняют сравнение скаляров как чисел. Использование их в "скалярно-строковом" контексте приведет к неверным результатам. Например, "11">"2", но "2"gt"11". Более того, $a="xyz"; $b="qwerty"; $a==$b - истинно, поскольку как числа обе этих переменных равны нулю.
# читать из файла с дексриптором FILE по одной строке (оператор <FILE>) # и удалять символ перевода строки из конца прочитанной строки (функция chomp) while ($a=<FILE>) { chomp $a; } # то же самое, используется переменная $_ while (<FILE>) { chomp; }
Операторы
if (...) {...}; условие ? выражение_да : выражение_нет ; while(...) {...}; do {...} while (...); for (...;...;...;) {...};Отличия и дополнения рассмотрены ниже.
if ($a<$b) { $a=$b; } while ($a<$b) { some_function($a); }Однако для операторов ветвления в таком случае есть сокращенная "постфиксная" форма записи:
$a=$b if ($a<$b);
print "a=",$a; НО: join ':', split ('/', $a); или join ':', split ('/'), $a;
foreach $i (@array) { print $i; } # то же самое с использованием переменной $_ foreach (@array) { print; }
# присвоить переменной $x значение 5, если $x имеет неопределеное или нулевое значение unless ($x) { $x=5; } # то же, что и $x ||= 5;Будьте внимательны при программировании сложных условий с помощью unless - помните теорему де Моргана: отрицание произведения есть сумма отрицаний; отрицание суммы есть произведение отрицаний.
until ($array[$i++] == $x) {;}соответственно и do {...} until(...).
Управление циклами:
foreach $i (@array) { next if ($i<0); $sum += $i; }
# считывать строки из файла и выводить на печать, пока не встретится пустая строка while ($a=<FILE>) { chomp $a; last unless ($a); print $a, "\n"; }
# это не самый лучший алгоритм поиска одинаковых слов в двух списках, # зато здесь используется выход сразу из двух циклов M1: foreach $i (@array1) { foreach $j (@array2) { if ( $i eq $j ) { last M1; } } }
Строковые выражения заключаются в одинарные (') или двойные (") кавычки. Везде внутри двойных кавычек производится подстановка переменных - скаляров, элементов списков и элементов хэшей; целые списки подставляются как подстрока в которой все элементы списка следуют друг за другом без разделителя; хэши целиком не подставляются. Для экранирования спецсимволов внутри двойных кавычек используется обратный слэш: \$, \@, \", \\; также распознаются все спецсимволы языка С: \n, \t и т.п.
$x="abc"; @array=('c','d','e'); %hash=( a => "A", b=> "B"); print "this is \$x: \"$x\"; \nthis is element 2 of \@array: \"$array[2]\";\n", "and \$hash{a} is \"$hash{a}\"\n"; ВЫВОД: this is $x: "abc"; this is element 2 of @array: "e"; and $hash{a} is "A"
Строки, употребляемые как ключи хэшей, если это буквальные строки без подстановок ("abc"), в кавычки можно не заключать: $hash{abc}.
Внутри одинарных кавычек никакие подстановки не производятся, спецсиволы типа "\n" не интепретируются и все символы воспринимаются буквально; исключение: комбинация \' (обратный слэш - одинарная кавычка), которая интепретируется как одинарная кавычка, являющаяся частью строки. Одинарная кавычка внутри двойных кавычек воспринимается буквально.
Регулярные выражения
Регулярные выражения (РВ) Перла - надмножество РВ grep/awk, используемых в Unix. В РВ следующие символы имеют специальное значение:
\ | ( ) [ { ^ $ * + ? . /
Символы "]" и "}" имеют специальные значения, только когда встречаются после соответственно "[" и "{". Все прочие символы в РВ воспринимаются буквально. Символ "/" имеет специальное значение не в регулярном выражении, а как признак начала и конца регулярного выражения (см. ниже примеры). Ниже кратко описаны наиболее часто используемые метасимволы РВ и их значения.
. (точка)
*
+
?
^
$
[символы] [^символы]
(РВ)
|
\s
\w
\d
\b
\S, \W, \D, \B
$переменная
\
Регулярные выражения заключаются в пару слэшей: /РВ/, после которых могут следовать модификаторы:
Обратите внимание, что метасимволы $, ^, \b не поглощают символы обрабатываемой строки, т.е. например, ^ - это не первый символ строки, а начало строки как таковое; аналогично, \b - это не первый или последний символ слова, а граница между символами типа \W и \w. Иначе говоря, слово cat соответствует РВ /^c/, а слово act ему не соответствует; или в строке "a yellow cat" регулярному выражению /\byellow\b/ соответствует подстрока "yellow", а не " yellow " или "ello".
Оператор сопоставления
Поиск РВ в строке:
$s =~ /РВ/модификаторы; $s !~ /РВ/модификаторы;Операция возвращает в скалярном контексте "Истинно", если строка $s (не) соответствует РВ, иначе возвращается "Ложно". Если $s не указано, используется $_.
if ( $d =~ /vvsu\.ru$/ ) { ... } if ( /^From: /i) { ... }
Оператор замены
Замена части строки:
$s =~ s/РВ/замена/модификаторы;Операция производит поиск подстроки строки $s, соответствующей РВ, после чего заменяет эту подстроку на замену. Если указан модификатор g, то заменяются все подстроки, соответствующие РВ, иначе заменяется только первая найденная подстрока. Операция возвращает количество произведенных замен. Если $s не указано, используется $_. Подстановку переменных можно использовать и в РВ, и в замене. В части замена можно использовать также спецсимволы $1,$2,..., которые произведут подстановку соответствующей части РВ, взятой в скобки. Примеры:
#в строке $x поменять первые два поля местами; #$1 ссылается на первое выражение в скобках в первой части оператора, $2 - на второе $x =~ s/([^ ]*) *([^ ]*)/$2 $1/; #убрать точку с конца строки $_ s/\.$//; #заменить все слова white на black в переменной $_ s/\bwhite\b/black/ig; #заменить в $_ все выражения #include_date на текущую дату #используется модификатор e, говорящий о том, #что вторая часть оператора должна быть воспринята как Перл-код s/#include_date/localtime/eg; #то же самое, но текст "#include_date" хранится в переменной $date_token s/$date_token/localtime/eg;
Чтение и запись файлов осуществляется через переменную-дескриптор файла. Имя такой переменной не имеет специального префикса и, как правило, записывается прописными буквами. Дексриптор создается при вызове функции open. Дескрипторы файлов не требуется предварительно объявлять даже при использовании "use strict".
Открытие файла
open (FILE, "filename") or die ("Cannot open filename: $!");Функция open возвращает "ложно" в случае невозможности открыть файл, в этом случае выполняется функция die, которая выводит свой аргумент на печать и завершает работу скрипта. Переменная S! - стандартная переменная Перла, содержит описание последней возникшей системной ошибки.
FILE - дескриптор файла, он будет использован ниже в операциях чтения, записи и закрытия файла.
В приведенном примере файл filename открывается для чтения. Для открытия файла на запись с нуля его имя нужно предварить символом ">" (если файл до этого существовал, его содержимое пропадет); для открытия на добавление в конец - предварить символами ">>":
open (FILE, ">filename") or die ("Cannot open filename: $!"); open (FILE, ">>filename") or die ("Cannot open filename: $!"); #открыть для чтения и записи open (FILE, "+>filename") or die ("Cannot open filename: $!");Для любого скрипта при его запуске по умолчанию открываются дескрипторы STDIN, STDOUT и STDERR.
Чтение из файла
Оператор <FILE> производит чтение из файла с дескриптором FILE. В скалярном контексте (например, в условии цикла while) оператор при кадом вызове возвращает очередную строку текста до следующего символа перевода строки включительно. В списочном контексте возвращается список всех текстовых строк от текущей позиции до конца файла.
# скалярный контекст while (<FILE>) { chomp; if ($_ eq "this text") { do_someting(); } } # списочный контекст @all_lines=<FILE>;
Внимание, распространенная ошибка! Строка, считанная из файла, скорее всего оканчивается на символ перевода строки (если только это не последняя строка в файле и при этом файл не заканчивается переводом строки). Про этот символ часто забывают и пытаются сравнивать считанное значение с неким другим значением, не содержащим, разумеется, перевода строки. Используйте функцию chomp для того, чтобы избавиться от этого символа на конце строки.
Еще одна распространенная ошибка! Оператор <> (внутри скобок пусто) считывает не из стандартного ввода, а из файлов, имена которых указанны как аргументы командной строки. При завершении одного файла ввод продолжается с начала следующего файла в том порядке, в каком имена файлов указаны в командной строке. Открывать файлы при использовании оператора <> не требуется.
Запись в файл
print FILE "эта строка печатается в FILE\n";
Закрытие файла
close FILE;Хотя при выходе из скрипта Перл и закрывает все незакрытые файлы, настоятельно рекомендуется делать это явным вызовом функции close во избежание различных побочных эффектов.
Вывод в программу
Для передачи данных в стандартный ввод другой программы нужно создать с помощью open соответствующий дескриптор файла (при этом программа будет запущена) и вывести в него данные. Для создания такого дескриптора вторым аргументом в функцию open передается путь к программе, предваренный символом "|":
open (MAIL, "|/usr/bin/mail $user") or die($!); print MAIL "Subject: Happy birthday!\n\n", "Hi, $user! Happy birthday!"; close MAIL;
Ввод из программы
Для приема данных из стандартного вывода другой программы нужно создать с помощью open соответствующий дескриптор файла (при этом программа будет запущена) и ввести из него данные. Для создания такого дескриптора вторым аргументом в функцию open передается путь к программе, после которого следует символ "|":
open (LS, "/usr/bin/ls $dir |") or die($!); @dir_listing= <LS>; close LS;
Переименование, удаление файла и изменение его атрибутов
# переименовать файл rename $oldname, $newname or die($!); # удалить файлы (возвращает количество успешно удаленных файлов) unlink $this_file, $that_file, @and_whole_list_of_files; # изменить атрибуты файлов # (возвращает количество файлов у которых атрибуты были изменены) chmod 0755, @array_of_filenames; # изменить владельцев файлов ($uid и $gid должны быть числовыми) # (возвращает количество файлов у которых владельцы были изменены) chown $uid, $gid, @array_of_filenames;Запуск другой программы
Для выполнения другой программы из скрипта используется функция system, аргументом которой является список argc[] (т.е. командная строка вывываемой программы, начинающаяся с имени самой программы).
system $program_to_run, $argument1, $argument2, ...;Функция возвращает 0 при успешном завершении вызванной программы и X*256, если статус выхода программы был X.
В этом пункте описаны некоторые полезные функции Перла для работы со строками (функции для работы с регулярными выражениями рассмотрены выше).
Разбор строки
Функция split разбивает скалярное значение, интерпретируемое как строка, на части, используя указанный разделитель, и возвращает результат в виде списка.
@список = split; @список = split разделитель; @список = split разделитель, строка; @список = split разделитель, строка, лимит;
Лимит указывает, что строка будет разбита не более, чем на лимит частей (иными словами, будут восприняты только первые лимит-1 разделителей; весь остаток будет возвращен в последнем элементе списка). Если лимит отсутствует, то все встретившиеся в строке разделители будут участвовать в разбиении.
Если строка отсутствует, обрабатывается $_.
Разделитель может быть регулярным выражением (заключается в слэши: /РВ/) или буквальной строкой (заключается в кавычки: 'строка'). Если разделитель отсутствует, то подразумевается /\s+/ ("один или более пробельных символов подряд"); т.е. split без аргументов экивалентен "split /\s+/, $_".
Пример:
# вывод списка всех пользователей системы с их идентификаторами UID open (PASSFILE, "/etc/passwd") or die($!); while (<PASSFILE>) { chomp; ($user, $junk, $uid) =split ':'; #ненужные элементы списка отбрасываются print "User $user has UID $uid\n"; };
Формирование строки из списка элементов
join
join 'разделитель', @список;
Функция join формирует скаляр-строку, состоящую из элементов списка, отделяя значения элементов друг от друга разделителем.
$_ = join ' | ', "a", "b", "c"; print; #ВЫВОД: "a | b | c" # заменить разделители с двоеточия на запятую с пробелом $_ = join ', ' (split, ':');
sprintf
Функция sprintf работает так же, как в языке С.
Поиск подстроки в строке
index
$позиция = index $строка, $подстрока, $начальная_позиция; $позиция = index $строка, $подстрока;Функция index возвращает позицию в строке, с которой начинается подстрока (имеется в виду первое вхождение подстроки, если их несколько). Если указана начальная позиция, то поиск начинается с этой позиции, иначе - с начала строки. Позиции в строке нумеруются, начиная с нуля. Если подстрока не найдена, функция возвращает -1.
# последовательный поиск всех вхождений $lookfor в строку $string $pos = -1; while (($pos=index($string, $lookfor, $pos)) > -1) { print "Found at position $pos\n"; $pos++; }
rindex
Функция rindex аналогична index, но возвращает позицию последнего вхождения подстроки в строку. В этом случае третий аргумент, если он указан, определяет позицию, на которой поиск следует прекратить.
Извлечение подстроки из строки в известной позиции
$подстрока = substr $строка, $смещение, $длина; $подстрока = substr $строка, $смещение;Функция substr извлекает из строки подстроку длиной длина, начинающуюся с позиции смещение. Если длина не указана, то подстрока извлекается от смещения до конца строки. Позиции в строке нумеруются, начиная с нуля.
Если смещение отрицательно, то оно отсчитывается не от начала, а от конца строки. Если длина отрицательна: длина=-N, то это значит, что извлекается подстрока такой длины, что она заканчивается за N символов до конца строки.
# все нижеследующие операции присваивают переменной $_ значение "abc" $_ = substr "xyzabcqqqq", 3, 3; $_ = substr "xyzabcqqqq", 3, -4; $_ = substr "xyzabcqqqq", -7, 3; $_ = substr "xyzabcqqqq", -7, -4;
Вставка/замена части строки в известной позиции
Для вставки подстроки в строку или замены одной подстроки другой подстрокой используется функция substr в левой части оператора присвоения. В этом случае первый аргумент функции substr обязан быть скалярным выражением, которому можно присвоить значение - скалярной переменной, элементом массива или элементом хэша. Примеры (перед каждым примером предполагается, что $a="abcdef"):
# 1) добавление подстроки "HELLO" в начало значения переменной $a substr($a,0,0) = "HELLO"; # в $a лежит "HELLOabcdef" # 2) замена второго и третьего символов в строке $_ словом "HELLO" substr($a, 1, 2) = "HELLO"; # в $a лежит "aHELLOdef" # 3) вставка слова "HELLO" между третьим и четвертым символами в строке $a substr($a, 3, 0) = "HELLO"; # в $a лежит "abcHELLOdef" # 4) замена последнего символа в строке $a словом "HELLO" substr($a, -1, 1) = "HELLO"; # в $a лежит "abcdeHELLO"
Вышеприведенные операции можно понимать так: из переменной $a извлекается указанная подстрока, а потом вместо нее в этой же позиции вставляется слово "HELLO".
Прочие функции
# удаляет один символ с конца строки $a; возвращает удаленный символ chop $a; # удаляет с конца строки символ перевода строки, если он там есть, # иначе строка остается без изменений; # возвращает количество удаленных символов chomp $a; #возвращает длину строки $a $l = length $a; #переводит все символы строки $a в верхний регистр uc $a; #переводит первый символ строки $a в верхний регистр ucfirst $a; #переводит все символы строки $a в нижний регистр lc $a;
Если аргументы вышеприведенных не указаны, подразумевается $_.
Функции описываются в любом месте скрипта с помощью конструкции
sub имя_функции { тело_функции; }и вызываются обычным образом:
имя_функции(аргумент1, аргумент2, ...); # со списком аргументов имя_функции(); # без аргументовСкобки при вызове написанных пользователем функций обязательны.
Количество и тип аргументов не декларируются и могут быть любыми и различными при последовательных вызовах одной и той же функции; все аргументы при передаче в функцию объединяются в единый список; внутри функции все аргументы доступны через массив @_ в том порядке, в каком они были указаны при вызове функции. Если аргументов не было, то массив @_ пуст. Хэш, переданный в качестве аргумента, преобразуется в список (о взаимоотношениях списков и хэшей см. выше п. "Типы переменных. Хэши").
sub function1 { # абсолютно бессмысленная функция - просто иллюстрация, как взять аргументы my $x = shift; # при этом первый аргумент удаляется из списка @_ # если @_ пуст, то $x=undef my ($y,$z) = @_; # а здесь аргументы из списка @_ не удаляются #(если они там вообще есть, иначе $y=$z=undef) print "Our first argument is ", defined $x ? $x : "empty", "\n"; $y ||=1; # а $y и $z присвоим значения по умолчанию $z ||=1; } sub function2 { # жутко полезная функция # предполагается, что в качестве аргументов поступают скаляр $k и хэш %h; # функция возвращает $h{$k} my $k = shift; # если $k пусто или в списке аргументов ничего не осталось - вернуть undef и выйти (defined $k and @_) or return undef; my %h=@_; return $h{$k}; } # вызов функции (например): $value=function2("my_key",%hash);
Каждая функция возвращает значение. Это может быть сделано явно с помощью оператора return, аргументом которого является скаляр или список. Если оператор return не встретился до конца функции, то возвращается значение последнего вычисленного выражения. Возвращаемое любой функцией значение может быть как использовано, так и проигнорировано вызывающей программой.
Каждая функция вызывается либо в скалярном, либо в списочном контексте; соответственным образом вызывающая программа интерпретирует возвращаемый функцией результат:
# ф-я f() возвращает список @array=f(); # списочный контекст - все тривиально $x=f(); # скалярный контекст - в $x помещается длина возвращаемого списка, а сам он пропадает # ф-я s() возвращает скаляр @array=s(); # списочный контекст - список @array теперь состоит из одного возвращенного значения $x=s(); # скалярный контекст - все тривиально
Для определения того, в каком контексте была вызвана функция, внутри нее используется оператор wantarray, возвращающий истину, если данная функция была вызвана в списочном контексте. Рассмотрим пример функции, которая возвращает список в списочном контексте и первый элемент списка - в скалярном.
sub f { # здесь как-то создается список @array # ... return wantarray ? @array : $array[0]; } push @biglist, f(); # списочный контекст, т.к. вторым аргументом функции push ожидается список $first=f(); # скалярный контекст
Внимание, распространенная ошибка! Если в функцию в качетсве аргументов переданы 2 массива: f(@ar1,@ar2), то внутри функции нет никаких способов определить, где в списке аргументов @_ кончается первый массив и начинается второй. Если внутри функции требуется различать эти массивы, то нужно передавать их в функцию в виде ссылок. Тоже самое касается и хэшей.
См. архив на Уране.
Клиент может запросить у веб-сервера как документ-файл с диска, так и документ, динамически формируемый некоторой внешней программой (как правило - в зависимости от данных, предоставленных пользователем при заполнении формы). Интерфейс CGI представляет собой спецификацию взаимодействия веб-сервера и внешней программы, которую веб-сервер запускает для обработки запроса. (Внешняя программа, вне зависимости от своей природы, часто называется CGI-скриптом.)
CGI определяет каким образом данные, предоставленные клиентом в запросе, передаются программе, как программа возвращает сгенерированный HTML-контент серверу, и какие переменные окружения устанавливаются сервером при запуске программы. Переменные окружения несут дополнительную информацию о сервере и запросе (например, тип сервера, IP-адрес клиента и др.).
Данные из заполненной клиентом HTML-формы могут передаваться на сервер двумя методами: GET и POST, это определяется параметром method соответствующего тэга <form method=... action=...>. В первом случае (GET) данные присоединяются после вопросительного знака в конец URL, указанной в параметре action, во втором случае - передаются в теле запроса - в секции, предназначенной для данных (следует после всех заголовокв и пустой строки). В обоих случаях данные кодируются одинаково - см. след. пункт.
При вызове CGI-программы все, что поступило в теле запроса, подается программе на стандартный ввод, а все, что находится в URL после вопросительного знака, помещается в переменную окружения QUERY_STRING. Веб-сервером данные запроса никак не интерпретируются и не преобразуются, эти задачи возложены на CGI-программу.
CGI-программа выдает содержимое ответа (как правило, HTML-контент) на свой стандартный вывод, который перехватывается веб-сервером с тем, чтобы отослать эти данные клиенту. Предварительно CGI-программа должна напечатать заголовок "Content-Type" и отделить его от данных пустой строкой. Например, вывод CGI-программы, генерирующей HTML, может выглядеть следующим образом:
Content-Type: text/html <HTML> <BODY> <H1>Hello, world</H1> </BODY> <HTML>
Для того, чтобы Apache воспринимал все файлы, находящиеся в некотором каталоге как CGI-скрипты, нужно использовать директиву
ScriptAlias /виртуальный/путь/ /путь/к/каталогу/ ScriptAlias /cgi-bin/ /usr/local/www/cgi-bin/Это означает, что для обработки запроса URL вида http://your.server.com/cgi-bin/dir/script будет взят не файл script из каталога DocumentRoot/cgi-bin/dir/, а запущена программа /usr/local/www/cgi-bin/dir/script.
Для смешанного хранения файлов, подлежащих просмотру, и CGI-скриптов в одном каталоге внутри дерева DocumentRoot следует присвоить CGI-скриптам одинаковые расширения (например, ".cgi") и указать серверу, что интерпретировать такие файлы следует как CGI-скрипты:
AddHandler cgi-script .cgiДиректива AddHandler может быть использована в любом контексте конфигурации Apache.
Для работы CGI-программ важное значение имеют части URL, называемые PATH_INFO и QUERY_STRING. Рассмотрим запрос с URL вида
http://my.server.com/cgi-bin/dir/prog/a/b?A=1&B=qwerty
Используя директиву ScriptAlias, приведенную в предыдущем пункте, сервер определяет что произошло обращение к CGI-программе и для поиска этой программы заменяет начальное /cgi-bin/ на /usr/local/www/cgi-bin/. Следуя запрошенному URL, сервер обнаруживает в этом каталоге подкаталог dir, однако подкаталога prog в каталоге /usr/local/www/cgi-bin/dir не обнаружено. В таком случае сервер предполагает, что prog - имя CGI-программы, подлежащей выполнению. Если программа /usr/local/www/cgi-bin/dir/prog не найдена или не может быть исполнена, сервер возвращает клиенту ошибку 403, 404 или 500. В противном случае программа prog запускается, а оставшаяся часть пути из URL - /a/b - передается программе prog в переменной окружения PATH_INFO. Таким способом можно передать в CGI-программу дополнительные параметры.
Все, что находится после вопросительного знака - A=1&B=qwerty - передается программе prog в переменной окружения QUERY_STRING. Это могут быть данные из заполненной пользователем формы, отправленные на сервер методом GET, либо какая-то другая информация (сервер не делает никаких предположений об интерпретации данных в QUERY_STRING, это задача вызываемой программы).
Данные из полей формы, заполненной пользователем - независимо от метода (POST или GET), которым они пересылаются на сервер - кодируются следующим образом:
имя_поля=значение_поля&имя_поля=значение_поля...
Пары имя-значение разделяются амперсандом. Алфавитно-цифровые символы и некоторые знаки препинания, не имеющие специального значения (тире, подчеркивание) передаются как есть. Остальные символы кодируются в виде "%NM", где NM - двузначный шестнадцатеричный код символа. Пробел может передаваться как "%20" или как символ "+". Кириллические символы также должны кодироваться указанным способом. Кодировка производится броузером при отправке полей заполненной формы.
Например:
http://my.server.com/cgi-bin/dir/prog?birthday=11%2F05%2F73&name=John+Smithозначает, что в поле birthday пользователь внес "11/05/73", а в поле name - "John Smith".
Декодирование данных формы является задачей CGI-программы.
При пересылке данных формы, закодированных вышеописанным способом, методом POST клиент должен установить заголовок запроса Content-Type следующим образом:
Content-Type: application/x-www-form-urlencoded
При запуске CGI-скрипта веб-сервер устанавливает дополнительные переменные окружения:
Переменная | Значение |
---|---|
AUTH_TYPE | Метод аутентифицирования, использованный для опознания пользователя. См. также REMOTE_USER и REMOTE_IDENT. |
CONTENT_LENGTH | Длина данных запроса в байтах, переданных CGI-скрипту через стандартный ввод. |
CONTENT_TYPE | MIME-тип данных запроса. |
DOCUMENT_ROOT | Корневой каталог дерева документов веб-сервера (определяется директивой DocumentRoot). |
GATEWAY_INTERFACE | Используемая версия CGI. |
HTTP_ACCEPT | Список MIME-типов данных, которые клиент может принять. |
HTTP_FROM | Адрес электронной почты пользователя, сделавшего запрос (многие броузеры не передают такие данные). |
HTTP_REFERER | URL документа, в котором находилась ссылка, вызвавшая настоящий запрос. |
HTTP_USER_AGENT | Броузер клиента. |
PATH_INFO | PATH_INFO (если есть) - см. выше "Структура URL и кодирование данных запроса" |
PATH_TRANSLATED | PATH_INFO, преобразованное в полный путь в файловой системе сервера (PATH_INFO, добавленное к DOCUMENT_ROOT). |
QUERY_STRING | Данные запроса, переданные в составе URL вслед за вопросительным знаком - см. выше "Структура URL и кодирование данных запроса". |
REMOTE_ADDR | IP-адрес клиента. |
REMOTE_HOST | Имя DNS клиента. |
REMOTE_USER | Аутентифицированное имя пользователя. |
REQUEST_METHOD | Метод запроса (GET, POST, HEAD и т.д.). |
SCRIPT_NAME | Виртуальный путь (например, /cgi-bin/program.pl) к исполняемому CGI-скрипту. |
SERVER_NAME | DNS-имя сервера или, при невозможности определить имя, его IP-адрес. |
SERVER_PORT | Номер порта сервера. |
SERVER_PROTOCOL | Имя и версия протокола, через который был сделан запрос (например, HTTP/1.1). |
SERVER_SOFTWARE | Тип и номер версии ПО веб-сервера. |
С Apache поставляется стандартный тестовый скрипт test-cgi, выводящий занчения переменных окружения CGI.
Основной проблемой при написании интерактивных CGI-скриптов, т.е. скриптов, чьи последовательные вызовы одним пользователем логически связаны друг с другом, является проблема сохранения состояния. Дело в том, что в протокол HTTP рассматривает все поступающие на сервер запросы как независимые друг от друга. Соответственно, после обработки каждого вновь поступившего запроса CGI-скрипт полностью завершает свою работу, а для обработки следующего запроса, неважно относится ли он к тому же логическому сеансу работы пользователя или нет, скрипт начинает свою работу с нуля без всякой информации о предыстории.
Примерами ситуаций, когда требуется сохранение состояния, являются: процесс последовательной регистрации, когда регистрант должен заполнить несколько форм, причем очередная форма зависит от результата заполнения предыдущей; шоппинг он-лайн, когда пользователь собирает покупки в корзину по мере своего движения по сайту; тесты и викторины, когда пользователь последовательно отвечает на вопросы.
Существует несколько методов сохранения состояния:
Два последних метода реализуют сохранение состояния на стороне сервера.
База данных
В качестве параллельно работающей базы данных может выступать любая из имеющихся СУБД, для обращения к которой язык программирования скрипта имеет интерфейс (в Перле есть библиотеки, обеспечивающие взаимодействие со всеми распространенными СУБД).
Также существует решение в виде демона, который запускается параллельно с http-сервером, и сохраняет требуемую информацию в своей оперативной памяти в виде переменная=значение. Для записи или извлечения данных скрипт соединяется с демоном по заранее оговоренному порту TCP или UDP, идентифицирует себя и использует набор простых команд типа "save name=value" и "extract name" (возвращается value).
Интересно, что несмотря на сложность реализации, такое решение (или использование СУБД с возможностью доступа по сети) позволяет разделять данные между скриптами работающими на различных серверах (если реализуется какая-то сложная распределенная интерактивная веб-система), при этом не вовлекается сохранение данных на стороне пользователя.
Файл
Основным недостатком сохранения данных в файле, кроме использования дискового пространства и накладных расходов на файловые операции, является операция записи на диск как таковая. Запись на диск может быть источником серьезных проблем в плане безопасности, так как работа CGI-скрипта фактически управляется воздействием внешних пользователей, которые могут иметь враждебные намерения. Путем посылки каких-либо специальных данных неаккуратно написанному скрипту можно вызвать серьезный сбой в работе сервера. Если же скрипт имеет право записи на диск, то последствия могут быть гораздо серьезнее, поэтому обычно CGI-скрипты, как и сам веб-сервер, работают с минимальными привилегиями от имени пользователя nobody без права записи на диск.
Сохранение состояния на стороне пользователя
Сохранение данных состояния на стороне пользователя (cookies и, технически, скрытые поля) существенный недостаток: пользователь имеет полный доступ к сохраняемым данным и может их несанкционированно изменить (например, прочитать правильный ответ теста или изменить идентификатор пользователя). Достоинством является простая реализация.
Cookies - это данные вида имя=значение, которые, будучи получены от сервера, сохраняются броузером на диске пользователя для их возврата серверу при последующих запросах к этому или другому URL. Поскольку данные сохраняются на диске, они могут быть использованы после перезапуска броузера.
Сервер передает cookie через специальное поле заголовка HTTP-ответа "Set-Cookie". Броузер возвращает cookie также через специальное поле в загловке HTTP-запроса - "Cookie". На стороне сервера cookie формируется, как правило, скриптом, который просто выводит в STDOUT соответствующий заголовок. Передача данных, полученных через cookie, от броузера в скрипт производится сервером через установку переменной окружения HTTP_COOKIE, которая доступна внутри скрипта и содержит пары имя=значение, которые броузер передал внутри поля "Cookie" в заголовке своего запроса.
Формат поля Set-Cookie (HTTP-ответ)
Set-Cookie: имя=значение; Max-Age=секунды; Comment=текстовый_комментарий; Path=URI_или_часть_URI; Domain=домен_сервера; Secure ; Version=1
Все элементы, кроме имя=значение и Version, не являются обязательными. В заголовке одного ответа сервера может содержаться несколько полей Set-Cookie.
Пример: при обращении на "http://s.vvsu.ru/a/b/c" сервер выдал ответ с установленным полем в заголовке:
SetCookie: X=5; Version=1Это значит, что cookie должно возвращаться броузером при обращении на все URL вида "http://s.vvsu.ru/a/b/какое-то_имя_файла".
SetCookie: X=5; Domain=.vvsu.ru; Path=/a/; Version=1то броузер должен присоединять это cookie ко всем запросам URL вида: "http://имя_без_точки.vvsu.ru/a/b/некий_путь_или_никакого".
Формат поля Cookie (HTTP-запрос)
Cookie: имя=значение; Path=URI_или_часть_URI; Domain=домен_сервера; Version=1
Параметры Path и Domain включаются только если они были установлены в заголовке Set-Cookie. Если несколько cookie удовлетворяют параметру Path, то они указываются в одном заголовке Cookie друг за другом (через точку с запятой) в следующем порядке: первыми передаются cookie с более длинным параметром Path. Порядок следования при равенстве параметров Path не определяется.
Скрытые поля
Скрытое поле создается внутри формы с помощью тега
<input type=hidden name=name1 value=value1>
Когда броузер получает документ с этой формой, содержимое полей типа "hidden" не отображается и пользователь не знает об их существовании (если только не посмотрит в HTML-текст присланного документа). После того, как пользователь отправляет форму на сервер, пара "name1=value1" присоединяется к данным формы, которые будут обработаны вновь запущенным скриптом. Таким образом скрипт может получить данные о предыстории своей работы с пользователем. Например, при электронном шоппинге в скрытых полях может сожержаться список товаров, выбранных для покупки в других отделах, которые пользователь уже посетил в данном сеансе работы.
Недостатком этого метода (кроме указанной выше возможности доступа и изменения данных) является то, что данные сохраняются только во время одного сеанса работы броузера. Если броузер будет перезапущен, все данные будут утеряны и процесс взаимодействия со скриптом начнется с нуля.
SSI представляет собой механизм разбора HTML-документов на стороне сервера с целью обнаружения в документе и выполнения директив, добавляющих в документ дополнительную информацию.
Все директивы вставляются внутрь тэгов HTML-комментариев, что позволяет клиенту, в случае, если сервер не поддерживает SSI, игнорировать эти директивы. Директивы имеют следующий формат:
<!--#директива параметр(ы)="значение"-->
Ниже следует список основных директив SSI и их параметров.
<H1>Вы пришли на сервер, находящийся по адресу <!--#echo var="SERVER_NAME"-->...</H1>
<!--#include file="stuff.html"--> <!--#include virtual="/personal/stuff.html"-->Эта директива очень удобна для создания стандартных шапок и подвалов веб-страниц.
Размер файла archive.zip - <!--#fsize file="/archive.zip"--> bytes.
Дата последнего изменения: <!--#flastmod file="/this_dir/this_file.html"-->bytes.Формат вывода даты и времени может быть специфицирован параметром timefmt директивы config.
<!--#exec cmd="/bin/finger $REMOTE_USER@$REMOTE_HOST"--> На эту страницу заходили <!--#exec cgi="/cgi-bin/counter.pl"--> раз.В первом примере используется подстановка значений переменных окружения (см. CGI-переменные).
<!--#config errmsg="Файл не найден"-->
<!--#config sizefmt="abbrev"--> Размер файла archive.zip - примерно <!--#fsize file="/archive.zip"--> bytes.
Ниже приведены переменные SSI, которые можно использовать в директиве echo в дополнение к переменным CGI.
Вы читаете файл под названием: <!--#echo var="DOCUMENT_NAME"-->
Ссылка на этот документ: <!--#echo var="DOCUMENT_URL"-->
Сейчас <!--#echo var="DATE_LOCAL"-->
Этот файл был последний раз изменен <!--#echo var="LAST_MODIFIED"-->
Написать CGI-скрипт для игры в виселицу (угадывание слова по буквам).
Правила игры
Сервер загадывает слово из словаря и показывает его пользователю в замаскированном виде (буквы заменены звездочками). Пользователь имеет некоторое число попыток; во время каждой попытки он может угадать одну букву. Если пользователь правильно угадывает букву или называет букву, которую он уже использовал, попытка не засчитывается. Иначе число попыток уменьшается на единицу.
Если пользователь правильно угадывает букву, сервер демаскирует в отображении слова все вхождения угаданной буквы. В любом случае сервер добавляет предложенную пользователем букву в список использованных букв, который демонстрируется во время каждой попытки для удобства пользователя. Также демонстрируется число оставшихся попыток.
Игра прекращается, если число попыток стало равным нулю (пользователь проиграл) или если угаданы все буквы в слове (пользователь выиграл).
Если пользователь на какой-либо попытке предлагает более одной буквы, считается, что пользователь пытается угадать слово целиком. При верной догадке пользователь выигрывает, иначе проигрывает независимо от числа оставшихся попыток.
Реализация
Слова выбираются случайным образом из заданного текстового файла.
Соотношение числа попыток и длины слова разумно определяется программистом. Например, число попыток есть заданная функция от длины слова; число попыток жестко привязано к каждому слову в словаре; длина слов в словаре и число попыток являются константами; при определении числа попыток используется заявленный пользователем уровень сложности.
При первом обращении к скрипту выдается заставка и регистрационная форма вида:
Обсуждение
При написании скрипта следует непрерывно помнить следующее основное правило CGI-программиста:
С каждым новым HTTP-запросом скрипт начинает свою работу с нуля. В данном случае: в игру может играть несколько пользователей одновременно; каждый из них последовательно формирует несколько HTTP-запросов; но для обработки каждого очередного запроса, поступившего на сервер, неважно к какому этапу игры или пользователю он относится, связан ли он с предыдущими или другими поступившими одновременно запросами, скрипт запускается с нуля и не имеет никаких знаний о своих предыдущих запусках и о состоянии игорного процесса.
В связи с вышесказанным требуется решить две проблемы при обработке каждого вновь поступившего запроса (или, что тоже, при каждом запуске программы):
Если такую информацию получить не удается, считается, что это первое обращение пользователя и ему предлагается регистрационная форма.
О методах сохранения состояния в CGI-программировании см. соответствующий раздел выше.
В результате анализа информации о состоянии игры и содержимого заполненной пользователем формы (если таковое имеется в поступившем запросе) программа разветвляется для формирования одного из следующих ответов (HTML-текстов):
Дополнительное задание
Скрипт имеет методику для подсчета очков, заработанных в игре, (алгоритм подсчета - на усмотрение программиста) и ведет таблицу чемпионов (формируется скриптом по нажатию специальной кнопки, которая демонстрируется в регистрационной форме).
Также для каждого пользователя ведется таблица его результатов; слова, уже предлагавшиеся пользователю, независимо от того, угадал он их или нет, исключаются из списка предложений.
См. архив на Уране.
HTTP (HyperText Transfer Protocol) - прикладной клиент-серверный протокол типа "запрос-ответ" работающий поверх TCP (стандартный порт 80). Клиентом является броузер; сервером - WWW-сервер. В первой версии протокола (1.0) на одно TCP-соединение приходился один запрос и ответ, после чего соединение закрывалось. В настоящее время протокол позволяет выполнить несколько итераций в рамках одного соединения, что позволяет избежать накладных расходов на установление/закрытие соединений, снизить нагрузку на сеть и уменьшить время получения данных.
Последняя версия HTTP 1.1 описана в RFC-2616,2617.
Запрос (Request) имеет следующий формат:
Поля, помеченные звездочкой (*), присутствуют только, если в запросе использован метод POST или PUT (см. ниже).
Строка запроса Заголовки запроса Заголовки данных* Пустая строка Данные*
Строка запроса имеет вид:
метод URI протокол/версияПримеры:
GET / HTTP/1.1 POST /cgi-bin/sript?X=1&Y=a%20b HTTP/1.1 HEAD http://www.vvsu.ru/index.html HTTP/1.1
Основные методы:
URI является указателем на запрашиваемый ресурс. При непосредственном обращении к серверу, содержащему запрашиваемый ресурс, URI имеет неполный вид (см. выше примеры GET и POST). При обращении через прокси-сервер URI должен быть полным (см. выше пример HEAD).
Заголовки, передаваемые вслед за строкой запроса, делятся на собственно заголовки запроса и на заголовки данных, передаваемых в запросе (разумеется, если такие данные присутствуют). Каждый заголовок начинается с новой строки и состоит из ключевого слова, за которым следует двоеточие, и данных, например:
Accept: text/html, text/plain;q=0.5
Порядок следования заголовков не имеет значения.
Ниже приведены примеры заголовков запроса (за полным списком и описаниями обращаться к RFC-2616). Заголовки данных рассмотрены ниже в отдельном пункте.
Host: athena.vvsu.ru
Accept: text/html, text/plain
Accept-Encoding: compress, gzip
Accept-Language: ru, en;q=0.2
User-Agent: Netscape/5.40; Solaris 2.5
Referer: http://athena.vvsu.ru/index.html
Authorization: Basic d2VibWFzdGVyOnpycW1hNHY=
Connection: Keep-Alive
If-Modified-Since: Wed, 03 Nov 1999 19:43:11 GMT
Range: bytes=0-499
Ответ (Response) имеет следующий формат:
Поля, помеченные звездочкой (*), могут отсутствовать - это зависит от типа запроса. Например при запросе методом HEAD возвращаются только статусная строка с кодом 200, заголовки ответа и данных, а сами данные не передаются; а при условном запросе If-Modified-Since, в случае если документ не модифицирован, возвращаются только статусная строка с кодом 304 и заголовок ответа без заголовков данных и самих данных.
Статусная строка Заголовки ответа Заголовки данных* Пустая строка Данные*
Статусная строка имеет вид:
протокол/версия код статусСтатус - текстовая строка, комментирующая код, предназначена для человека; программное обеспечение анализирует только числовое значение кода. Примеры статусных строк:
HTTP/1.1 200 OK HTTP/1.1 304 Not Modified
Код ответа является трехзначным числом. Коды разделены по группам в зависимости от первой цифры:
Сервер конфигурируется для возврата ответов с кодом 301 при реструктуризации его пространства документов - с тем, чтобы клиенты, использующие старые ссылки, перенаправлялись к новому расположению документов, а не получали ошибку 404 Not Found.
Заголовки, передаваемые вслед за статусной строкой, делятся на собственно заголовки ответа и на заголовки данных, передаваемых в ответе (если такие заголовки требуются). Каждый заголовок начинается с новой строки и состоит из ключевого слова, за которым следует двоеточие, и данных. Порядок следования заголовков не имеет значения.
Ниже приведены примеры заголовков ответа (за полным списком и описаниями обращаться к RFC-2616). Заголовки данных рассмотрены ниже в отдельном пункте.
Server: Apache/1.3.6 (Unix) rus/PL28.16
Date: Mon, 13 Mar 2000 07:38:48 GMT
Accept-Ranges: bytes
WWW-Authenticate: Basic realm="SysAdmin"
Location: http://new.url.com/
Connection: close
Allow: GET, HEAD, POST
Заголовки данных описывают данные, посылаемые в HTTP-запросе или ответе. В запросе обычно посылаются данные заполненных форм (метод POST). Заголовки данных следуют в заголовочной части HTTP-ответа или запроса вместе с заголовками ответа или запроса; порядок следования заголовков различныз типов не определен. Сами данные всегда отделяются от заголовочной части пустой строкой.
Ниже приведены примеры заголовков данных (за полным списком и описаниями обращаться к RFC-2616).
Content-Type: text/html; charset=koi8-r
Last-Modified: Sat, 11 Dec 1999 11:21:19 GMT
Expires: Mon, 14 Mar 2000 07:38:48 GMT
ETag: "8512-28a-3835276d-koi8-r"
Content-Length: 295
Content-Encoding: gzip
Content-Range: 0-294/1234
Apache - самый распространенный в Интернет WWW-сервер. Программа распространяется бесплатно в исходных текстах для Unix и Windows; проект поддерживается организацией Apache Software Foundation. Кроме собственно WWW-сервера ASF поддерживает еще ряд проектов, связанных с Apache, например, интеграция Apache и Перл (модуль mod_perl, см. ниже), проект Java-Apache.
Одним из основных достоинств Apache является его модульность и открытость API (Application Programming Interface), что позволяет
Общая схема работы Apache (на примере версии для Unix) такова:
Цикл обработки запроса клиента состоит из следующих фаз:
Конфигурирование сервера выполняется путем внесения директив в файл httpd.conf, расположенный в каталоге conf дерева установки сервера (см. ниже п. "Установка и работа с сервером") и считываемый сервером при запуске (перезапуске). Директивы могут находиться также и в других файлах, которые подключаются с помощью директивы "Include" в файле httpd.conf. Обычно Apache поставляется с файлом httpd.conf, уже содержащим примерную рабочую конфигурацию, причем каждая директива снабжена подробным комментарием на английском языке.
Существует несколько контекстов конфигурирования, в которых могут применяться те или иные директивы (для каждой директивы в справочной документации указываются все контексты, где она может быть применена):
<VirtualHost имя_виртуального_сервера> ..... #директивы, находящиеся здесь относятся только к запросам, в #заголовке Host которых указан данный виртуальный сервер ..... </VirtualHost>Сама по себе директива VirtualHost может находиться только в контексте server-config.
<Directory путь_к_каталогу> .... #директивы, находящиеся здесь относятся только к запросам файлов, #находящихся в указанном каталоге (или его подкаталогах); путь #отсчитывается от каталога, указанного директивой DocumentRoot .... </Directory> <Location начало_URI> .... #директивы, находящиеся здесь относятся только к запросам URI, #начинающихся со строки, указанной в директиве Location .... </Location> <Files рег._выражение_для_файлов> .... #директивы, находящиеся здесь относятся только к запросам файлов, #имена которых удовлетворяют указанному регулярному выражению .... </Files>
Сами по себе директивы Location, Directory и Files могут находиться как в контексте server config, так и в контексте virtual host. Более того, директива Files может находиться внутри раздела Directory, а также в контексте htaccess.
Если одна и та же директива в разных контекстах имеет разные значения, то действует значение в наиболее узком применимом к данному запросу контексте.
Apache предоставляет следующие возможности по контролю доступа к ресурсам веб-сервера:
Для реализации этого механизма применяются директивы allow, deny. order, AuthName, AuthType, AuthUserFile, AuthGroupFile, require, satisfy.
Возможности контроль доступа могут быть существенно расширены дополнительными модулями, написанными c использованием Perl-API (модуль mod_perl).
На стадии разбора URI сервер определяет файл, который был запрошен клиентом. Если на стадии определения MIME-типа документа было обнаружено, что файл должен быть интерпретирован как исполнимая программа, то для генерации ответа Apache запускает этот файл, в соответствии со спецификацями интерфейса CGI.
В Apache реализован механизм SSI, который представляет собой разбор HTML-документов на стороне сервера с целью обнаружения в документе и выполнения директив, добавляющих в документ дополнительную информацию. Директивы могут вставлять в HTML-документ другой HTML-файл или результат работы CGI-программы; вставлять характеристики документа (размер файла, дату последней модификации), текущее время и т.п.
Модуль mod_charset производит переобразование кириллических документов в кодировку, требуемую клиенту. При этом сначала рассматриваются заголовки запроса (Accept-Charset), потом, если требуемую кодировку определить не удалось, - конфигурационные директивы модуля mod_charset (CharsetSelectionOrder и др.), а в случае неудачи документ возвращается в кодировке, определенной директивой CharsetDeafult.
Кодировка, в которой документ хранится на диске, задается директивой CharsetSourceEnc, которую можно применять во всех конфигурационных контекстах.
Модуль mod_perl реализует интерфейс API к серверу Apache на языке Perl, что позволяет модифицировать поведение сервера на любой фазе обработки запроса в соответствии с конкретными требованиями пользователя (например производить аутентификацию не по файлу с именами пользователей и их паролями, а по базе данных). Кроме того, модуль позволяет существенно ускорить исполнение CGI-скриптов, написанных на Perl: теперь не запускается отдельного процесса Perl-интепретатора для каждого скрипта; интерпретатор находится внутри процесса веб-сервера; более того, однажды исполненный скрипт кэшируется в компилированном виде.
Модуль mod_jserv является интерфейсом сервера Apache к исполнителю сервлетов (servlet engine), написанных на языке Java. Интерфейс jserv сходен по функциональности с CGI, но обладает повышенной гибкостью.
Модуль mod_ssl реализует в Apache слой SSL, осуществляющий шифрование всего потока данных между клиентом и сервером. Для всех остальных частей веб-сервера модуль mod_ssl является прозрачным. Для работы в этом режиме, требуется броузер, поддерживающий мехнизм SSL (этому условию удовлетворяют все современные распространенные броузеры).
Установка сервера осуществляется в следующей последовательности:
./configure --prefix=/путь/установки/Apache(возможны различные дополнительные ключи, свзяанные с добавлением или удалением модулей);
Все компоненты сервера устанавливаются в дерево, корнем которого является каталог, указанный в параметре "--prefix"; вне этого дерева никакие компоненты программы не располагаются (это может быть неверно для дополнительных модулей - см. документацию к этим модулям). Этот же каталог должен быть значением конфигурационной директивы ServerRoot. Конфигурационный файл httpd.conf находится в каталоге ServerRoot/conf.
Запуск, перезапуск и останов сервера выполняются командой ServerRoot/bin/apachectl с параметрами start, stop, restart, graceful.
1. Запустить на своем компьютере (Unix) анонимный FTP-сервер. Создать каталог с "невидимым" содержимым для входящих файлов.
2. Сконфигурировать на своем компьютере WWW-сервер Russian Apache для перекодировки кириллицы по номеру порта (осуществлять перекодировку в win, koi, iso, dos и транслитерацию). В дереве документов организовать два каталога: в одном хранить документы в оригинальной кодировке win, в другом - в koi. Обеспечить разумный порядок определения выходной кодировки при обращении на стнадартный порт 80.
3. На один из каталогов назначить авторизованный доступ: с "разрешенных" компьютеров - доступ открытый; со всех остальных - по паролю.