POD2-RU-PSGI

 view release on metacpan or  search on metacpan

lib/POD2/RU/PSGI/FAQ.pod  view on Meta::CPAN

объекты в IO::Handle-подобные.

=head3 Как серверу следует определять возможность переключиться на использование
sendfile(2)?

Для начала, приложению СЛЕДУЕТ всегда создавать IO::Handle-подобный объект
(или массив частей), который отвечает на C<getline> и C<close> как тело ответа.
Это гарантированно работает с любыми серверами.

Опционально, если сервер написан на Perl или может передать номер файлового
дескриптора на C-сторону программы, чтобы обслужить запрос на этот файл, то
сервер МОЖЕТ проверить, является ли тело реальным файловым дескриптором
(возможно, используя функцию C<is_real_fh> из L<Plack::Util>), затем получить
файловый дескриптор при помощи C<fileno> и вызвать sendfile(2) или
эквивалентную функцию передачи данных без затрат на копирование, использующую
его.

В противном случае, если сервер не может отправить файл используя файловый
дескриптор, но нуждается в локальном пути к файлу (как mod_perl или nginx),
приложение может вернуть IO::Handle-подобный объект, который также отвечает на
метод C<path>. Этот тип IO-подобных объектов может легко быть создан при
использовании L<IO::File::WithPath>, L<IO::Handle::Util> или функции
C<set_io_path> из модуля L<Plack::Util>.

Middleware также может проверять, есть ли у body метод C<path>, и делать с ним
что-нибудь интересное - вроде установки заголовка C<X-Senfile>.

Подводя итог:

=over 4

=item *

При отдаче статических файлов приложению следует всегда возвращать реальный
дескриптор или объект IO::Handle. Это должно работать везде, и может быть
оптимизировано в некоторых средах.

=item *

Приложения могут также создавать IO::Handle-подобный объект с дополнительным
методом C<path>, после чего это также должно работать везде, и может быть
оптимизировано даже в большем количестве различных сред.

=back

=head3 Что если я хочу стримить контент или использовать long-polling Comet технологию?

Самый простой способ для реализации технологии push со стороны сервера это
вернуть из приложения IO::Handle-подобный объект как body, который
реализует метод C<getline> возвращающий push данные. Это гарантированно везде
работает, но это больше pull, чем push, и сложно делать неблокируемый
ввод/выдать, если только не используется Coro.

Если вы хотите делать push со стороны сервера, где приложение работает в цикле
обработки событий (event loop) и при готовности выдает данные клиенту, следует
вернуть callback для отложенного ответа.

  # long-poll comet подобное чат-приложение
  my $app = sub {
      my $env = shift;
      unless ($env->{'psgi.streaming'}) {
          die "Это приложение требует поддержки psgi.streaming";
      }
      return sub {
          my $respond = shift;
          wait_for_new_message(sub {
              my $message = shift;
              my $body = [ $message->to_json ];
              $respond->([200, ['Content-Type', 'application/json'], $body]);
          });
      };
  };

C<wait_for_new_message> может быть блокирующим и неблокирующим: выбор за
разработчиком. В большинстве случаев требуется неблокирующий режим, тогда
следует использовать цикл обработки событий (event loop), как L<AnyEvent>. Также
можно проверить значение C<psgi.nonblocking>, и, если это не поддерживается,
использовать блокирующий вызов.

Для того, чтобы стримить контент (для стриминга сообщений через Flash сокет или
multipart XMLHTTPRequest):

  my $app = sub {
      my $env = shift;
      unless ($env->{'psgi.streaming'}) {
          die "Это приложение требует поддержки psgi.streaming";
      }
      return sub {
          my $respond = shift;
          my $writer = $respond->([200, ['Content-Type', 'text/plain']]);
          wait_for_new_message(sub {
              my $message = shift;
              if ($message) {
                  $writer->write($message->to_json);
              } else {
                  $writer->close;
              }
          });
      };
  };

=head3 Какой же фреймворк использовать для стриминга?

Есть серверы, который поддерживают неблокирующий режим (где C<psgi.nonblocking>
имеет true значение), но проблема в том, что серверный фреймворк совсем не
обязательно поддерживает асинхронный цикл обработки событий. Например, в
Catalyst есть метод C<write> у объекта ответа:

  while ($cond) {
      $c->res->write($some_stuff);
  }

