Тарифы Услуги Сим-карты

Общий шлюзовый интерфейс (CGI). Конфигурирование сервера для CGI. Установка и выполнение программы CGI

Страница 1 из 30

Сегодня такие вещи, как гостевая книга, поиск по серверу, форма для отправки сообщений - неотъемлемый атрибут практически любого серьезного сайта. Проблема внедрения этих и других прибамбасов, разумеется, всячески возбуждает воображение начинающего вебмастера, лишая его сна, аппетита и тяги к пиву. К сожалению, изучение HTML-исходников страниц конкурентов ничего, кроме ссылок на некий "cgi-bin", не дает, да еще в телеконференциях иногда встречается упоминание о каких-то cgi-скриптах. Эта статья и посвящена основам использования этих самых cgi-скриптов во славу и процветание вашего сайта.

Для начала, думаю, надо разобраться с понятиями. CGI-скрипт - это программа, которая выполняется на Web-сервере по запросу клиента (то есть посетителя Web-сайта). Программа эта принципиально ничем не отличается от обычных программ, которые установлены на вашем компьютере - будь то MS Word или игра Quake. CGI - это не язык программирования, на котором написан скрипт, а Common Gateway Interface - специальный интерфейс, с помощью которого и происходит запуск скрипта и взаимодействие с ним.

Краткое лирическое отступление насчет CGI

Итак что такое CGI - скрипты и вообще подобные вещи. Начнем с того что ваш браузер (когда вы набрали URL ) соединяется по протоколу HTTP с указаным сервером и просит у него нужный файл,примерно так:

GET /~paaa/cgi-bin/guestbbok.cgi HTTP/1.0 -Вот это самое главное в запросе

Ну и если запрошен простой файл например .html то если если такой файл есть, То сервер отошлет браузеру ответ:

HTTP/1.0 200 Okay
Content-Type: text/html

Далее после пустой строки(она нужна чтоб отделить заголовок от тела ) идет информация из самого URL "а ...
Вот в принципе и весь WWW ....ходишь от ссылки к ссылке....
А что если Нужно внести в этот унылый процесс что-нибудь по настоящему интерактивное, динамическое,прекрасное и великолепное....? Чтож есть ответ и на этот вопрос. Просто что если в запрашиваемом URL указать спецыальную программу (CGI ,программа Common Gateway Inteface - Общего Шлюзового Интерфейса ) и то что эта прога выдаст то и отправить браузеру....Сервер запускает .cgi программу и она например обработав данные формы заносит вас куда-нибудь в свою базу данных,а вам сообщит что вы большой молодец:)
Ну надеюсь я вас заинтриговал......?

Краткие сведения о том что надо знать чтоб писать CGI скрипты: Ну вопервых надо знать что такое интернет и как он работает (а вы знаете? ;))) ) Ну и чуть-чуть умения прграмировать(это самое главное)
Давайте вместе писанем какой нибудь простенький скриптик а потом я вам расскажу где сдесь собака порылась....
Ну сначала в своем домашнем каталоге создайте директорию cgi-bin :

cd public_html
mkdir cgi-bin
chmod 0777 cgi-bin

Последняя строчка будет очень важна.
Возьмите редактор и наберите: #!/usr/bin/perl
#first.cgi
print "Content-Type: text/html\n\n";
print "";
print "

Hello you!!!

";
print "";

Сохраните его в директории cgi-bin под именем first.cgi .Ну как сохранили?
А теперь сделайте его исполняемым(ведь это программа):

chmod +x first.cgi

Ну вот,подходим к торжественному моменту.... наберите в строке браузера http://www.uic.nnov.ru/~твой_логин/cgi-bin/first.cgi
и посмотрите чо будет. Будет одно из двух,либо скрипт заработает и вы увидите сгенерированую им страничку (поздравляю,в нашем полку прибыло!) либо Internal Server Error -тогда не расстраивайтесь,вы что-то сделали не так. Вам тогда пригодится пособие по ловле блох. Ну вопервых проверку синтаксиса можно осуществить следующим образом:

perl -с first.cgi

Perl вам сразу выдаст либо сообщения об ошибках(ну бывает,точку с запятой пропустили, скобочки или кавычки забыли закрыть...) это сразу по ходу дела поправимо.
Более грубая с логической точки зрения это пропустить вывод пустой строки, которая отделяет заголовок от тела:
print "Content-Type: text/html\n\n"; #Все Правильно
print "Content-Type: text/html\n"; #ОШИБКА!!!

Разберем скрипт:
Первая строка #!/usr/bin/perl Просто указывает где в системе расположен Perl. Вторая это просто коментарий -вы можете тыкать чо угодно после знака #
Затем идет print "Content-Type: text/html\n\n"; Это заголовок указывающий тип содержимого все что скрипт печатает в свой стандартный вывод STDOUT идет на обработку к серверу. Пустая строка отделяет заголовок от тела,которое в нашем случае представляет собой

Hello you!!!



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

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

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

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

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

При описании различных программ, которые вызываются сервером HTTP и реализованы в стандарте CGI, используют следующую терминологию:

CGI-скрипт - программа, написанная в соответствии со спецификацией Common Gateway Interface. CGI-скрипты могут быть написаны на любом языке программирования (C, C++ (язык программирования) , PASCAL, FORTRAN и т.п.) или командном языке (shell (Операционные Системы) , cshell, командный язык MS-DOS, Perl и т.п.). Скрипт может быть написан даже на языке редактора EMAC в системах Unix.

Шлюз - это CGI-скрипт, который используется для обмена данными с другими информационными ресурсами Internet или приложениями-демонами. Обычная CGI-программа запускается сервером HTTP для выполнения некоторой работы, возвращает результаты серверу и завершает свое выполнение. Шлюз выполняется точно также, только, фактически, он инициирует взаимодействие в качестве клиента с третьей программой. Если эта третья программа является сервисом Internet, например, сервер Gopher, то шлюз становится клиентом Gopher, который посылает запрос по порту Gopher, а после получения ответа пересылает его серверу HTTP.

Общий шлюзовый интерфейс CGI

CGI (Common Gateway Interface) - механизм доступа к программам на стороне веб-сервера. Спецификация CGI была разработана для расширения возможностей сервиса www за счет подключения различного внешнего программного обеспечения. При использовании CGI веб-сервер представляет браузеру доступ к исполнимым программам, запускаемым на его (серверной) стороне через стандартные потоки ввода и вывода.

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

Веб-серверы

Веб-сервер - это сетевое приложение, обслуживающее HTTP-запросы от клиентов, обычно веб-браузеров. Веб-сервер принимает запросы и возвращает ответы, обычно вместе с HTML-страницей, изображением, файлом, медиа-потоком или другими данными. Веб-серверы - основа Всемирной паутины. С расширением спектра сетевых сервисов веб-серверы все чаще используются в качестве шлюзов для серверов приложений или сами представляют такие функции (например, Apache Tomcat).

Созданием программного обеспечения веб-серверов занимаются многие разработчики, но наибольшую популярность имеют такие программные продукты, как Apache (Apache Software Foundation), IIS (Microsoft), Google Web Server (GWS, Google Inc.) и nginx.

  • Apache - свободное программное обеспечение, распространяется под совместимой с GPL лицензией. Apache уже многие годы является лидером по распространенности во Всемирной паутине в силу своей надежности, гибкости, масштабируемости и безопасности.
  • IIS (Internet Information Services) - проприетарный набор серверов для нескольких служб Интернета, разработанный Майкрософт и распространяемый с серверными операционными системами семейства Windows. Основным компонентом IIS является веб-сервер, также поддерживаются протоколы FTP, POP3, SMTP, NNTP.
  • nginx - это HTTP-сервер, совмещенный с кэширующим прокси-сервером. Разработан И. Сысоевым для компании Рамблер. Осенью 2004 года вышел первый публично доступный релиз, сейчас nginx используется на 9-12% веб-серверов. Браузеры
  • Браузер, веб-обозреватель (web-browser) - клиентское приложение для доступа к веб-серверам по протоколу HTTP и просмотра веб-страниц. Как правило браузеры дополнительно поддерживают и ряд других протоколов (например ftp, file, mms, pop3).

Первые HTTP-клиенты были консольными и работали в текстовом режиме, позволяя читать гипертекст и перемещаться по ссылкам. Сейчас консольные браузеры (такие, как lynx, w3m или links) практически не используются рядовыми посетителями веб-сайтов. Тем не менее такие браузеры весьма полезны для веб-разработчиков, так как позволяют «увидеть» веб-страницу «глазами» поискового робота.

Исторически первым браузером в современном понимании (т.е. с графическим интерфейсом и т.д.) была программа NCSA Mosaic, разработанная Марком Андерисеном и Эриком Бина. Mosaic имел довольно ограниченные возможности, но его открытый исходный код стал основой для многих последующих разработок.

Принцип работы CGI

Обобщенный алгоритм работы через CGI можно представить в следующем виде:

  1. Элемент нумерованного списка
  2. Клиент запрашивает CGI-приложение по его URI.
  3. Веб-сервер принимает запрос и устанавливает переменные окружения, через них приложению передаются данные и служебная информация.
  4. Веб-сервер перенаправляет запросы через стандартный поток ввода (stdin) на вход вызываемой программы.
  5. CGI-приложение выполняет все необходимые операции и формирует результаты в виде HTML.
  6. Сформированный гипертекст возвращается веб-серверу через стандартный поток вывода (stdout). Сообщения об ошибках передаются через stderr.
  7. Веб-сервер передает результаты запроса клиенту.

