Главная
Трудоустройство
Ваши объявления
Наши новости
Информация







Hello, Perl! Perl FAQ по-русски

Дмитрий Репин aka cmapuk[0nline]

ЧАСТЬ 3.








В этой части мы рассмотрим принципы работы с HTTP- и CGI-протоколами, а также начнём программировать CGI-приложения.


HTTP, как он есть

"GET мне вон тот mp3", - запросил клиент.
"404 OK RTFM", - ответил сервер.
c2002 cmapuk[0nline]

Казалось бы, что начать рассказ о практическом применении Perl я должен был со столь популярного (особенно среди начинающих) CGI-программирования. Но для того, чтобы толком понять, как работает CGI, надо понять принципы взаимодействия клиента (броузера) и сервера, где лежат cgi-скрипты.

Клиент и сервер - это, в простейшем варианте, консольные приложения, которые читают из стандартного ввода и пишут в стандартный вывод. Броузер, как программа для отображения страниц - всего лишь удобная красивая оболочка. Хотите убедиться? Пожалуйста!

Наберите в консоли telnet. В Win32, если откроется белое окошко, в меню выберите "Подключить", введите адрес perl.ru, а в поле "Порт" поставьте 80. Если окошко не открылось, а открылась консоль вида telnet>, наберите open perl.ru 80. После подключения просто введите следующее:

GET / HTTP/1.0
User-Agent:Shmozilla 3000

После этого вы получите на экран HTML-страницу с сервера. Вот и вся хитрость! Сервер и клиент обмениваются текстовой информацией по определенным правилам, совокупность которых и называется протоколом. В данном случае - HTTP.

Запрос к серверу состоит из трех частей, в зависимости от метода запроса GET или POST. Есть и другие методы, но они редко используются и мы их рассматривать не будем.

Запрос методом GET: Получить.

GET /path/to/file.cgi?param1=value HTTP/1.0
User-Agent: Shmozilla 3000
Referer: http://www.necrosoft.com
Accept-Language:en;ru

Первая строка - 1-я часть запроса - делится на 3 части:

- GET - определяет метод.

- /path/to/file.cgi?param1=value1 - путь к файлу, который нам нужен, и параметры. Для строки в броузере http://www.perl.ru/go.cgi?action=forum это будет выглядеть как /go.cgi?action=forum.

- HTTP/1.0 - определяет версию протокола (или HTTP/1.1)

Далее идут переменные - 2-я часть запроса.

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

Полный список можно взять из спецификации по HTTP - RFC2616 (www.rfc.org). Кстати, все протоколы описаны в документах RFC, расшифровки номеров которых можно найти в документе, называемом rfcindex, проще говоря, в полном списке документов (опять же на www.rfc.org).

Третья часть - область данных - отделяется от второй пустой строкой.

В методе GET эта часть - пустая. То есть, признаком конца запроса будет последовательность из двух переводов строки - "\n\n".

Запрос методом POST: Послать.

POST /path/to/file.cgi HTTP/1.0
User-Agent: Shmozilla 3000
Referer: http://www.necrosoft.com
Content-length:42
Content-type:application/www-form-urlencoded

param1=value1¶m2=value2¶m3-=value3

Метод POST отличается от GET следующими моментами:

1) Данные передаются не в первой строке с именем скрипта, а в третьей части, после всех переменных и пустой строки.

2) Переменная Content-length обязательна и должна содержать размер данных в байтах.

3) Поле Content-type содержит mime-тип посылаемых данных.

Из этих строк и состоит HTTP-запрос. Все они пишутся программой-клиентом в стандартный вывод (STDOUT). Ниже мы более подробно рассмотрим запросы, уже создавая клиентские программы.

Теперь остается разобраться: что же отвечает сервер на все эти запросы?

Ответ сервера:

200 OK Found
Content-Length:1024
...
Content-type:text/plain

1024 байт данных

Ответ сервера тоже состоит из трех частей:

- Первая строка - 1 часть.

- ХХХ - цифровой код ошибки.

- OK, Error, etc. - Код словесный =)

- Found, Not Found, etc. - расшифровка ответа.

- Вторая часть - опять же переменные, говорящие о многом =).

- Третья часть - после пустой строки - данные, размер которых обозначен в переменной Content-Length.

Некоторые коды:

- 2ХХ - различные ОКейные ответы.

- 4ХХ - Ошибки категории File not found, Authorization error, и прочие.

- 5ХХ - Ошибки сервера (проклятая 500 Error из этой категории).

Расшифровку всех кодов, а также переменных можно увидеть все в том же RFC2616.

Теперь можно перейти и к CGI.

Клиент -> Сервер -> Скрипт, и обратно

"GET /mp3filez/cool.mp3 тока быстро!", - опять запросил клиент.
"404 RTFM, я ведь сказал", - ответил сервер.
c2002 cmapuk[0nline]

