Group
Extension

POD2-RU-PSGI/lib/POD2/RU/PSGI.pod

=encoding utf-8

=head1 НАИМЕНОВАНИЕ

POD2::RU::PSGI - Спецификация Веб-серверного Шлюзового Интерфейса для Perl

=head1 ОБЗОР

Этот документ определяет стандартный интерфейс между веб-серверами
и веб-приложениями или фреймворками на Perl. Этот интерфейс призван
содействовать переносимости веб-приложений и сокращению дублирования
усилий разработчиков веб-фреймворков.

Пожалуйста, имейте в виду, что PSGI - не просто "Ещё Один Веб-фреймворк".
PSGI - это спецификация, предназначенная для отделения среды веб-сервера
от кода веб-фреймворка. Также, PSGI не является программным интерфейсом (API)
для веб-приложений. Разработчики веб-приложений (конечные пользователи) не
будут запускать свои веб-приложения, используя PSGI напрямую - вместо этого
предполагается использование фреймворков, поддерживающих PSGI.

=head1 ТЕРМИНОЛОГИЯ

=over 4

=item Веб-серверы

I<Веб-серверы> принимают HTTP запросы от веб-клиентов, передают эти запросы
веб-приложениям (если сконфигурированы делать это) и возвращают HTTP ответы
инициировавшим запрос клиентам.

=item PSGI сервер

I<PSGI сервер> - это программа на Perl, предоставляющая среду для запуска
в ней I<PSGI приложения>.

PSGI определяет интерфейс для веб-приложений и основную задачу веб-приложений,
которая заключается в том, чтобы обслуживать запросы через Интернет. I<PSGI
сервер>, скорее всего, будет либо: частью веб-сервера (как Apache mod_perl),
соединён с веб-сервером (при помощи FastCGI, SCGI), вызываться веб-сервером
(как в старом добром CGI), или же сам по себе будет автономным веб-сервером,
написанным целиком или частично на Perl.


Тем не менее, I<PSGI сервер> не обязан в действительности быть веб-сервером
или его частью, так как I<PSGI> только определяет интерфейс между сервером
и приложением, а не между сервером и остальным миром.

I<PSGI Сервер> также часто называется I<PSGI Application Container>,
так как он похож на I<Java Servlet container>, который представляет собой
Java процесс, предоставляющий среду для I<сервлетов Java>.

=item Приложения

I<Веб-приложения> принимают HTTP запросы и возвращают HTTP ответы.

I<PSGI приложения> - веб-приложения, соответствующие интерфейсу PSGI.
Они должны принимать форму code reference с определёнными входными
и выходными данными.

Для простоты I<PSGI приложения> также будут называться I<Приложениями>
до конца этого документа.

=item Middleware (связующее ПО)

I<Middleware> (I<Связующее ПО>) - приложение PSGI (code reference), I<а также>
I<Сервер>. I<Middleware> выглядит как I<приложение>, когда вызывается со
стороны I<сервера>, и, в свою очередь, может вызывать другие I<приложения>.
Можно думать о нём как о I<плагине> для расширения приложения PSGI.

=item Разработчики фреймворков

I<Разработчики фреймворков> - это авторы фреймворков для веб-приложений. Они
пишут адаптеры (или движки), которые принимают входные данные PSGI, запускают
веб-приложение и возвращают ответ PSGI I<серверу>.

=item Разработчики веб-приложений

I<Разработчики веб-приложений> - это разработчики, которые пишут код на основе
веб-фреймворков. Эти разработчики никогда не должны иметь дело с PSGI
напрямую.

=back

=head1 СПЕЦИФИКАЦИЯ

=head2 Приложение

Приложение PSGI - это Perl code reference. Оно принимает ровно один аргумент,
окружение, и возвращает ссылку на массив, содержащий ровно три
значения.

  my $app = sub {
      my $env = shift;
      return [
          '200',
          [ 'Content-Type' => 'text/plain' ],
          [ "Hello World" ], # or IO::Handle-like object
      ];
  };

=head3 Окружение