Механизмы обмена данными

  • через переменные окружения;
  • через командную строку;
  • через стандартный ввод;
  • через стандартный вывод.

Переменные окружения

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

Общие переменные окружения

  • SERVER_SOFTWARE - определяет имя и версию сервера.
  • SERVER_NAME - определяет доменное имя сервера.
  • GATEWAY_INTERFACE - определяет версию интерфейса.

Запрос-ориентированные окружения

  • SERVER_PROTOCOL - протокол сервера. Вообще говоря, CGI разрабатывалась не только для применения в www с протоколом HTTP, но и для других протоколов также, но широкое применение получила только в www.
  • SERVER_PORT - определяет порт TCP (Transmission Control Protocol) - протокол управления передачей), по которому осуществляется взаимодействие. По умолчанию для работы по HTTP используется 80 порт, но он может быть и переназначен при конфигурировании сервера.
  • REQUEST_METHOD - определяет метод доступа к информационному ресурсу. Это важнейшая переменная в CGI. Разные методы доступа используют различные механизмы передачи данных. Данная переменная может принимать значения GET, POST, HEAD и т. п.
  • PATH_INFO - передает программе путь, часть спецификации URL, в том виде, в котором она указана в клиенте. Реально это означает, что передается путь (адрес скрипта) в виде, указанном в HTML-документе.
  • PATH_TRANSLATED - то же самое, что и PATH_INFO, но только после подстановки сервером определенных в его конфигурации вставок.
  • SCRIPT_NAME - определяет адрес скрипта так, как он указан клиентом.
  • QUERY_STRING - переменная определяет содержание запроса к скрипту.

Идентификация пользователя и его машины

  • REMOTE_HOST - доменный адрес машины, с которой осуществляется запрос.
  • REMOTE_ADDR - IP-адрес запрашивающей машины.
  • AUTH_TYPE - тип идентификации пользователя. Используется в случае если скрипт защищен от несанкционированного использования.
  • REMOTE_USER - используется для идентификации пользователя.
  • REMOTE_IDENT - данная переменная порождается сервером, если он поддерживает идентификацию пользователя по протоколу RFC-931. Рекомендовано использование этой переменной для первоначального использования скрипта.

Переменные, определяющие тип и длину передаваемой информации от клиента к серверу

  • CONTENT_TYPE - определяет MIME-тип данных, передаваемых скрипту. Используя эту переменную можно одним скриптом обрабатывать различные форматы данных.
  • CONTENT_LENGTH - определяет размер данных в байтах, которые передаются скрипту. Данная переменная чрезвычайно важна при обмене данными по методу POST, т. к. нет другого способа определить размер данных, которые надо прочитать со стандартного ввода.

Возможна передача и других переменных окружения. В этом случае перед именем указывается префикс "HTTP_". Отдельный случай представляют переменные, порожденные в заголовке HTML-документа в тагах META. Они передаются в заголовке сообщения и некоторые серверы могут порождать переменные окружения из этих полей заголовка.

Опции командной строки

Командная строка используется только при запросах типа ISIN-DEX . При HTML FORMS или любых других запросах неопределенного типа командная строка не используется. Если сервер определил, что к скрипту обращаются через ISINDEX -документ, то поисковый критерий выделяется из URL и преобразуется в параметры командной строки. При этом знаком разделения параметров является символ "+". Тип запроса определяется по наличию или отсутствию символа "=" в запросе. Если этот символ есть, то запрос не является запросом ISINDEX , если символа нет, то запрос принадлежит к типу ISIN-DEX . Параметры, выделенные из запроса, помещаются в массив параметров командной строки argv. При этом после из выделения происходит преобразование всех шестнадцатеричных символов в их ASCII-коды. Если число параметров превышает ограничения, установленные в командном языке, например в shell, то формирования командной строки не происходит и данные передаются только через QUERY_STRING . Вообще говоря, следует заранее подумать об объеме данных, передаваемом скрипту и выбрать соответствующий метод доступа. Размер переменных окружения тоже ограничен, и если необходимо передавать много данных, то лучше сразу выбрать метод POST, т.е. передачу данных через стандартный ввод.

Формат стандартного ввода

Стандартный ввод используется при передаче данных в скрипт по методу POST. Объем передаваемых данных задается переменной окружения CONTENT_LENGTH , а тип данных - переменной CONTENT_TYPE . Если из HTML-формы надо передать запрос типа: a=b&b=c, то CONTENT_LENGTH =7, CONTENT_TYPE =application/x-www-form-urlencoded, а первым символом в стандартном вводе будет символ "а". Следует всегда помнить, что конец файла сервером в скрипт не передается, а поэтому завершать чтение следует по числу прочитанных символов. Позже мы разберем примеры скриптов и обсудим особенности их реализации в разных операционных системах.

Формат стандартного вывода

Стандартный вывод используется скриптом для возврата данных серверу. При этом вывод состоит из заголовка и собственно данных. Результат работы скрипта может передаваться клиенту без каких-либо преобразований со стороны сервера, если скрипт обеспечивает построение полного HTTP-заголовка, в противном случае сервер заголовок модифицирует в соответствии со спецификацией HTTP. Заголовок сообщения должен отделяться от тела сообщения пустой строкой. Обычно в скриптах указывают только три поля HTTP-заголовка: Content-type , Location , Status .

Content-type

Указывается в том случае, когда скрипт сам генерирует документ "на лету" и возвращает его клиенту. В этом случае реального документа в файловой системе сервера не остается. При использовании такого сорта скриптов следует учитывать, что не все серверы и клиенты отрабатывают так, как представляется разработчику скрипта. Так, при указании Content-type: text/html, некоторые клиенты не реализуют сканирования полученного текста на предмет наличия в нем встроенной графики. Обычно в Content-type указывают текстовые типы text/plain и text/html.

Location

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

Области применения CGI

Наиболее частая задача, для решения которой применяется CGI - создание интерактивных страниц, содержание которых зависит от действий пользователя. Типичными примерами таких веб-страниц являются форма регистрации на сайте или форма для отправки комментария. Другая область применения CGI, остающаяся за кулисами взаимодействия с пользователем, связана со сбором и обработкой информации о клиенте: установка и чтение «cookies»; получение данных о браузере и операционной системе; подсчет количества посещений веб-страницы; мониторинг веб-трафика и т. п.

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

Важно знать, что CGI - это не язык программирования! Это простой протокол, позволяющий веб-серверу передавать данные через stdin и читать их из stdout . Поэтому, в качестве CGI-обработчика может использоваться любая серверная программа, способная работать со стандартными потоками ввода-вывода.

Преимущества CGI

Многие возможности CGI сейчас дублируются такими технологиями, как например DHTML, ActiveX или Java-апплетами. Основными преимуществами использования серверных скриптов является то, что вы можете быть уверены, что все клиенты (за редким исключением, как правило связанным с блокировкой доступа к определенным ресурсам на уровне файрвола) смогут работать с серверным приложением. Клиентские-же программы могут быть просто отключены в браузере, или вовсе не поддерживаться.

Недостатки CGI

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

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

Common Gateway Interface (CGI, рус. Общий шлюзовый интерфейс) - это стандартный метод динамического управления содержимым веб-страниц с помощью специальных программ, выполняющихся на стороне веб-сервера. Эти программы называются CGI-обработчики или шлюзы, но чаще - CGI-скрипты, т.к. обычно они пишутся на скриптовых языках, в основном на Perl.

Поскольку гипертекст статичен по своей природе, веб-страница не может непосредственно взаимодействовать с пользователем. До появления JavaScript , не было иной возможности отреагировать на действия пользователя, кроме как передать введенные им данные на веб-сервер для дальнейшей обработки. В случае CGI эта обработка осуществляется с помощью внешних программ и скриптов, обращение к которым выполняется через стандартизованный (см. RFC 3875: CGI Version 1.1) интерфейс - общий шлюз. Упрощенная модель, иллюстрирующая работу CGI, приведена на рис. 1.

Как работает CGI?

Обобщенный алгоритм работы через CGI можно представить в следующем виде:

  1. Клиент запрашивает CGI-приложение по его URI .
  2. Веб-сервер принимает запрос и устанавливает переменные окружения , через них приложению передаются данные и служебная информация.
  3. Веб-сервер перенаправляет запросы через стандартный поток ввода (stdin) на вход вызываемой программы.
  4. CGI-приложение выполняет все необходимые операции и формирует результаты в виде HTML.
  5. Сформированный гипертекст возвращается веб-серверу через стандартный поток вывода (stdout). Сообщения об ошибках передаются через stderr.
  6. Веб-сервер передает результаты запроса клиенту.

Области применения CGI

Наиболее частая задача, для решения которой применяется CGI - создание интерактивных страниц, содержание которых зависит от действий пользователя. Типичными примерами таких веб-страниц являются форма регистрации на сайте или форма для отправки комментария. Другая область применения CGI, остающаяся за кулисами взаимодействия с пользователем, связана со сбором и обработкой информации о клиенте: установка и чтение «печенюшек»-cookies ; получение данных о браузере и операционной системе; подсчет количества посещений веб-страницы; мониторинг веб-трафика и т.п.

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