Нет, сейчас мы еще не будем писать скрипты. Сначала разберемся, что такое CGI.

Итак, связь между клиентом и нашим скриптом происходит через посредничество самого сервера. Клиент с сервером общаются, как мы уже выяснили, по протоколу HTTP, а протокол CGI нужен для связи между сервером и скриптом. В предыдущей главе я намеренно назвал HTTP-заголовки(Content-type, etc.) переменными для того, чтобы проследить связи между этими заголовками и Perl-хешем переменных окружения %ENV. Разберемся.

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

1) Откуда брать данные: из первой строки после имени скрипта и "?" или из 3-й части запроса.

2) Куда эти данные положить скрипту - в $ENV{QUERY_STRING} или в STDIN.

Все заголовки, метод запроса и версию протокола сервер забивает в окружение, которое доступно скрипту из %ENV. Вот примерно таким образом мы и получаем в скрипте клиентские данные. Яснее разжевать не могу ;-).

Теперь о том, как скрипт отдает данные серверу для клиента. Сервер разрешает скрипту самому ставить заголовки, которые он потом отправит клиенту. Виды и форматы заголовков все те же, что в HTTP-спецификации. Пишется это все скриптом в стандартный вывод. Самое главное правило - в выводе должен быть заголовок "Content-type" или "Location" и ОН ДОЛЖЕН БЫТЬ ПОСЛЕДНИМ!!!. После последнего заголовка пишется пустая строка (то есть "\n\n"), а затем, если это не Location, данные, которые должен получить клиент. Вот и весь принцип работы. Для того чтобы заниматься CGI-программированием, эти простые истины НУЖНО ЗНАТЬ!!! А теперь будем практиковаться )).

CGI, или сетевая Камасутра

В русском издании "Perl CookBook" глава "Программирование CGI" начинается со страницы ©666. Что бы это значило?
Просто наблюдение.

CGI-программирование - основная область работы для Перл-программистов, особенно начинающих. По этой причине объем вопросов в форумах по этой теме составляет, вероятно, процентов 45 (Еще 45 на базы данных, а оставшиеся 10 - вопросы настройки Apache ;-)). Поэтому можно смело сказать, что CGI - это главный враг начинающего программиста. Шутка =).

По причинам личной неприязни, модуль CGI.pm я тут описывать не буду. Если понять принцип работы с протоколом CGI, то для использования этого модуля достаточно будет стандартной документации. Напротив, не зная, что из себя представляет протокол, программирование с CGI.pm будет неотрывно связано с ошибками, глупыми вопросами и т.п. Безусловно, модуль CGI.pm решает очень много программных задач, но если в скрипте все эти решения не нужны, зачем заставлять интерпретатор обрабатывать более 214 килобайт кода?!!! Итак, CGI без CGI.pm.

# Как мы уже выяснили, данные клиента в скрипте мы принимаем либо из
# $ENV{QUERY_STRING}, либо из STDIN. Выглядит это так.
$ENV{REQUEST_METHOD} eq "GET"?$data=$ENV{QUERY_STRING}:-read(STDI-N,$data,$ENV{CONTENT_LENGTH});
# Здесь мы использовали "хитрый" условный оператор "?:"
# Принцип: (Выражение-условие)?(ВыражениеЕслиTrue):-(ВыражениеЕслиFalse);
# Таким образом, если данные передаются как GET, переменная $data
# заполняется из QUERY_STRING, то есть из строки параметров запроса.
# Если же метод - POST, мы берем данные из стандартного ввода
# с помощью функции read (perldoc -f read). Эта функция требует
# в качестве третьего параметра - количество байт для чтения. Это
# мы узнаем из CONTENT_LENGTH.
# Теперь в $data лежит что-то типа param1=value1¶m2=value2
@pairs=split/\&/,$data;
# Разбили на пары param=value
# Теперь раскладываем наши пары на ключи и значения и кладем в хэш
foreach $pair (@pairs){
($key,$val)=split/=/,$pair;
$val=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
# Это для того, чтобы превратить белиберду типа %2C в нормальные
# знаки. В частности, русские буквы.
$val=~s/\+/ /g;
# Пробелы передаются как "+", мы их возвращаем в нормальный вид
         $F{$key}=~s/\r//g;
         # В многострочном текстовом поле формы переводы строк могут
         # выглядеть как \r\n, мы эти \r убиваем, так как оно нам не надо.
         $F{$key}=$val;
         # Создаем хэш ключ=>значение
}
# Теперь все параметры формы у нас в хэше.
# Если в форме присутствуют параметры с одинаковыми именами,
# например чекбоксы, то строку $F{$key}=$val можно заменить на
if(!$F{$key}){
         $F{$key}=$val;
}else{
         $F{$key}.="\n$val";
}
# таким образом одноименные параметры мы превратили в многострочную
# строку, которую потом можно разбить в массив.