Окружение B<ДОЛЖНО> быть ссылкой на хеш, включающей в себя CGI-подобные
заголовки, как описано ниже. Приложение может свободно изменять окружение.
Окружение B<ДОЛЖНО> содержать эти переменные (заимствовано из
L<PEP 333|http://www.python.org/dev/peps/pep-0333/>,
L<Rack|http://rack.rubyforge.org/doc/files/SPEC.html> и
L<JSGI|http://jackjs.org/jsgi-spec.html>), за исключением тех случаев, когда
они, как правило, пусты.

Если переменная окружения является логической переменной, её значение B<ОБЯЗАНО>
соответствовать нотации Perl для булевых чисел. Это означает, что пустая
строка или явно указанный C<0> являются правильными false значениями. Если
булевый ключ отсутствует, приложение B<МОЖЕТ> интерпретировать его как false
значение.

Значения всех CGI переменных (в имени которых нет точки) B<ДОЛЖНЫ> быть
скалярными строками.

Подробнее см. ниже.

=over 4

=item *

C<REQUEST_METHOD>: Метод HTTP запроса, такой как "GET" или "POST".
Это B<НЕ ДОЛЖНА> быть пустая строка, так что метод всегда должен
присутствовать.

=item *

C<SCRIPT_NAME>: Начальная часть I<пути> URL запроса, соответствующего
приложению. Говорит приложению о своем виртуальном "местоположении". Может
быть пустой строкой, если приложение соответствует корневому URI сервера.

Если этот ключ не пуст, он B<ДОЛЖЕН> начинаться с прямого слэша (C</>).

=item *

C<PATH_INFO>: Остаточная часть I<пути> URL запроса, определяющая виртуальное
"расположение" цели запроса внутри приложения. Может быть пустой строкой, если
запрашиваемый URL указывает на корень приложения и не имеет завершающего
слэша. Это значение должно быть URI-декодировано серверами для того, чтобы
быть совместимым с L<RFC 3875|http://www.ietf.org/rfc/rfc3875>.

Если эта переменная не пуста, она B<ДОЛЖНА> начинаться с прямого слэша (C</>).

=item *

C<REQUEST_URI>: Не декодированная, необработанная строка URL. Это
необработанный путь URI и часть запроса, которые фигурируют в HTTP C<GET /...
HTTP/1.x> строке и не содержат схемы URI и имён хоста.

В отличие от C<PATH_INFO>, это значение B<НЕ ДОЛЖНО> декодироваться сервером.
Это ответственность приложения - правильно декодировать пути для того,
чтобы отображать URL-ы на обработчики приложения, если оно предпочитает
использовать эту переменную окружения вместо C<PATH_INFO>.

=item *

C<QUERY_STRING>: Порция запрашиваемого URL, которая следует за C<?>, если
таковая имеется. Эта переменная B<МОЖЕТ> быть пустой, но B<ДОЛЖНА> всегда
присутствовать, даже если она пуста.

=item *

C<SERVER_NAME>, C<SERVER_PORT>: При комбинации со C<SCRIPT_NAME> и
C<PATH_INFO>, эти переменные окружения могут быть использованы для завершения
URL. Обратите внимание, что, тем не менее, для реконструкции запрашиваемого
URL предпочтительнее использовать C<HTTP_HOST> (если он присутствует),
чем C<SERVER_NAME>. C<SERVER_NAME> и C<SERVER_PORT> B<НЕ ДОЛЖНЫ> быть пустыми
строками, и всегда должны присутствовать.

=item *

C<SERVER_PROTOCOL>: Версия протокола, используемого клиентом для отправки
запроса. Как правило, имеет вид "HTTP/1.0" или "HTTP/1.1" и может
использоваться приложением для определения того, как трактовать заголовки
запроса HTTP.

=item *

C<CONTENT_LENGTH>: Длина содержимого в байтах, в виде целого числа.
Присутствие или отсутствие этой переменной окружения должно соответствовать
присутствию или отсутствию HTTP заголовка Content-Length в запросе.

=item *

C<CONTENT_TYPE>: MIME-тип запроса, в том виде, в каком он указан клиентом.
Присутствие или отсутствие этой переменной окружения должно соответствовать
присутствию или отсутствию HTTP заголовка Content-Type в запросе.

=item *

Переменные C<HTTP_*> : Эти переменные окружения соответствуют предоставленным
клиентом заголовкам HTTP запроса. Присутствие или отсутствие этих ключей
должно соответствовать присутствию или отсутствию соответствующих заголовков
HTTP в запросе.

Переменная окружения получается путём конвертации имени поля заголовка HTTP в
верхний регистр, замещения всех дефисов C<-> на подчёркивания C<_> и
подстановки приставки C<HTTP>, как в L<RFC 3875|http://www.ietf.org/rfc/rfc3875>.

Если есть несколько строк заголовка, отправленных с одним ключом (именем
переменной), серверу следует рассматривать их так, как будто они были посланы в
одной строке и объединять их при помощи C<, >, как описано в
L<RFC 2616|http://www.ietf.org/rfc/rfc2616>.

=back

В добавление к ключам, описанным выше, окружение PSGI B<ДОЛЖНО> также включать
следующие специфичные для PSGI ключи:

=over 4

=item *

C<psgi.version>: Ссылка на массив [1,1], представляющий используемую версию
PSGI. Первое число - старший номер версии, второе - младший номер версии.

=item *

C<psgi.url_scheme>: Строка C<http> или C<https>, зависящая от URL запроса.

=item *

C<psgi.input>: входной поток. Подробности см. ниже.

=item *

C<psgi.errors>: поток ошибок. Подробности см. ниже.

=item *

C<psgi.multithread>: Это булево значение, которое B<ДОЛЖНО> быть true, если
приложение может быть одновременно вызвано другой нитью того же процесса,
иначе false.

=item *

C<psgi.multiprocess>: Это булево значение, которое B<ДОЛЖНО> быть true, если
эквивалентный объект приложения может быть одновременно вызван другим
процессом, иначе false.

=item *

C<psgi.run_once>: Булево значение, содержащее true, если сервер ожидает (но не
гарантирует!), что приложение будет вызвано только в этот единственный раз за
время жизни содержащего его процесса. Обычно это бывает верно только для
сервера, основанного на CGI (или на чём-то похожем).


=item *

C<psgi.nonblocking>: Булево значение, содержащее true, если сервер вызывает
приложение в неблокирующем событийном цикле.

=item *

C<psgi.streaming>: Булево значение, равное true, если сервер ожидает
отложенного ответа в стиле callback и потокового пишущего объекта.

=back

Сервер или приложение может также сохранять свои данные в переменных
окружения. Эти переменные B<ДОЛЖНЫ> содержать по меньшей мере одну точку, и их
B<СЛЕДУЕТ> называть, начиная с уникальной приставки.

Приставка C<psgi.> зарезервирована для использования с основной спецификацией
PSGI, а приставка C<psgix.> зарезервирована для официально одобренных
расширений. Эти приставки B<НЕ ДОЛЖНЫ> использоваться другими серверами или
приложением. См. список официально одобренных расширений в
L<psgi-extensions|PSGI::Extentions>.

Окружение B<НЕ ДОЛЖНО> содержать ключи с именем C<HTTP_CONTENT_TYPE> или
C<HTTP_CONTENT_LENGTH>.

Одна из переменных C<SCRIPT_NAME> или C<PATH_INFO> B<ДОЛЖНА> быть установлена.
Когда C<REQUEST_URI> - это C</>, переменной C<PATH_INFO> следует быть равной
C</>, а переменной C<SCRIPT_NAME> следует быть пустой. Переменная
C<SCRIPT_NAME> B<НЕ ДОЛЖНА> быть равной C</>, но B<МОЖЕТ> быть пустой.


=head3 Входной Поток

Входной поток в C<psgi.input> - это L<IO::Handle>-подобный объект, который
отдаёт поток необработанных данных HTTP POST или PUT запросов. Если это
дескриптор файла, он B<ДОЛЖЕН> быть открыт в бинарном режиме. Входной поток
B<ДОЛЖЕН> отвечать на запрос C<read> и B<МОЖЕТ> реализовывать C<seek>.

Встроенные в Perl файловые дескрипторы или основанные на L<IO::Handle> объекты
должны работать "как есть" внутри PSGI сервера. Разработчикам приложений B<НЕ
СЛЕДУЕТ> контролировать тип или класс потока. Вместо этого им B<СЛЕДУЕТ>
просто вызывать метод C<read> объекта.

Разработчикам приложений B<НЕ СЛЕДУЕТ> использовать встроенную функцию Perl
C<read> или итератор (C<< <$fh> >>) для чтения из входного потока. Вместо
этого разработчикам приложений следует вызывать C<read> как метод
(C<< $fh->read >>) для обеспечения утиной типизации.

Разработчикам фреймворков, если они знают, что входной поток будет использован
со встроенной функцией read() в любом апстрим-коде, который они не смогут
трогать, B<СЛЕДУЕТ> использовать PerlIO или tied handle для обработки этой
проблемы.

Ожидается, что объект входного потока обеспечивает метод C<read>:

=over 4

=item read

  $input->read($buf, $len [, $offset ]);

Возвращает число действительно прочитанных символов, 0 в конце файла, или
undef при ошибке.

=back

Он может также реализовывать опциональный метод C<seek>. Если
переменная окружения C<psgix.input.buffered> равна true, он B<ДОЛЖЕН>
реализовывать метод C<seek>.

=over 4

=item seek

  $input->seek($pos, $whence);

Возвращает 1 при успехе, иначе 0.

=back

См. документацию L<IO::Handle> для получения дальнейших подробностей о том,
как эти методы в точности должны работать.

=head3 Поток Ошибок

Поток ошибок в C<psgi.errors> - это L<IO::Handle>-подобный объект для печати
ошибок. Поток ошибок должен реализовывать метод C<print>.

Как и с входным потоком, встроенные файловые дескрипторы Perl или
основанные на L<IO::Handle> объекты должны работать "как есть" в PSGI сервере.
Разработчикам приложений B<НЕ СЛЕДУЕТ> проверять тип или класс потока. Вместо
этого, им B<СЛЕДУЕТ> просто вызывать метод объекта C<print>.

=over 4

=item print

  $errors->print($error);

Возвращает true при успехе.

=back

=head3 Ответ

Приложения B<ДОЛЖНЫ> возвращать ответ или как ссылку на трёхэлементный массив,
или code reference на отложенный/потоковый ответ.

Массив, на который возвращается ссылка, состоит из следующих элементов:

=head4 Статус

Код статуса HTTP. Это B<ДОЛЖНО> быть целое число, большее или равное 100, и
ему B<СЛЕДУЕТ> быть кодом статуса HTTP, как описано в L<RFC
2616|http://www.w3.org/Protocols/rfc2616>.

=head4 Заголовки

Заголовки B<ДОЛЖНЫ> быть ссылкой на массив (B<не> ссылкой на хеш) или парами
ключ/значение. Это значит, они B<ДОЛЖНЫ> содержать чётное число элементов.

Заголовок B<НЕ ДОЛЖЕН> содержать ключ с названием C<Status>, а также
какие-либо ключи с C<:> или переводом строки в названии. Он B<НЕ ДОЛЖЕН>
содержать какие-либо ключи, оканчивающиеся на C<-> или C<_>.

Все ключи B<ДОЛЖНЫ> состоять только из букв, цифр, C<_> или C<->. Все ключи
B<ДОЛЖНЫ> начинаться с буквы. Значение заголовка B<ДОЛЖНО> быть скалярной
строкой, и должно быть определено. Значение строки B<НЕ ДОЛЖНО> содержать
символы ниже восьмеричного 037, т.е. chr(31).

Если одни и те же имена ключей появляются несколько раз в ссылке на массив,
эти строки заголовков B<ДОЛЖНЫ> отправляться к клиенту по отдельности
(например, несколько строк C<Set-Cookie>).

=head4 Content-Type

B<ДОЛЖЕН> присутствовать C<Content-Type> за исключением случая, когда
C<Status> равен 1xx, 204 или 304 - в этом случае B<НЕ ДОЛЖНО> быть указано
типа содержимого.

=head4 Content-Length

Заголовок C<Content-Length> B<НЕ ДОЛЖЕН> быть указан, когда C<Status> равен
1xx, 204 или 304.

Если Status не 1xx, 204 или 304 и заголовок C<Content-Length> отсутствует,
PSGI сервер B<МОЖЕТ> вычислить длину содержимого, глядя на Тело ответа. Это
значение может затем быть добавлено к списку заголовков, возвращенному
приложением.

=head4 Тело ответа

Тело ответа B<ДОЛЖНО> быть возвращено из приложения или как ссылка на массив,
или как дескриптор, содержащий тело ответа в виде байтовой строки. Тело
B<ДОЛЖНО> быть закодировано в соответствующей кодировке и B<НЕ ДОЛЖНО>
содержать "широких" символов (> 255).

=over 4

=item *

Если тело ответа - ссылка на массив, ожидается, что оно содержит массив строк,
из которых состоит само тело.

  my $body = [ "Hello\n", "World\n" ];

Обратите внимание, что элементы в массиве B<НЕОБЯЗАТЕЛЬНО> должны оканчиваться
переводом строки. Серверу B<СЛЕДУЕТ> отправлять каждый элемент "как есть"
клиенту, и B<НЕ СЛЕДУЕТ> заботиться о том, оканчиваются ли строки переводом
строки, или нет.

Ссылка на массив с единственным значением валидна. Так, C<[ $html ]> -
валидное тело ответа.

=item *

Тело ответа может вместо этого быть дескриптором - или встроенным файловым
дескриптором Perl, или L<IO::Handle>-подобным объектом.

  open my $body, "</path/to/file";
  open my $body, "<:via(SomePerlIO)", ...;
  my $body = IO::File->new("/path/to/file");

  # mock class that implements getline() and close()
  my $body = SomeClass->new();

Серверам B<НЕ СЛЕДУЕТ> проверять тип или класс тела ответа. Вместо этого им
следует просто вызывать C<getline> для перебора строк тела, и вызвать C<close>
по завершению.

Серверы B<МОГУТ> проверять, является ли тело реальным файловым дескриптором,
используя C<fileno> и C<Scalar::Util::reftype>. Если тело - реальный
дескриптор файла, сервер B<МОЖЕТ> оптимизировать выполнение, используя методы
вроде I<sendfile(2)>.

Объект тела также B<МОЖЕТ> иметь метод C<path>. Ожидается, что этот метод
возвратит путь к файлу, доступному серверу. Это позволяет серверу использовать
данную информацию вместо номера файлового дескриптора для отдачи файла.

Серверам B<СЛЕДУЕТ> устанавливать специальную переменную C<$/> для получения
размера буфера при чтении содержимого из C<$body> при помощи метода
C<getline>. Это делается путём присваивания C<$/> ссылки на целое число (C<$/
= \8192>).

Если файловый дескриптор тела ответа - встроенный файловый дескриптор Perl или
объект L<IO::Handle>, они обратят внимание на значение. Похожим образом
объект, предоставляющий аналогичный API, B<МОЖЕТ> также обратить внимание на
эту специальную переменную, но от него не требуется делать это.

=back

=head2 Отложенный Ответ и Потоковое Тело ответа

The PSGI interface allows applications and servers to provide a
callback-style response instead of the three-element array
reference. This allows for a delayed response and a streaming body
(server push).

Интерфейс PSGI позволяет приложениям и серверам предоставлять ответ в виде
вызываемой функции вместо ссылки на трёхэлементный массив. Это позволяет
возвращать отложенные ответы и потоковое тело ответа (server push).

PSGI серверам B<СЛЕДУЕТ> реализовывать этот интерфейс, и переменная
C<psgi.streaming> должна быть равна true в таких серверах.

Чтобы разрешить отложенный ответ, приложению B<СЛЕДУЕТ> возвращать вызываемую
функцию в качестве ответа. Приложение B<МОЖЕТ> проверять, что переменная
C<psgi.streaming> равна true, и возвращаться к непосредственному ответу, если
это не так.

This callback will be called with I<another> subroutine reference (referred to
as the I<responder> from now on) as its only argument. The I<responder>
should in turn be called with the standard three element array reference
response. This is best illustrated with an example:

Эта вызываемая функция будет вызвана со ссылкой наI<другую> функцию (далее
называемую I<ответчик>) в качестве единственного аргумента. I<Ответчик> должен
в свою очередь быть вызван со стандартным ответом в виде ссылки на
трёхэлементный массив. Наилучшим образом это иллюстрируется следующим
примером:

  my $app = sub {
      my $env = shift;

      # Откладывает ответ до тех пор, пока он не получит ответ от сети
      return sub {
          my $responder = shift;

          fetch_content_from_server(sub {
              my $content = shift;
              $responder->([ 200, $headers, [ $content ] ]);
          });
      };
  };

Приложение B<МОЖЕТ> опустить третий элемент (тело) при вызове I<ответчика>.
Если тело пропущено, I<ответчик> B<ДОЛЖЕН> возвратить I<ещё один> объект,
в котором реализованы методы C<write> и C<close>. Снова проиллюстрируем это
примером.

  my $app = sub {
      my $env = shift;

      # immediately starts the response and stream the content
      return sub {
          my $responder = shift;
          my $writer = $responder->(
              [ 200, [ 'Content-Type', 'application/json' ]]);

          wait_for_events(sub {
              my $new_event = shift;
              if ($new_event) {
                  $writer->write($new_event->as_json . "\n");
              } else {
                  $writer->close;
              }
          });
      };
  };

Этот отложенный ответ и потоковый API полезны, если вы хотите реализовать
основанную на неблокирующем вводе/выводе отдачу потока сервером или технологию
long-poll Comet push, но могут также быть использованы для реализации
небуферизованной записи в блокирующем сервере.

=head2 Middleware

Компонент I<middleware> берёт другое PSGI приложение и запускает его. С точки
зрения сервера компонент middleware - PSGI приложение. С точки зрения
приложения, запускаемого компонентом middleware, middleware - это сервер. Как
правило это делается для реализации некоего способа предварительной обработки
хеша PSGI окружения или пост-обработки запроса.

Вот простой пример, который добавляет специальный HTTP заголовок
I<X-PSGI-Used> к любому приложению PSGI.

  # $app - простое приложение PSGI
  my $app = sub {
      my $env = shift;
      return [ '200',
               [ 'Content-Type' => 'text/plain' ],
               [ "Hello World" ] ];
  };

  # $xheader - часть middleware, которая обёрнута вокруг $app
  my $xheader = sub {
      my $env = shift;
      my $res = $app->($env);
      push @{$res->[1]}, 'X-PSGI-Used' => 1;
      return $res;
  };

Middleware B<ДОЛЖНО> вести себя в точности так же, как приложение PSGI с точки
зрения сервера. Middleware B<МОЖЕТ> решить не поддерживать потоковый
интерфейс, обсуждавшийся ранее, но ему B<СЛЕДУЕТ> пропускать беспрепятственно
те типы ответов, которые оно не понимает.

=head1 CHANGELOGS

1.1: 2010.02.xx

=over 4

=item *

Added optional PSGI keys as extensions: C<psgix.logger> and C<psgix.session>.

=item *

C<psgi.streaming> B<SHOULD> be implemented by PSGI servers, rather than B<MAY>.

=item *

PSGI keys C<psgi.run_once>, C<psgi.nonblocking> and C<psgi.streaming>
B<MUST> be set by PSGI servers.

=item *

Removed C<poll_cb> from writer methods.

=back

=head1 ACKNOWLEDGEMENTS

Some parts of this specification are adopted from the following specifications.

=over 4

=item *

PEP333 Python Web Server Gateway Interface L<http://www.python.org/dev/peps/pep-0333>

=item *

Rack L<http://rack.rubyforge.org/doc/SPEC.html>

=item *

JSGI Specification L<http://jackjs.org/jsgi-spec.html>

=back

I'd like to thank authors of these great documents.

=head1 AUTHOR

Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>

=head1 CONTRIBUTORS

The following people have contributed to the PSGI specification and
Plack implementation by commiting their code, sending patches,
reporting bugs, asking questions, suggesting useful advices,
nitpicking, chatting on IRC or commenting on my blog (in no particular
order):

  Tokuhiro Matsuno
  Kazuhiro Osawa
  Yuval Kogman
  Kazuho Oku
  Alexis Sukrieh
  Takatoshi Kitano
  Stevan Little
  Daisuke Murase
  mala
  Pedro Melo
  Jesse Luehrs
  John Beppu
  Shawn M Moore
  Mark Stosberg
  Matt S Trout
  Jesse Vincent
  Chia-liang Kao
  Dave Rolsky
  Hans Dieter Pearcey
  Randy J Ray
  Benjamin Trott
  Max Maischein
  Slaven Rezić
  Marcel Grünauer
  Masayoshi Sekimura
  Brock Wilcox
  Piers Cawley
  Daisuke Maki
  Kang-min Liu
  Yasuhiro Matsumoto
  Ash Berlin
  Artur Bergman
  Simon Cozens
  Scott McWhirter
  Jiro Nishiguchi
  Masahiro Chiba
  Patrick Donelan
  Paul Driver
  Florian Ragwitz

=head1 COPYRIGHT AND LICENSE

Copyright Tatsuhiko Miyagawa, 2009-2011.

This document is licensed under the Creative Commons license by-sa.

=cut


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.