Это должно сработать с серверами поддерживающими C<psgi.streaming> даже если они
блокирующие, и если они работают в режиме нескольких процессов
(C<psgi.multiprocess> в значении true).

L<Catalyst::Engine::PSGI> также поддерживает установку IO::Handle-подобного
объекта, который имеет C<getline> метод, пример с L<IO::Handle::Util>

  my $io = io_from_getline sub {
       return $data; # or undef when done()
  };
  $c->res->body($io);

И это работает вполне нормально для стриминга, но это скорее блокирущая операция
(I<pull>), а не асинхронный push со стороны серверы, поэтому, опять же, стоит
быть осторожным при запуске приложения в неблокирующем (и не многопроцессорном)
серверном окружении.

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

=head3 Является ли интерфейс psgi.streaming необходимым для серверов?

Он указан в спецификации как B<SHOULD>, поэтому, если нет серьёзной причины
отказаться от реализации интерфейса, всем серверам рекомендуется реализовывать
этот интерфейс.

Тем не менее, если вы реализовываете PSGI сервер, используя Perl XS интерфейс
для достижения максимальной производительности или интеграции с веб-серверами
вроде Apache или nginx, или создаёте окружение типа "песочницы" (наподобие
Google AppEngine или Heroku), или распределённую платформу, используя утилиты
вроде Gearman, вы можете не захотеть реализовывать этот интерфейс.

Это прекрасно, и в этом случае приложения, основанные на потоковом интерфейсе,
всё ещё могут использовать L<Plack::Middleware::BufferedStreaming> в качастве
запасного варианта для буферизованной записи на неподдерживаемых серверах.

=head3 Почему переменные окружения в стиле CGI вместо хеша с HTTP заголовками?

Большинство существующих фреймворков для веб-приложений уже содержат код или
обработчик для запуска в CGI-окружении. Использование CGI-подобных ключей хеша
вместо HTTP заголовков делает тривиальной для разработчиков фреймворков задачу
реализации адаптера для поддержки PSGI. Например, L<Catalyst::Engine::PSGI>
всего лишь на несколько дюжин строк отличается от L<Catalyst::Engine::CGI> и
был написан менее чем за час.

=head3 Почему PATH_INFO URI декодирован?

Для совместимости со спецификацией CGI (RFC 3875) и большинством реализаций
веб-серверов (наподобие Apache и lighttpd).

Я понимаю, может быть неудобно, что вы не можете различить C<foo%2fbar> от
C<foo/bar> в окончании пути, но спецификация CGI ясно говорит, что
C<PATH_INFO> следует декодировать помощи сервера, и что веб-серверы могут
отклонять запросы, содержащие C<%2f> (так как такие запросы могут потерять
информацию в PATH_INFO). Оставив эти зарезервированные символы
недекодированными (частичное декодирование) можно ухудшить положение, так как
после этого вы не сможете сказать C<foo%2fbar> из C<foo%252fbar>, и возможна
дырка в безопасности при двойном кодировании или декодировании.

Для разработчиков веб-фреймворков, которым нужен больший контроль над
актуальным необработанным URI (таких как L<Catalyst>), мы сделали
ключ хеша окружения C<REQUEST_URI> ОБЯЗАТЕЛЬНЫМ. Серверам следует
устанавливать недекодированный (неразобранный) оригинальный URI (содержащий
строку запроса) в значение этого ключа. Обратите внимание, что C<REQUEST_URI>
совершенно необработан, даже если закодированные сущности URI-безопасны.

Для сравнения, WSGI (PEP-333) определяет C<SCRIPT_NAME> и C<PATH_INFO> как
декодированные, и Rack оставляет это зависеть от реализации, в то время как
I<исправление> большей части PATH_INFO осталось закодированным в реализациях
Ruby веб-серверов.

L<http://www.python.org/dev/peps/pep-0333/#url-reconstruction>
L<http://groups.google.com/group/rack-devel/browse_thread/thread/ddf4622e69bea53f>

=head1 СМ. ТАКЖЕ

WSGI FAQ ясно отвечает на множество вопросов о том, как были приняты некоторые
решения в устройстве API, некоторые из которых можно прямо применить к PSGI.

L<http://www.python.org/dev/peps/pep-0333/#questions-and-answers>



( run in 1.707 second using v1.01-cache-2.11-cpan-39bf76dae61 )