С приемом данных вроде разобрались. Стоит еще отметить, что все HTTP-заголовки, передаваемые клиентом в запросе, содержатся в хеше %ENV, а имена их содержат префикс HTTP_. Тире(дефисы) в этих заголовках в хеше %ENV превращаются в "_". Примеры:

User-Agent       $ENV{HTTP_USER_AGENT}
Cookie           $ENV{HTTP_COOKIE}
Referer          $ENV{HTTP_REFERER}

Нельзя с уверенностью сказать, что данные в этих заголовках - чистая правда. Они передаются клиентом в запросе и могут быть запросто подделанными. Почему? Об этом мы поговорим ниже. А здесь надо заметить, что это не вся информация о киенте. Есть ведь еще REMOTE_ADDR, REMOTE_PORT, по которым можно идентифицировать клиента. Как я уже говорил, список всех заголовков вы найдете в RFC.

Вернемся к программированию.

# Теперь мы будем составлять ответ клиенту.
# После получения данных, мы их обработали и уже решили,
# какие данные нам вернуть.
...
print "Cool-header:blablabla\n";
print "Content-Charset:gluckowin-1251\n";
print "Content-type:text/html\n\n";
print "<html>Basile Pupkin was here!</html>";
# Сначала мы выводим нужные заголовки.
# Это могут быть описания данных(типа Content-Charset), куки и пр.
# ПОСЛЕДНИМ заголовком должен быть либо Content-type, либо Location
# После этого заголовка - \n\n, а дальше данные, если надо.
# Здесь следует отметить важный момент.

В Перл-скриптах, по умолчанию, работает буферизация вывода. Это означает, что все printы сначала печатаются в буфер, и только в конце скрипта весь вывод выдается. За буферизацию отвечает встроенная переменная $|. Если $| определена (например $|=1), то буфер отключен. В этом случае вышеприведенный кусок кода выдаст ошибку 500, а в лог запишется сообщение о неправильном заголовке. Включение буферизации происходит так - undef $|;. Часто модули, для своей работы отключают буферизацию (например, при использовании баз данных), и это надо учитывать. В этом случае, весь наш вывод надо сохранить в переменной, которую и вывести в конце ОДНИМ printом.

...
$OUT="Cool-header:blablabla\n";
$OUT.="Content-Charset:gluckowin-1251\n";
$OUT.="Content-type:text/html\n\n";
$OUT.="<html>Basile Pupkin was here!</html>";
...
print $OUT;

# или так:
...
$OUT=



www.comprice.ru
Скорая компьютерная помощь 02-12-2006
Вопрос-ответ 17-03-2008 Скорая компьютерная помощь
Уважаемые читатели! Мы продолжаем публикацию вопросов-ответов в рамках рубрики "F1-Help". Присылайте нам любые вопросы на компьютерную и телекоммуникационную тематику по адресу forum@comprice.ru, а мы постараемся вам ответить. Также по этому адресу вы можете оставлять свои пожелания в адрес нашего журнала, и то, о чем бы вы хотели прочитать на его страницах.Вопрос: Планирую приобрести пишущий DVD-привод, но достоверной информации найти практиче...


Вопрос - ответ 12-11-2007 Скорая компьютерная помощь
Вопрос: У меня возникла такая вот ошибка. При открытии приложения Nero Info Tool выдается "синий экран" со следующим содержимым:A problem has been detected and windows has been shut down to prevent damage to your computer If this the first time you"ve seen this Stop error screen, restart your computer.If this screen appears again, follow these steps: Check to be sure you adequate disk space. If a driver is identified in the Stop message, disa...


Вопрос - ответ 29-08-2007 Скорая компьютерная помощь
Уважаемые читатели! Мы продолжаем публикацию вопросов-ответов в рамках рубрики "F1-Help". Присылайте нам любые вопросы на компьютерную и телекоммуникационную тематику по адресу forum@comprice.ru, а мы постараемся вам ответить. Также по этому адресу вы можете оставлять свои пожелания в адрес нашего журнала, и то, о чем бы вы хотели прочитать на его страницах.Вопрос: Начал устанавливать WindowsXP на новый винчестер, в установщике разбил жесткий д...

Copyright © 2005
"service-centers.com.ua"


Rambler's Top100   Рейтинг@Mail.ru

Работа
Карта сайта
О сайте
Реклама
все объявления
поиск резюме
поиск вакансий
добавить резюме
добавить вакансию

Все объявления
Компьютеры, оргтехника
Автомобили
Продукты питания
Аудио, Фото, Видео
Связь
Отдых, путешествия
Мебель
Разное
Добавить объявление
 Автомобили
 Ремонтно-строительные услуги
 Пресс-релизы
 Железо
 Soft
 Мобильная планета
 Для милых дам
 Родительский клуб
 Недвижимость
 Домашний очаг, усадьба
 Строительство
 Обустройство и безопасность дома
 Сантехника