ОБРАТИТЕ ВНИМАНИЕ: CGI - это не язык программирования! Это простой протокол, позволяющий веб-серверу передавать данные через stdin и читать их из stdout. Поэтому, в качестве CGI-обработчика может ипользоваться любая серверная программа, способная работать со стандарными потоками ввода-вывода.

Hello, world!

Пример простого CGI-скрипта на языке Perl приведен в листинге 1. Если этот код сохранить в файле с именем hello (имя может быть любым, расширение - тоже), поместить файл в серверный каталог cgi-bin (точнее, в тот каталог веб-сервера, который предназначен для CGI-приложений и указан в настройках веб-сервера) и установить для этого файла права на исполнение (chmod uo+x hello), то он станет доступен по адресу вида http://servername/cgi-bin/hello.

Листинг 1. Пример CGI-скрипта (Perl)

#!/usr/bin/perl print "Content-type: text/html\n\n"; print < CGI say Hello

Hello, world!

HTML exit;

В этом коде строка #!/usr/bin/perl указывает полный путь к интерпретатору Perl. Строка Content-type: text/html\n\n - http-заголовок, задающий тип содержимого (mime-type). Удвоенный символ разрыва строки (\n\n) - обязателен, он отделяет заголовки от тела сообщения.

Переменные окружения

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

Переменная окружения Описание
CONTENT_TYPE Тип данных, передаваемых на сервер. Используется, когда клиент отправляет данные, например, загружает файл.
CONTENT_LENGTH Размер содержимого запроса. Эта переменная определена для POST-запросов.
HTTP_COOKIE Возвращает набор «куков» в виде пар «ключ значение».
HTTP_USER_AGENT Информация об агенте пользователя (браузере)
PATH_INFO Путь к каталогу CGI
QUERY_STRING Строка запроса (URL-encoded), передаваемая методом GET.
REMOTE_ADDR IP-адрес клиента, выполняющего запрос.
REMOTE_HOST Полное имя (FQDN) клиента. (Если доступно)
REQUEST_METHOD Метод, которым выполняется запрос. Чаще всего GET или POST.
SCRIPT_FILENAME Полный путь к запрашиваемому скрипту (в файловой системе сервера).
SCRIPT_NAME Имя скрипта
SERVER_NAME Имя сервера
SERVER_ADDR IP-адрес сервера
SERVER_SOFTWARE Информация о серверном ПО

В листинге 2 приведен код небольшой программы на Perl, выводящей список переменных окружения. Результат ее работы приведен на рис. 2.

Листинг 2. Переменные окружения

#!/usr/bin/perl print "Content-type: text/html\n\n"; print "\n\n

Environment

\n"; foreach (sort keys %ENV) { print "$_: $ENV{$_}
\n"; } print "\n"; exit;

Передача данных: метод GET

Метод GET используется для передачи urlencoded -данных через строку запроса. Адрес запрашиваемого ресурса (CGI-скрипта) и передаваемые ему данные отделяются знаком «?». Пример такого адреса:

http://example.com/cgi-bin/script.cgi?key1=value1&key2=value2

Метод GET используется по умолчанию для данных, введенных в адресную строку браузера. Такая же строка может быть сформирована при отправке данных из веб-формы (тег

), если метод передачи для формы не указан. Вся информация, отправляемая методом GET, передается в открытом виде, поэтому никогда не следует использовать его для отправки на сервер паролей или другой подобной информации. Метод GET имеет ограничение по размеру: строка запроса должна быть не длинее 1024 символов.

Информация, отправляемая методом GET передается в заголовке QUERY_STRING в виде строки, состоящей из пар вида ключ=значение , CGI-скрипт может получить ее через одноименную переменную окружения.

Листинг 3. Отправка данных из веб-формы методом GET

A simple CGI scripting: Sending data using GET-method You name: name="user" >
Where are you from?: name="from" >

После ввода данных в форму из листинга 3 и нажатия кнопки "Submit" будет сформирована строка запроса вида:

http://example.com/cgi-bin/sayhello ?user =sometext &from =anothertext

где: sayhello - имя CGI-скрипта; user и from - имена параметров; sometext и anothertext - введенные пользователем значения соответствующих параметров.

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

Листинг 4. Отправка данных из веб-формы методом GET

#!/usr/bin/perl local ($buffer, @pairs, $pair, $name, $value, %FORM); # Анализируем окружение $ENV{"REQUEST_METHOD"} =~ tr/a-z/A-Z/; if ($ENV{"REQUEST_METHOD "} eq "GET ") { $buffer = $ENV{"QUERY_STRING "}; } # Разделяем строку запроса на пары вида ключ/значение @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%(..)/pack("C", hex($1))/eg; $FORM{$name} = $value; # Сохраняем данные в массив } # Отправляем заголовок print "Content-type: text/html\n\n"; # Отправляем гипертекст print < CGI say Hello

Hello, $FORM{user} from $FORM{from}!

HTML exit;

Передача данных: метод POST

В общем случае более подходящим для передачи информации CGI-скрипту является метод POST. Блок передаваемых данных формируется так же, как и для метода GET, но непосредственно передача осуществляется в теле запроса. Данные поступают на вход CGI-приложения через стандартный ввод (stdin).

Для отправки данных этим методом, он должен быть явно задан в описании формы (action="POST").

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

... # Анализируем окружение $ENV{"REQUEST_METHOD"} =~ tr/a-z/A-Z/; if ($ENV{"REQUEST_METHOD "} eq "POST "){ read(STDIN , $buffer, $ENV{"CONTENT_LENGTH "}); } ...

Дальнейшая обработка сохраненных в переменной $buffer параметров и их значений выполняется так же, как и в при использовании метода GET.

Преимущества CGI

Многие возможности CGI сейчас дублируются такими технологиями, как например DHTML , ActiveX или Java-апплетами. Основными преимуществами использования серверных скриптов является то, что вы можете быть уверены, что все клиенты (за редким исключением, как правило связанным с блокировкой доступа к определенным ресурсам на уровне файрвола) смогут работать с серверным приложением. Клиентские-же программы могут быть просто отключены в браузере, или вовсе не поддерживаться.

Недостатки CGI

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

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

Постоянный адрес этой страницы:

Введение.

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

Теория CGI .

CGI – (Common Gateway Interface) – Общий Шлюзовый Интерфейс. Как не трудно догадаться интерфейс этот служит шлюзом между сервером (здесь я подразумеваю программу - сервер) и какой-либо внешней программой написанной для ОС на которой этот самый сервер запущен. Таким образом CGI отвечает за то, каким именно образом данные будут переданы от программы-сервера к CGI-программе и обратно. Интерфейс не накладывает никаких ограничений на то, на чем должна быть написана CGI-программа, это может быть как обычный исполнимый файл, так и любой другой файл – главное, чтобы сервер смог его запустить (в среде windows это например может быть файл с расширением, привязанным к какой-либо программе).

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

Вэб-клиент (например браузер) создает подключение к серверу, указанному в URL;

Вэб-клиент посылает запрос серверу, запрос этот обычно делается с помощью двух методов GET или POST;

Данные из запроса клиента (например значения полей формы) передаются сервером, используя CGI-интерфейс, CGI-программе, указанной в URL;

CGI-программа обрабатывает данные клиента, полученные от сервера и генерирует на основе этой обработки ответ клиенту, который она передает по все тому же CGI-интерфейсу серверу, а он в свою очередь передает его уже непосредственно клиенту;

Сервер разрывает соединение с клиентом.

В стандартной спецификации CGI принято, что сервер может обмениваться с программой следующими способами:

Переменные окружения – они могут быть установлены сервером при запуске программы;

Стандартный поток ввода (STDIN) – с его помощью сервер может передать данные программе;

Стандартный поток вывода (STDOUT) – программа может писать в него свой вывод, передающийся серверу;

Командная строка – в ней сервер может передать некоторые параметры программе.

Стандартные потоки ввода/вывода весьма удобны и широко используются на UNIX-системах, чего не скажешь о windows, поэтому существует спецификация CGI, разработанная специально для windows-систем так и называемая «Windows CGI». Но, естественно, и стандартные потоки ввода/вывода так же можно использовать в windows CGI программировании. Здесь я не буду затрагивать стандарт «Windows CGI», и на это существует по крайней мере две причины – первая, и самая главная – на данный момент не все http-сервера под windows поддерживают эту спецификацию (в частности мой любимый Apache 1.3.19). Вторую причину вы можете наблюдать набрав в любой поисковой системе строчку «Windows CGI». Отмечу относительно этого интерфейса лишь общие детали – все данные от сервера к клиенту передаются посредством обычного для windows *.ini файла, имя которого передается программе в командной строке. При этом все данные в файле уже заботливо разбиты по секциям сервером и вам лишь остается используя функции «GetPrivateProfile*» извлечь их оттуда. Ответ серверу передается опять же посредством файла, имя которого указано в соответствующей записи ini-файла.

Какие же данные могут быть переданы клиентом CGI-программе? – практически любые. В общем случае программе передаются значения полей формы, которые заполняет клиент, но это также могут быть и какие-либо двоичные данные, например файл с картинкой или музыкой. Данные могут быть переданы на сервер двумя различными методами – это метод GET и метод POST. Когда мы создаем форму для заполнения на нашей страничке мы явно указываем каким из приведенных методов мы хотим отправить введенные пользователем данные, делается это в основном тэге формы примерно так:

get action="/cgi-bin/name_script">

