Матрёшка: использование FastCGI в Web приложениях

До недавнего времени средства разработки Web приложений на языке программирования Ada сложно было признать пригодными для широкого использования. Фактически было доступно два варианта: использовать один из вариантов реализации протокола CGI либо же специализированный Web сервер AWS. Первый вариант не только низкоэффективен сам по себе, но является ещё более низкоэффективным при программировании на Ada за счёт накладных расходов на запуск и останов приложения. Второй вариант не позволяет использовать приложение совместно с штатными Web‐серверами, что накладывает ограничения на применение конечного приложения.

Модуль FastCGI предоставляемый Матрёшкой исправил ситуацию за счёт возможности разработки приложения с одной стороны предназначенного для использования со стандартными web‐серверами, а с другой — предполагающим работу приложения в виде самостоятельного процесса, не требующего запуск нового процесса для обработки каждого запроса.

Далее в этой статье будет рассмотрено простейшее Web‐приложение класса «Hello, world!"; предназначенное показать, что один запущенный процесс обрабатывает несколько запросов от сервера.

Общая структура приложения

Общая структура приложения фактически всегда одинакова, она состоит в инициализации приложения и запуска цикла обработки запросов.


          

          
with FastCGI.Application;
with Callbacks;

procedure Demo is
begin
   FastCGI.Application.Initialize;
   FastCGI.Application.Execute (Callbacks.Handler'Access);
   FastCGI.Application.Finalize;
end Demo;

Обработчик запросов

Пакет Callbacks содержит процедуру обработки запросов, вызываемую при получении запроса от Web‐сервера для его обработки. Спецификация пакета Callbacks выглядит следующим образом:


          

          
with FastCGI.Requests;
with FastCGI.Replies;

package Callbacks is

   procedure Handler
    (Request : FastCGI.Requests.Request;
     Reply   : out FastCGI.Replies.Reply;
     Status  : out Integer);

end Callbacks;

В параметре Request обработчику передаётся вся информация касающаяся поступившего запроса: параметры запроса, заголовки запроса и, при наличии, содержимое запроса. Объект, поступающий в параметре Reply предназначен для формирования ответа на запрос, который будет отправлен клиенту. Последний параметр предназначен для индикации ошибки при обработке запроса. Его не нужно путать с кодом ошибки HTTP запроса, этот параметр является частью протокола FastCGI. В общем случае его нужно установить в значение 0, что будет означать, что запрос успешно обработан приложением.

Теперь пора рассмотреть реализацию подпрограммы обработки запросов, которая будет выводить надпись «Hello, world!» и количество обработанных запросов с момента последнего запуска приложения.


          

          
with Ada.Streams;
with League.Strings;
with League.Text_Codecs;

package body Callbacks is

   Counter : Natural := 0;
   Codec   : constant League.Text_Codecs.Text_Codec
     := League.Text_Codecs.Codec
         (League.Strings.To_Universal_String ("utf-8"));

   procedure Handler
    (Request : FastCGI.Requests.Request;
     Reply   : out FastCGI.Replies.Reply;
     Status  : out Integer)
   is
      Text : constant League.Strings.Universal_String
        := League.Strings.To_Universal_String
            ("<?xml version='1.1' encoding='utf-8'?>"
               & "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'"
               & " 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>"
               & "<html xmlns='http://www.w3.org/1999/xhtml'>"
               & "<head>"
               & "<title>Matreshka's FastCGI demo</title>"
               & "</head>"
               & "<body>"
               & "<p>Hello, world!</p>"
               & "<p>Number of processed requests:"
               & Integer'Wide_Wide_Image (Counter)
               & "</p>"
               & "</body>"
               & "</html>");

   begin
      Counter := Counter + 1;

      Reply.Set_Content_Type
       (League.Strings.To_Universal_String ("text/html"));
      Ada.Streams.Stream_Element_Array'Write
       (Reply.Stream,
        Codec.Encode (Text).To_Stream_Element_Array);

      Status := 0;
   end Handler;

end Callbacks;

Прежде всего, нам необходим кодек для преобразования текстовых данных во внешнее представление. Объект Codec и представляет собой таковой, настроенный на преобразование текстовых данных в кодировку UTF-8. Более подробно об использовании текстовых кодеков можно прочитать в статье Матрёшка: обмен текстовыми данными.

Второй объявленный на библиотечном уровне объекат — переменная Counter — используется для подсчёта количества обработанных запросов.

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

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

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


          

          
with "fastcgi";

project Demo is

   for Main use ("demo.adb");
   for Object_Dir use ".objs";

   package Compiler is
      for Default_Switches ("Ada") use ("-g", "-gnat12");
   end Compiler;
end Demo;

Автор: Вадим Годунко
Дата: 17.12.2011