При отправке данных методом GET данные браузером считываются из формы и помещаются следом за URL скрипта, за знаком вопроса, если значимых полей в форме несколько, то они передаются все через значёк «&», имя поля и его значение пишутся в URL через знак «=». Например запрос, сгенерированный браузером из формы при нажатии на кнопку, к которой привязан скрипт «/cgi-bin/test.exe», при учете что первое поле формы называется «your_name», второе – «your_age», может выглядеть так:

GET /cgi-bin/test.exe?your_name=Pupkin&your_age=90 HTTP/1.0

Использование метода GET имеет сразу несколько слабых сторон – первое и самое главное – т.к. данные передаются в URL то он имеет ограничение на количество этих самых передаваемых данных. Вторая слабость опять же вытекает из URL – это конфиденциальность, при такой передаче данные остаются абсолютно открытыми. Итак, хорошо если у нас в форме 2-3 небольших поля… встает вопрос что же делать если данных больше? Ответ – использовать метод POST!

При использовании метода POST данные передаются серверу как блок данных, а не в URL, что несколько развязывает нам руки для увеличения объема передаваемой информации, для вышеприведенного примера формы POST блок, посылаемый серверу будет примерно такой:

POST /cgi-bin/test.exe HTTP/1.0

Accept: text/plain

Accept: text/html

Accept: */*

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

Content-length: 36

your_name=Pupkin&your_age=90

Как уже говорилось выше, после получения данных сервер должен преобразовать их и передать CGI программе. В стандартной спецификации CGI введенные клиентом данные при запросе GET помещаются сервером в переменную среды программы «QUERY_STRING». При запросе POST данные помещаются в стандартный поток ввода приложения, откуда могут быть им считаны. Кроме того, при таком запросе сервером устанавливаются еще две переменные среды - CONTENT_LENGTH и CONTENT_TYPE, по которым можно судить о длине запроса в байтах и о его содержании.

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

REQUEST_METHOD

Описывает каким именно методом получены данные

Пример :REQUEST_METHOD=GET

QUERY_STRING

Строка запроса, если использовался метод GET

Пример :QUERY_STRING= your_name=Pupkin&your_age=90&hobby=asm

CONTENT_LENGTH

Длина в байтах тела запроса

Пример:CONTENT_LENGTH=31

CONTENT_TYPE

Тип тела запроса

GATEWAY_INTERFACE

Версия протокола CGI

Пример: GATEWAY _ INTERFACE = CGI /1.1

REMOTE_ADDR

IP-Адрес удаленного хоста, то бишь клиента, нажавшего кнопочку в форме

Пример:REMOTE_ADDR=10.21.23.10

REMOTE_HOST

Имя удаленного хоста, это может быть его доменное имя или например имя компьютера в среде Windows, если таковые получены быть не могут, то поле содержит его IP

Пример :REMOTE_HOST=wasm.ru

SCRIPT_NAME

Имя скрипта, использованное в запросе.

Пример :SCRIPT_NAME=/cgi-bin/gols.pl

SCRIPT_FILENAME

Имя файла скрипта на сервере.

Пример :SCRIPT_FILENAME=c:/page/cgi-bin/gols.pl

SERVER _ SOFTWARE

Программное обеспечение сервера

Пример:Apache/1.3.19 (WIN 32)

В общем-то это вкратце все, для получения более подробной информации об Общем Шлюзовом Интерфейсе смотрите специализированную документацию, это описание я сделал для того, чтобы напомнить вам, а если не знали то ввести в курс дела. Давайте попробуем что-нибудь сделать на практике.

Практическая часть.

Для практики нам понадобятся как минимум 3 вещи – какой-нибудь http-сервер для Windows, все примеры я пробовал на Apache 1.3.19 для Windows, сервер бесплатный, скачать его можно с http://httpd.apache.org/download.cgi . Да, и сервер нам понадобится не абы – какой, а настроенный для запуска cgi-скриптов! Как это делается для сервера используемого вами смотрите документацию. Вторая вещь, которая нам понадобится это, естественно, ассемблер, так же необходимо, чтобы компилятор поддерживал создание консольных WIN32 приложений, я использую Tasm, но прекрасно подойдут и Fasm и Masm и множество других *asm’ов. Ну и наконец самое главное, что потребуется это желание.

Итак, я допускаю, что сервер был вами благополучно поставлен и настроен, так, что в корневой директории документов сервера лежит файлик index.html, который замечательно показывается в браузере, когда вы набираете адрес http://127.0.0.1 . Так же я учту, что где-то в дебрях папок сервера существует папочка «cgi-bin», в которой разрешен запуск скриптов.

Давайте проверим настройку сервера, а заодно и напишем небольшой скрипт. Скрипт наш будет обычным *.bat файлом. Предвижу вопросы – как? неужели? Да, это обычный командный файл, как уже говорилось выше спецификация CGI не делает различий между типами файлов, главное, чтобы сервер мог его запустить, а он в свою очередь, имел доступ к stdin/stdout и переменным окружения, bat-файл, пусть и не в полной мере, но для примера нас вполне устроит. Создадим файл примерно такого содержания:

@echo off rem Заголовок апроса echo Content-type: text/html echo. rem Тело запроса echo "Привет!
echo "С запросом GET пришли данные: %QUERY_STRING%

Файл назовем test.bat и поместим его в директорию для запуска скриптов, скорее всего это будет директория «cgi-bin». Следующее, что нам нужно будет сделать, это каким либо образом вызвать этот скрипт, в принципе, сделать это можно напрямую набрав в окошке адреса браузера примерно следующее «http://127.0.0.1/cgi-bin/test.bat», но давайте сделаем его вызов с нашей главной странички, заодно проверим работу метода GET. Создадим в корне сервера файл index.html со следующим содержанием:

Введите данные для передачи серверу:
Данные:

Теперь при входе на сервер (http://127.0.0.1 в строке адреса браузера) должна появиться форма, наберите в ней что-нибудь и нажмите кнопку «послать», если все было сделано правильно, Вы увидите в окне браузера ответ нашего bat-скрипта. Теперь давайте посмотрим что же мы там намутили.

Как можно догадаться команда «echo» осуществляет вывод в stdout, первым делом мы передаем серверу заголовок нашего ответа – «echo Content-type: text/html». Это есть стандартный заголовок спецификации CGI, говорящий о том, что передавать мы хотим текст или документ html, существуют и другие заголовки. Очень важный момент – заголовок должен отделяться от тела ответа пустой строкой, что мы и делаем следующей командой «echo.». Дальше передается тело самого ответа – это обычный html-документ, в теле документа я для наглядности отображаю одну из переменных среды, переданной нам сервером – «QUERY_STRING», как уже говорилось при методе GET (а это именно наш случай) в этой переменной передаются все введенные пользователем данные, что мы и можем наблюдать в ответе скрипта. Вы могли заметить «кавычки не к месту» в последних 2-х строках файла, сразу после «echo», стоят они там из-за специфичности bat-файлов, как можно заметить тэги html обрамляются символами «<» и «>», в тоже время эти символы служат перенаправлением ввода/вывода в bat-файлах, а посему мы не можем их здесь свободно использовать.

Рекомендую немного побаловаться с подобными bat-скриптами, это бывает очень полезно, попробуйте посмотреть другие переменные окружения. Немного скажу, отступив от темы, на UNIX-системах языки командных интерпретаторов очень сильно развиты и грань между программированием на языке командного интерпретатора и программированием на «реальном» языке программирования весьма и весьма размыта в некоторых случаях, поэтому на UNIX-системах частенько простенькие скрипты пишутся именно на языках командных интерпретаторов, но windows-интерпретатор cmd.exe или, ранее, command.com явно слабоваты для этих целей.

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

2. Программа должна уметь писать в стандартный поток вывода (stdout), чтобы передать результат своей работы серверу;

3. Из первых двух пунктов следует, то, что для того, чтобы сервер мог передать нашей программе что-либо в stdin, а она могла ему что-либо ответить в stdout CGI-программа должна быть консольным приложением;

Этого вполне достаточно для создания полноценного CGI-приложения.

Начнем с последнего пункта. Для получения доступа к переменным окружения Windows-приложения используется функция API «GetEnvironmentStrings», функция не имеет аргументов и возвращает указатель на массив переменных окружения (ИМЯ=ЗНАЧЕНИЕ) разделенных между собой нулем, массив закрывается двойным нулем, при запуске программы сервером в окружение программы помимо стандартных переменных добавляются специфические CGI-переменные, описанные выше, при запуске программы из командной строки вы их не увидите, естественно.

Для того, что бы писать что-то в stdout или читать из stdin сначала мы должны получить хэндлы этих потоков, делается это с помощью функции API «GetStdHandle», в качестве параметра функции передается одно из следующих значений:

STD_INPUT_HANDLE - для stdin (стандартный ввод);

STD_OUTPUT_HANDLE - для stdout (стандартный вывод);

STD_ERROR_HANDLE - для stderr.

Функция возвратит необходимый нам для операций чтения/записи хэндл. Следующее что нам необходимо делать это писать/читать эти потоки. Делается это обычными операциями чтения/записи файлов, т.е. ReadFile и WriteFile. Тут есть одна тонкость, можно подумать, что для этих целей можно использовать WriteConsole/ReadConsole, да это действительно справедливо для консоли и будет прекрасно работать, результаты, так же как и с WriteFile будут выводиться на консоль, но продолжаться это будет пока мы не запустим нашу программу как скрипт на сервере. Происходит это потому что, когда нашу программу запускает сервер хэндлы, возвращаемые функцией «GetStdHandle» уже не будут хэндлами консоли как таковыми, они будут хэндлами pipe, что необходимо для связи двух приложений.

Вот небольшой пример того, как должна выглядеть CGI-программа на ассемблере, думаю разобраться в ней не составит большого труда:>

386 .model flat,stdcall includelib import32.lib .const PAGE_READWRITE = 4h MEM_COMMIT = 1000h MEM_RESERVE = 2000h STD_INPUT_HANDLE = -10 STD_OUTPUT_HANDLE = -11 .data hStdout dd ? hStdin dd ? hMem dd ? header: db "Content-Type: text/html",13,10,13,10,0 start_html: db "Окружение CGI-программы выглядит \ так:
",13,10,0 for_stdin: db "STDIN программы содержит:
",13,10,0 end_html: db "",13,10,0 nwritten dd ? toscr db 10 dup (32) db " - Тип файла",0 .code _start: xor ebx,ebx call GetStdHandle,STD_OUTPUT_HANDLE mov hStdout,eax call GetStdHandle,STD_INPUT_HANDLE mov hStdin,eax call write_stdout, offset header call write_stdout, offset start_html call VirtualAlloc,ebx,1000,MEM_COMMIT+MEM_RESERVE,PAGE_READWRITE mov hMem,eax mov edi,eax call GetEnvironmentStringsA mov esi,eax next_symbol: mov al, or al,al jz end_string mov ,al next_string: cmpsb jmp short next_symbol end_string: mov ,">rb<" add edi,3 cmp byte ptr ,0 jnz next_string inc edi stosb call write_stdout, hMem call write_stdout, offset for_stdin call GetFileSize,,ebx mov edi,hMem call ReadFile,,edi, eax,offset nwritten, ebx add edi, mov byte ptr ,0 call write_stdout, hMem call write_stdout, offset end_html call VirtualFree,hMem call ExitProcess,-1 write_stdout proc bufOffs:dword call lstrlen,bufOffs call WriteFile,,bufOffs,eax,offset nwritten,0 ret write_stdout endp extrn GetEnvironmentStringsA:near extrn GetStdHandle:near extrn ReadFile:near extrn WriteFile:near extrn GetFileSize:near extrn VirtualAlloc:near extrn VirtualFree:near extrn ExitProcess:near extrn lstrlen:near ends end _start

Исполняемый файл строится командами:

tasm32.exe /ml test.asm

tlink32.exe /Tpe /ap /o test.obj

Не забудьте, что программа должна быть консольной.

Архив с программой .

Вызывать эту программу можно используя вышеописанную html-форму, нужно только поменять имя test.bat в форме на test.exe и скопировать его в /cgi-bin/ соответственно, при том можно выставить в методе запроса POST, программа его обрабатывает.

Еще хочу отметить, что можно вызывать программу и по-другому, можно создать в каталоге cgi-bin файл например test.cgi с одной единственной строчкой «#!c:/_путь_/test.exe» и вызывать в запросах его, а сервер в свою очередь будет читать первую его строчку и запускать exe-файл, для этого необходимо, чтобы в настройках http-сервера было прописано расширение *.cgi как расширение для скриптов. При таком подходе сервер запустит нашу программу с командной строкой «test.exe путь_к_test.exe» это имеет несколько плюсов – первое, это то, что человек, запускающий наш скрипт не будет даже догадываться на чем скрипт написан, второе – так-как нам передается имя файла с нашей строчкой мы можем например дописать в этот файл какие-либо настройки для нашего скрипта, что упрощает отладку, кстати именно так работают все интерпретаторы – вы успели заметить, что во всех perl/php/итд программах, присутствует подобная строка – указывающая на сам командный интерпретатор. Так вот сервер при запуске cgi-программы, если расширение программы прописано у него как скрипт в настройках читает первую строку файла, и если она оказывается описанного выше формата, то запускает указанную в строчке программу с именем этого файла ч/з пробел, допустим что в строчке указан интерпретатор перла, он получив такой подарок начинает его выполнение, т.к. комментарий в перле это символ «#», то первую строчку он пропускает и идет дальнейшее выполнение скрипта, в общем штука удобная.

Вот в общем и все о чем я хотел написать, не знаю насколько это все окажется Вам полезным, но скажу что у меня работает сервер интрасети используя скрипты на ассемблере. Каюсь, больших оснований делать это не было, но все же я сделал это сначала просто из эстетических соображений и некоторой не охоты учить перл/php или что-то еще. НО я никоим образом не отговариваю Вас учить перл, а наоборот скажу что сделать это нужно, и даже очень нужно, это я понял позже, но все же считаю, что на сильно загруженных серверах, где скорость выполнения, загрузки и объем памяти занимаемый приложением играет решающую роль cgi-скрипты, написанные на ассемблере займут свое достойное место.

  • Tutorial

Добрый день.
В этой статье я бы хотел рассказать про протокол FastCGI и способы работы с ним. Не смотря на то, что сам протокол и его реализация появились ещё в 1996 году, подробных руководств по этому протоколу просто нет - разработчики так и не написали справки к собственной библиотеке. Зато года два назад, когда я только начал пользоваться этим протоколом, часто встречались фразы типа «я не совсем понимаю, как пользоваться этой библиотекой». Именно этот недостаток я и хочу исправить - написать подробное руководство по использованию данного протокола в многопоточной программе и рекомендации по выбору различных параметров, которым могли бы воспользоваться все желающие.

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

Итак, в этой статье мы рассмотрим:
- Что такое FastCGI и чем отличается от протокола CGI
- Зачем мне нужен FastCGI, когда уже есть много языков для разработки под веб
- Какие реализации протокола FastCGI существуют
- Что такое сокеты
- Описание функций библиотеки FastCGI
- Простой пример многопоточной FastCGI-программы
- Простой пример конфигурации Nginx
К сожалению, очень сложно написать статью одинаково понятной новичкам и интересной опытным старожилам, поэтому я буду стараться осветить все моменты как можно подробнее, а Вы можете просто пропустить неинтересные Вам разделы.

Что такое FastCGI?

Про FastCGI можно прочитать в Википедии . Если в двух словах, это CGI-программа, запущенная в цикле. Если обычная CGI-программа заново запускается для каждого нового запроса, то в FastCGI-программе используется очередь запросов, которые обрабатываются последовательно. А теперь представьте: на Ваш 4-8-ядерный сервер поступило 300-500 одновременных запросов. Обычная CGI-программа будет запущена на выполнение эти самые 300-500 раз. Очевидно, такого количества процессов слишком много - Ваш сервер физически не сможет отработать их все сразу. Значит, у Вас получится очередь процессов, ожидающих свой квант процессорного времени. Обычно планировщик будет распределять процессорное равномерно (так так в данном случае приоритеты всех процессов одинаковые), а значит у Вас будет 300-500 «почти готовых» ответов на запросы. Звучит как-то не очень оптимистично, не правда ли? В FastCGI-программе все эти проблемы решаются простой очередью запросов (то есть применяется мультиплексирование запросов).

Зачем мне FastCGI, когда уже есть PHP, Ruby, Python, Perl и т.п.?

Пожалуй, главная причина - компилируемая программа будет работать быстрее интерпретируемой. Для PHP, например, существует целая линейка акселераторов, среди которых - APC, eAccelerator, XCache, которые уменьшают время интерпретации кода. Но для C/C++ всё это просто не нужно.
Второе, о чём Вы должны помнить - динамическая типизация и сборщик мусора занимают много ресурсов. Иногда - очень много. Например, массивы целых чисел в PHP занимают примерно в 18 раз больше памяти (до 35 раз в зависимости от различных параметров компиляции PHP), чем в C/C++ для того же объема данных, поэтому задумайтесь о накладных расходах для сравнительно больших структур данных.
Третье - FastCGI-программа может хранить общие для разных запросов данные. Например, если PHP каждый раз начинает обработку запроса с «чистого листа», то FastCGI-программа может сделать ряд подготовительных действий ещё до поступления первого запроса, например выделить память, загрузить часто используемые данные и т.п. - очевидно, всё это может повысить общую производительность системы.
Четвёртое - масштабируемость. Если mod_php предполагает, что веб-сервер Apache и PHP находятся на одной и той же машине, то FastCGI-приложение может использовать TCP-сокеты. Другими словами, у Вас может быть целый кластер из нескольких машин, связь с которыми осуществляется по сети. При этом FastCGI также поддерживает Unix domain sockets, что позволяет при необходимости эффективно запускать FastCGI-приложение и веб-сервер на одной и той же машине.
Пятое - безопасность. Вы не поверите, но с настройками по умолчанию Apache позволяет выполнять всё на свете. Например, если злоумышленник загрузит на сайт вредоносный скрипт exploit.php.jpg под видом «невинной картинки» и потом откроет её в браузере, Apache «честно» выполнит вредоносный php-код. Пожалуй, единственное достаточно надежное решение - удалять или изменять все потенциально опасные расширения из имен загружаемых файлов, в данном случае - php, php4, php5, phtml и т.п. Такой приём используется, например, в Drupal - ко всем «дополнительным» расширениям добавляется символ подчеркивания и получается exploit.php_.jpg. Правда следует отметить, что системный администратор может добавить любое дополнительное расширение файла в качестве обработчика php, так что какое-нибудь.html может вдруг превратиться в ужасную дыру в безопасности только из-за того, что.php выглядело некрасиво, было плохо для SEO или не нравилось заказчику. Итак, что же нам даёт в плане безопасности FastCGI? Во первых, если использовать вместо Apache веб-сервер Nginx, то он будет просто отдавать статические файлы. Точка. Другими словами, файл exploit.php.jpg будет отдан «как есть», без какой-либо обработки на стороне сервера, так что запустить вредоносный скрипт просто не получится. Во вторых, FastCGI-программа и веб-сервер могут работать из под разных пользователей, а значит и права на файлы и папки у них будут разные. Например, веб-сервер может только читать загруженные файлы - для отдачи статических данных этого достаточно, а FastCGI-программа может только читать и изменять содержимое папки с загружаемыми файлами - этого достаточно для загрузки новых и удаления старых файлов, но доступа непосредственно к самим загруженным файлам иметь не будет, а значит выполнить вредоносный код тоже не сможет. В третьих, FastCGI-программа может работать в chroot"е, отличном от chroot"а веб-сервера. Сам по себе chroot (смена корневой директории) позволяет сильно ограничить права программы, то есть повысить общую безопасность системы, потому что программа просто не сможет получить доступ к файлам за пределами указанного каталога.

Какой веб-сервер с поддержкой FastCGI лучше выбрать?

Если коротко - я пользуюсь Nginx . Вообще, серверов с поддержкой FastCGI довольно много, в том числе коммерческих, так что позвольте рассмотреть несколько альтернатив.
Apache - пожалуй, это первое, что приходит в голову, правда он потребляет гораздо больше ресурсов, чем Nginx. Например, на 10 000 неактивных HTTP keep-alive соединений Nginx расходует около 2,5M памяти, что вполне реально даже для сравнительно слабой машины, а Apache вынужден создавать новый поток для каждого нового соединения, так что 10 000 потоков - просто фантастика.
Lighttpd - главный недостаток этого веб-сервера в том, что он обрабатывает все запросы в одном потоке. Это значит, что могут быть проблемы с маштабируемостью - Вы просто не сможете задействовать все 4-8 ядер современных процессоров. И второе - если по какой-то причине подвиснет поток веб-сервера (например из-за длительного ожидания ответа от жесткого диска), у вас «зависнет» весь сервер. Другими словами, все остальные клиенты перестанут получать ответы из-за одного медленного запроса.
Еще один кандидат - Cherokee . По заявлениям разработчиков, в ряде случаев работает быстрее Nginx и Lighttpd.

Какие есть реализации протокола FastCGI?

На данный момент есть две реализации протокола FastCGI - библиотека libfcgi.lib от создателей протокола FastCGI, и Fastcgi++ - библиотека классов на С++. Libfcgi разрабатывалась с 1996 года и, по заявлениям Open Market, является очень стабильной, к тому же более распространена, поэтому пользоваться в этой статье будем ей. Хочется отметить, что библиотека написана на C, встроенную «обертку» C++ нельзя назвать высокоуровневой, поэтому будем использовать C-интерфейс.
Думаю, на установке самой библиотеки останавливаться смысла нет - в ней есть makefile, так что проблем быть не должно. Кроме того, в популярных дистрибутивах эта библиотека доступна из пакетов.

Что такое сокеты?

Общее понятие о сокетах можно получить в Википедии . Если в двух словах, сокеты - это способ межпроцессного взаимодействия.
Как мы помним, во всех современных операционных системах каждый процесс использует собственное адресное пространство. За непосредственный доступ к оперативной памяти отвечает ядро операционной системы, и если программа обратиться по несуществующему (в контексте данной программы) адресу памяти, ядро вернет segmentation fault (ошибка сегментирования) и закроет программу. Это замечательно - теперь ошибки в одной программе просто не могут повредить другим - они находятся как бы в других измерениях. Но раз у программ разное адресное пространство, от общих данных или обмена данными тоже быть не может. А если очень нужно передать данные из одной программы в другую, как тогда? Собственно, для решения этой проблемы и разрабатывались сокеты - два или более процесса (читайте: программы) подключаются к одному и тому же сокету и начинают обмен данными. Получается этакое «окно» в другой мир - через него можно получать и отправлять данные в другие потоки.
В зависимости от типа использования соединения сокеты бывают разные. Например, есть TCP-сокеты - они используют обычную сеть для обмена данными, то есть программы могут работать на разных компьютерах. Второй наиболее распространенный вариант - доменные сокеты Unix (Unix domain socket) - пригодны для обмена данными только в рамках одной машины и выглядят как обычный путь в файловой системе, но реально жесткий диск не используется - весь обмен данными происходит в оперативной памяти. Из-за того, что не нужно использовать сетевой стек, работают несколько быстрее (примерно на 10%), чем TCP-сокеты. Для ОС Windows данный вариант сокетов называется named pipe (именованный канал).
Примеры использования сокетов для ОС GNU/Linux можно найти в этой статье . Если Вы еще не работали с сокетами, я бы рекомендовал с ней ознакомиться - это не является обязательным, но улучшит понимание изложенных здесь вещей.

Как пользоваться библиотекой Libfcgi?

Итак, мы хотим создать многопоточное FastCGI-приложение, поэтому разрешите описать ряд наиболее важных функций.
Прежде всего, библиотеку нужно инициализировать:
int FCGX_Init(void);
Внимание! Эту функцию нужно вызывать перед любыми другими функциями этой библиотеки и только один раз (всего один раз, для любого количества потоков).

Далее нам нужно открыть слушающий сокет:
int FCGX_OpenSocket(const char *path, int backlog);
Переменная path содержит строку подключения к сокету. Поддерживаются как доменные сокеты Unix, так и TCP-сокеты, всю необходимую работу по подготовке параметров и вызова функции библиотека сделает сама.
Примеры строк подключения для доменных сокетов Unix:
"/tmp/fastcgi/mysocket" "/tmp/fcgi_example.bare.sock"
Думаю, тут всё понятно: нужно просто передать уникальный путь в виде строки, при этом все взаимодействующие с сокетом процессы должны иметь к нему доступ. Еще раз повторюсь: этот способ работает только в рамках одного компьютера, но несколько быстрее, чем TCP-сокеты.
Примеры строк подключения для TCP-сокетов:
":5000" ":9000"
В этом случае открывается TCP-сокет на указанном порту (в данном случае - 5000 или 9000 соответственно), при этом запросы будут приниматься с любого IP-адреса. Внимание! Данный способ потенциально небезопасен - если Ваш сервер подключен к сети Internet, то Ваша FastCGI-программа будет принимать запросы от любого другого компьютера. Значит, любой злоумышленник сможет отправить Вашей FastCGI-программе «пакет смерти». Разумеется, ничего хорошего в этом нет - в лучшем случае Ваша программа может просто «упасть» и получится отказ в обслуживании (DoS-атака, если хотите), в худшем - удаленное выполнение кода (это если совсем уж не повезёт), поэтому всегда ограничивайте доступ к таким портам при помощи файервола (межсетевого экрана), при этом доступ нужно предоставлять только тем IP-адресам, которые реально используются при штатной работе FastCGI-программы (принцип «запрещено все, что явно не разрешено»).
Следующий пример строк подключения:
"*:5000" "*:9000"
Способ полностью аналогичен предыдущему: открывается TCP-сокет с приёмом соединений от любого IP-адреса, поэтому в этом случае так же необходимо со всей тщательностью настраивать файервол. Единственный плюс от такой строки подключения сугубо административный - любой читающий конфигурационные файлы программист или системный администратор поймёт, что Ваша программа принимает соединения с любого IP-адреса, поэтому при прочих равных условиях лучше предпочесть данных вариант предыдущему.
Более безопасный вариант - явно указать IP-адрес в строке подключения:
"5.5.5.5:5000" "127.0.0.1:9000"
В этом случае запросы будут приниматься только от указанного IP-адреса (в данном случае - 5.5.5.5 или 127.0.0.1 соответственно), для всех остальных IP-адресов данный порт (в данном случае - 5000 или 9000 соответственно) будет закрыт. Это повышает общую безопасность системы, поэтому по возможности всегда используйте этот формат строки подключения к TCP-сокетам - а вдруг системный администратор «просто забудет» настроить файервол? Прошу обратить внимание на второй пример - там указан адрес той же машины (localhost). Это позволяет создать TCP-сокет на одной и той же машине, если по каким-то причинам Вы не можете использовать доменные сокеты Unix (например, потому что chroot веб-сервера и chroot FastCGI-программы находятся в разных папках и не имеют общих файловых путей). К сожалению, Вы не можете указать два или более разных IP-адреса, поэтому если Вам действительно нужно принимать запросы от нескольких веб-серверов, расположенных на разных компьютерах, то придётся или полностью открыть порт (см. предыдущий способ) и положиться на настройки Вашего файервола, или использовать несколько сокетов на разных портах. Так же библиотека libfcgi не поддерживает IPv6-адреса - в далёком 1996 году этот стандарт только-только появился на свет, так что придётся ограничить свои аппетиты обычными IPv4-адресами. Правда, если Вам действительно необходима поддержка IPv6, её сравнительно просто добавить, пропатчив функцию FCGX_OpenSocket - лицензия библиотеки это позволяет.
Внимание! Использование функции указания IP-адреса при создании сокета не является достаточной защитой - возможны атаки IP-спуфинга (подмены IP-адреса отправителя пакета), поэтому настройка файервола всё равно обязательна. Обычно в качестве защиты от IP-спуфинга файервол проверяет соответствие между IP-адресом пакета и MAC-адресом сетевой карты для всех хостов нашей локальной сети (точнее - для широковещательного домена с нашим хостом), и отбрасывает все поступающие из Интернета пакеты, обратный адрес которых находится в зоне частных IP-адресов или локального хоста (маски 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7, 127.0.0.0/8 и::1/128). Тем не менее, всё же лучше использовать данную возможность библиотеки - в случае неверно настроенного файервола отправить «пакет смерти» с подделанного IP-адреса гораздо сложнее, чем с любого, так как TCP-протокол имеет встроенную защиту от IP-спуфинга.
Последний вид строки подключения - использовать доменное имя хоста:
"example.com:5000" "localhost:9000"
В этом случае IP-адрес будет получен автоматически на основе доменного имени указанного Вами хоста. Ограничения всё те же - хосту должен соответствовать один IPv4-адрес, иначе возникнет ошибка. Правда, учитывая что сокет создается один раз в самом начале работы с FastCGI, вряд ли этот способ будет очень полезен - динамически менять IP-адрес всё равно не получится (точнее, после каждой смены IP-адреса придётся перезапускать Вашу FastCGI-программу). С другой стороны, возможно это будет полезно для сравнительно большой сети - запомнить доменное имя всё же легче, чем IP-адрес.

Второй параметр функции backlog определяет длину очереди запросов сокета. Специальное значение 0 (нуль) означает длину очереди по умолчанию для данной операционной системы.
Каждый раз, когда приходит запрос от веб-сервера, новое соединение ставится в эту очередь в ожидании обработки нашей FastCGI-программой. Если очередь полностью заполнится, все последующие запросы на соединение будут заканчиваться неудачей - веб-сервер получит ответ Connection refused (в подключении отказано). В принципе, ничего плохого в этом нет - у веб-сервера Nginx есть своя очередь запросов, и если свободных ресурсов нет, то новые запросы будут ожидать своей очереди на обработку уже в очереди веб-сервера (по крайней мере до тех пор, пока не истечёт время ожидания). Кроме того, если у Вас несколько серверов с работающей FastCGI-программой, Nginx может передать такой запрос на менее загруженный сервер.
Итак, давайте попробуем разобраться, какая длина очереди будет оптимальной. Вообще, этот параметр лучше настраивать индивидуально исходя из данных нагрузочного тестирования, но мы попробуем оценить наиболее подходящий диапазон для этой величины. Первое, что нужно знать - максимальная длина очереди ограничена (определяется настройками ядра операционной системы, обычно - не более 1024 подключений). Второе - очередь потребляет ресурсы, копеечные, но всё же ресурсы, поэтому необоснованно длинной её делать не стоит. Далее, допустим у нашей FastCGI-программы есть 8 рабочих потоков (вполне реально для современных 4-8-ядерных процессоров), и каждому потоку нужно собственное подключение - задачи обрабатываются параллельно. Значит, в идеале, у нас уже должно быть 8 запросов от веб-сервера, чтобы сразу же, без ненужных задержек, обеспечить работой все потоки. Другими словами, минимальный размер очереди запросов - это количество рабочих потоков FastCGI-программы. Можно попробовать увеличить эту величину на 50%-100%, чтобы обеспечить некоторый запас по загрузке, так как время передачи данных по сети конечно.
Теперь давайте определимся с верхней границей этой величины. Тут нужно знать, сколько запросов мы реально можем обработать и ограничить очередь запросов этой величиной. Представьте, что Вы сделали эту очередь слишком большой - настолько, что Вашим клиентам просто надоедает ждать своей очереди и они просто уходят с Вашего сайта так и не дождавшись ответа. Очевидно, ничего хорошего в этом нет - веб-сервер должен был отправить запрос на открытие соединения, что само по себе дорого, а потом ещё и закрыть это соединение только лишь по тому, что FastCGI-программе не хватило времени на обработку этого запроса. Одним словом, мы только тратим процессорное время впустую, а ведь его нам как раз и не хватает! Но это еще не самое страшное - хуже, когда клиент отказался от получения информации с Вашего сайта уже поле начала обработки запроса. Получается, что мы должны будем полностью обработать в сущности уже никому не нужный запрос, что, согласитесь, только ухудшит ситуацию. Теоретически может возникнуть ситуация, когда большая часть клиентов так и не дождется ответа при 100% загрузке Вашего процессора. Нехорошо.
Итак, допустим один запрос мы можем обработать за 300 миллисекунд (то есть 0,3 секунды). Далее нам известно, что в среднем 50% посетителей покидают ресурс, если веб-страница грузится более 30 секунд. Очевидно, что 50% недовольных - это слишком много, поэтому ограничим максимальное время загрузки страницы в 5 секунд. При этом имеется ввиду уже полностью готовая веб-страница - после применения каскадных таблиц стилей и выполнения JavaScript"ов - этот этап на среднестатистическом сайте может занимать 70% от общего времени загрузки веб-страницы. Итак, на загрузку данных по сети осталось не больше 5*0,3 = 1,5 секунд. Дальше следует вспомнить, что html-код, таблицы стилей, скрипты и графика передаются в разных файлах, причём сначала - html-код, а потом уже всё остальное. Правда, после получения html-кода браузер начинает запрашивать оставшиеся ресурсы параллельно, так что можно оценить время загрузки html-кода как 50% от общего времени получения данных. Итак, в нашем распоряжении осталось не более 1,5*0,5 = 0,75 секунды на обработку одного запроса. Если в среднем один поток обрабатывает запрос за 0,3 секекунды, то в очереди должно быть 0,75/0,3 = 2,5 запроса на поток. Так как у нас 8 рабочих потоков, то результирующий размер очереди должен составлять 2,5*8 = 20 запросов. Хочется отметить условность приведенных расчетов - при наличии конкретного сайта используемые в расчете величины можно определить гораздо точнее, но всё же он дает отправную точку для более оптимальной настройки производительности.

Итак, мы получили дескриптор сокета, после этого необходимо выделить память под структуру запроса. Описание этой структуры следующее:
typedef struct FCGX_Request { int requestId; int role; FCGX_Stream *in; FCGX_Stream *out; FCGX_Stream *err; char **envp; struct Params *paramsPtr; int ipcFd; int isBeginProcessed; int keepConnection; int appStatus; int nWriters; int flags; int listen_sock; int detached; } FCGX_Request;
Внимание! После получения нового запроса все предыдущие данные будут утеряны, поэтому при необходимости длительного хранения данных применяйте глубокое копирование (копируйте сами данные, а не указатели на данные).
Вы должны знать об этой структуре следующее:
- переменные in, out и err играют роль соответственно потоков ввода, вывода и ошибок. Поток ввода содержит данные POST-запроса, в поток вывода нужно отправить ответ FastCGI-программы (например, http-заголовки и html-код веб-страницы), а поток ошибок просто добавит запить в лог ошибок веб-сервера. При этом потоком ошибок можно вообще не пользоваться - если Вам действительно нужно логгировать ошибки, то, пожалуй, для этого лучше использовать отдельный файл - передача данных по сети и их последующая обработка веб-сервером потребляет дополнительные ресурсы.
- переменная envp содержит значения переменных окружения, устанавливаемых веб-сервером, и http-заголовки, например: SERVER_PROTOCOL, REQUEST_METHOD, REQUEST_URI, QUERY_STRING, CONTENT_LENGTH, HTTP_USER_AGENT, HTTP_COOKIE, HTTP_REFERER и так далее. Эти заголовки определяются соответственно стандартами CGI и HTTP протоколов, примеры их использования можно найти в любой CGI-программе. Сами данные хранятся в массиве строк, при этом последний элемент массива содержит нулевой указатель (NULL) в качестве обозначения конца массива. Каждая строка (каждый элемент массива строк) содержит одно значение переменной в формате НАЗВАНИЕ_ПЕРЕМЕННОЙ=ЗНАЧЕНИЕ, например: CONTENT_LENGTH=0 (в данном случае означает, что у данного запроса нет POST-данных, так как их длина равна нулю). Если в массиве строк envp нет нужного Вам заголовка, значит он не был передан. Если Вы хотите получить все переданные FastCGI-программе значения переменных, просто прочитайте в цикле все строки массива envp до тех пор пока не встретите указатель на NULL.
Собственно, на этом с описанием этой структуры мы закончили - всё остальные переменные Вам не понадобятся.

Память выделили, теперь нужно выполнить инициализацию структуры запроса:
int FCGX_InitRequest(FCGX_Request *request, int sock, int flags);
Параметры функции следующие:
request - указатель на структуру данных, которую нужно инициализировать
sock - дескриптор сокета, который мы получили после вызова функции FCGX_OpenSocket. Хочется отметить, что вместо уже готового дескриптора можно передать 0 (нуль) и получить сокет с настройками по умолчанию, но для нас данный способ совершенно не интересен - сокет будет открыт на случайном свободном порте, а, значит, мы не сможем правильно настроить наш веб-сервер - нам неизвестно заранее, куда именно нужно отправлять данные.
flags - флаги. Собственно, в эту функцию можно передать только один флаг - FCGI_FAIL_ACCEPT_ON_INTR - не вызывать FCGX_Accept_r при разрыве.

После этого нужно получить новый запрос:
int FCGX_Accept_r(FCGX_Request *request);
В неё нужно передать уже инициализированную на прошлом этапе структуру request. Внимание! В многопоточной программе необходимо использовать синхронизацию при вызове данной функции.
Собственно, эта функция выполняет всю работу по работе с сокетами: сначала она отправляет ответ веб-серверу на предыдущий запрос (если таковой был), закрывает предыдущий канал передачи данных и освобождает все связанные с ним ресурсы (в том числе - переменные структуры request), потом получает новый запрос, открывает новый канал передачи данных и подготавливает новые данные в структуре request для их последующей обработки. В случае ошибки получения нового запроса функция возвращает код ошибки, меньший нуля.

Далее Вам наверняка потребуется получить переменные окружения, для этого можно или самостоятельно обработать массив request->envp, или воспользоваться функцией
char *FCGX_GetParam(const char *name, FCGX_ParamArray envp);
где name - строка, содержащая название переменной окружения или http-заголовка, значение которого Вы хотите получить,
envp - массив переменных окружения, которые содержатся в переменной request->envp
Функция возвращает значение нужной нам переменной окружения в виде строки. Пусть внимательного читателя не пугает несоответствие типов между char ** и FCGX_ParamArray - эти типы объявлены синонимами (typedef char **FCGX_ParamArray).
Кроме того, Вам наверняка понадобится отправить ответ веб-серверу. Для этого нужно воспользоваться потоком вывода request->out и функцией
int FCGX_PutStr(const char *str, int n, FCGX_Stream *stream);
где str - буффер, содержащий данные для вывода, без завершающего нуля (то есть буффер может содержать двоичные данные),
n - длинна буффера в байтах,
stream - поток, в который мы хотим вывести данные (request->out или request->err).

Если Вы пользуетесь стандартами C-строками с завершающим нулём, удобнее будет использовать функцию
int FCGX_PutS(const char *str, FCGX_Stream *stream);
которая просто определит длину строки функцией strlen(str) и вызовет предыдущую функцию. Поэтому, если Вам заранее известна длина строки (например, Вы пользуетесь C++-строками std::string), лучше используйте предыдущую функцию по соображениям эффективности.
Хочется отметить, что эти функции прекрасно работают с UTF-8-строками, так что с многоязычными веб-прилодениями проблем быть не должно.
Вы так же можете вызывать эти функции несколько раз во время обработки одного и того же запроса, в ряде случаев это может повысить производительность. Например, Вам нужно отправить какой-то большой файл. Вместо того, чтобы загружать весь этот файл с жёсткого диска, а потом уже отправить его «одним куском», Вы можете сразу же начать отправлять данные. В результате клиент вместо белого экрана браузера начнёт получать интересующие его данные, что чисто психологически заставит его ещё немного подождать. Другими словами, Вы как бы выигрываете немного времени для загрузки страницы. Так же хочется отметить, что большинство ресурсов (каскадные таблицы стилей, JavaScript"ы и т.п.) указываются в начале веб-страницы, то есть браузер сможет проанализировать часть html-кода и начать загрузку этих ресурсов раньше - ещё один повод выводить данные по частям.

Следующее, что Вам может понадобиться - это обработать POST-запрос. Для того, что бы получить его значение, нужно прочитать данные из потока request->in при помощи функции
int FCGX_GetStr(char * str, int n, FCGX_Stream *stream);
где str - указатель на буффер,
n - размер буффера в байтах,
stream - поток, из которого мы читаем данные.
Размер передаваемых данных в POST-запросе (в байтах) можно определить с помощью переменной окружения CONTENT_LENGTH, значение которой, как мы помним, можно получить с помощью функции FCGX_GetParam. Внимание! Создавать буффер str на основании значения переменной CONTENT_LENGTH без каких-либо ограничений очень плохая идея: любой злоумышленник может отправить любой, сколь угодно большой POST-запрос, и у Вашего сервера может просто закончиться свободная оперативная память (получится DoS-атака, если хотите). Вместо этого лучше ограничить размер буффера какой-то разумной величиной (от нескольких килобайт до нескольких мегабайт) и вызывать функцию FCGX_GetStr несколько раз.

Последняя важная функция флеширует потоки вывода и ошибок (отправляет клиенту всё ещё не отправленные данные, которые мы успели поместить в потоки вывода и ошибок) и закрывает соединение:
void FCGX_Finish_r(FCGX_Request *request);
Хочется особо отметить, что эта функция не является обязательной: функция FCGX_Accept_r так же отправляет клиенту данные и закрывает текущее соединение перед получением нового запроса. Спрашивается: тогда зачем же она нужна? Представьте, что Вы уже отправили клиенту все необходимые данные, и сейчас Вам нужно выполнить какие-то завершающие операции: записать статистику в базу данных, ошибки в лог-файл и т.п. Очевидно, что соединение с клиентом уже больше не нужно, но клиент (в смысле, браузер) всё ещё ждёт от нас информацию: а вдруг мы отправим что-нибудь еще? При этом очевидно, что мы не можем вызвать FCGX_Accept_r раньше времени - после этого нужно будет начать обрабатывать следующий запрос. Как раз в этом случае Вам понадобится функция FCGX_Finish_r - она позволит закрыть текущее соединение до получения нового запроса. Да, мы сможем обработать такое же число запросов в единицу времени, как и без использования этой функции, но клиент получит ответ раньше - ему уже не придется ждать конца выполнения наших завершающих операций, а ведь именно из-за большей скорости обработки запросов мы и используем FastCGI.
На этом, собственно, заканчивается описание функций библиотеки и начинается обработка полученных данных.

Простой пример многопоточной FastCGI-программы

Думаю, в примере всё будет понятно. Единственное, печать отладочных сообщений и «засыпание» рабочего потока сделаны исключительно в демонстрационных целях. При компилировании программы не забудьте подключить библиотеки libfcgi и libpthread (параметры компилятора gcc: -lfcgi и -lpthread).

#include #include #include #include "fcgi_config.h" #include "fcgiapp.h" #define THREAD_COUNT 8 #define SOCKET_PATH "127.0.0.1:9000" //хранит дескриптор открытого сокета static int socketId; static void *doit(void *a) { int rc, i; FCGX_Request request; char *server_name; if(FCGX_InitRequest(&request, socketId, 0) != 0) { //ошибка при инициализации структуры запроса printf("Can not init request\n"); return NULL; } printf("Request is inited\n"); for(;;) { static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER; //попробовать получить новый запрос printf("Try to accept new request\n"); pthread_mutex_lock(&accept_mutex); rc = FCGX_Accept_r(&request); pthread_mutex_unlock(&accept_mutex); if(rc < 0) { //ошибка при получении запроса printf("Can not accept new request\n"); break; } printf("request is accepted\n"); //получить значение переменной server_name = FCGX_GetParam("SERVER_NAME", request.envp); //вывести все HTTP-заголовки (каждый заголовок с новой строки) FCGX_PutS("Content-type: text/html\r\n", request.out); //между заголовками и телом ответа нужно вывести пустую строку FCGX_PutS("\r\n", request.out); //вывести тело ответа (например - html-код веб-страницы) FCGX_PutS("\r\n", request.out); FCGX_PutS("\r\n", request.out); FCGX_PutS("\r\n", request.out); FCGX_PutS("\r\n", request.out); FCGX_PutS("

FastCGI Hello! (multi-threaded C, fcgiapp library)

\r\n", request.out); FCGX_PutS("

Request accepted from host ", request.out); FCGX_PutS(server_name ? server_name: "?", request.out); FCGX_PutS("

\r\n", request.out); FCGX_PutS("\r\n", request.out); FCGX_PutS("\r\n", request.out); //"заснуть" - имитация многопоточной среды sleep(2); //закрыть текущее соединение FCGX_Finish_r(&request); //завершающие действия - запись статистики, логгирование ошибок и т.п. } return NULL; } int main(void) { int i; pthread_t id; //инициализация библилиотеки FCGX_Init(); printf("Lib is inited\n"); //открываем новый сокет socketId = FCGX_OpenSocket(SOCKET_PATH, 20); if(socketId < 0) { //ошибка при открытии сокета return 1; } printf("Socket is opened\n"); //создаём рабочие потоки for(i = 0; i < THREAD_COUNT; i++) { pthread_create(&id[i], NULL, doit, NULL); } //ждем завершения рабочих потоков for(i = 0; i < THREAD_COUNT; i++) { pthread_join(id[i], NULL); } return 0; }

Простой пример конфигурации Nginx

Собственно, простейший пример конфига выглядит так:

Server { server_name localhost; location / { fastcgi_pass 127.0.0.1:9000; #fastcgi_pass unix:/tmp/fastcgi/mysocket; #fastcgi_pass localhost:9000; include fastcgi_params; } }

В данном случае этого конфига достаточно для корректной работы нашей FastCGI-программы. Закоментированные строчки - это пример работы с соответственно доменными сокетами Unix и заданием доменного имени хоста вместо IP-адреса.
После компиляции и запуска программы, и настройки Nginx у меня по адресу localhost появилась гордая надпись:
FastCGI Hello! (multi-threaded C, fcgiapp library)

Спасибо всем, кто дочитал до конца.