PSGI
view release on metacpan or search on metacpan
- Fixed a dumb mistake about allowed characters in header values
- Updated FAQ document
- psgi.input MUST have seek() only if psgix.input.buffered is true
1.09_1 Mon Mar 28 11:35:44 PDT 2011
- 1.1 beta
- Upped psgi.version to be [1,1]
- Lots of grammar and style fixes
- Removed poll_cb from writer spec
- Streaming interface now SHOULD be implemented, rather than MAY
- Promoted psgi.streaming, nonblocking and run_once keys to be MUST
- Added psgix.logger and psgix.session extensions
- Updated FAQ
1.03 Tue Oct 27 13:44:01 PDT 2009
- Added an optional callback interface to allow delayed response and streaming
1.02 Tue Oct 13 01:57:28 PDT 2009
- No spec changes. Just to let PAUSE index this stuff.
1.01 Tue Oct 13 01:17:28 PDT 2009
- PAUSE distribution issue
the life of its containing process. Normally, this will only be true for a
server based on CGI (or something similar).
=item *
C<psgi.nonblocking>: A boolean which is true if the server is calling the
application in an non-blocking event loop.
=item *
C<psgi.streaming>: A boolean which is true if the server supports callback
style delayed response and streaming writer object.
=back
The server or the application can store its own data in the
environment as well. These keys MUST contain at least one dot, and
SHOULD be prefixed uniquely.
The C<psgi.> prefix is reserved for use with the PSGI core
specification, and C<psgix.> prefix is reserved for officially blessed
extensions. These prefixes B<MUST NOT> be used by other servers or
$errors->print($error);
Returns true if successful.
=back
=head3 The Response
Applications MUST return a response as either a three element array
reference, or a code reference for a delayed/streaming response.
The response array reference consists of the following elements:
=head4 Status
An HTTP status code. This MUST be an integer greater than or equal to 100,
and SHOULD be an HTTP status code as documented in L<RFC
2616|http://www.w3.org/Protocols/rfc2616>.
=head4 Headers
If the body filehandle is a Perl built-in filehandle L<IO::Handle> object,
they will respect this value. Similarly, an object which provides the same API
MAY also respect this special variable, but are not required to do so.
=back
=head2 Delayed Response and Streaming Body
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).
This interface SHOULD be implemented by PSGI servers, and
C<psgi.streaming> environment MUST be set to true in such servers.
To enable a delayed response, the application SHOULD return a
callback as its response. An application MAY check if the
C<psgi.streaming> environment is true and falls back to the direct
response if it isn't.
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:
my $app = sub {
my $env = shift;
my $new_event = shift;
if ($new_event) {
$writer->write($new_event->as_json . "\n");
} else {
$writer->close;
}
});
};
};
This delayed response and streaming API is useful if you want to
implement a non-blocking I/O based server streaming or long-poll Comet
push technology, but could also be used to implement unbuffered writes
in a blocking server.
=head2 Middleware
A I<middleware> component takes another PSGI application and runs it. From the
perspective of a server, a middleware component is a PSGI application. From
the perspective of the application being run by the middleware component, the
middleware is the server. Generally, this will be done in order to implement
some sort of pre-processing on the PSGI environment hash or post-processing on
# $xheader is a piece of middleware that wraps $app
my $xheader = sub {
my $env = shift;
my $res = $app->($env);
push @{$res->[1]}, 'X-PSGI-Used' => 1;
return $res;
};
Middleware MUST behave exactly like a PSGI application from the perspective
of a server. Middleware MAY decide not to support the streaming interface
discussed earlier, but SHOULD pass through the response types that it doesn't
understand.
=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> SHOULD be implemented by PSGI servers, rather than B<MAY>.
=item *
PSGI keys C<psgi.run_once>, C<psgi.nonblocking> and C<psgi.streaming>
MUST be set by PSGI servers.
=item *
Removed C<poll_cb> from writer methods.
=back
=head1 ACKNOWLEDGEMENTS
PSGI/FAQ.pod view on Meta::CPAN
work everywhere, but it's more like I<pull> than I<push>, and it's
hard to do non-blocking I/O unless you use Coro.
If you want to do server push, where your application runs in an event
loop and push content body to the client as it's ready, you should
return a callback to delay the response.
# long-poll comet like a chat application
my $app = sub {
my $env = shift;
unless ($env->{'psgi.streaming'}) {
die "This application needs psgi.streaming support";
}
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> can be blocking or non-blocking: it's up to
you. Most of the case you want to run it non-blockingly and should use
event loops like L<AnyEvent>. You may also check C<psgi.nonblocking>
value to see that it's possible and fallback to a blocking call
otherwise.
Also, to stream the content body (like streaming messages over the
Flash socket or multipart XMLHTTPRequest):
my $app = sub {
my $env = shift;
unless ($env->{'psgi.streaming'}) {
die "This application needs psgi.streaming support";
}
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 Which framework should I use to do streaming though?
We have servers that support non-blocking (where C<psgi.nonblocking>
is set to true), but the problem is that framework side doesn't
necessarily support asynchronous event loop. For instance Catalyst has
C<write> method on the response object:
while ($cond) {
$c->res->write($some_stuff);
}
This should work with all servers with C<psgi.streaming> support even
if they are blocking, and it should be fine if they're running in
multiple processes (C<psgi.multiprocess> is true).
L<Catalyst::Engine::PSGI> also supports setting an IO::Handle-like
object that supports C<getline>, so using L<IO::Handle::Util>
my $io = io_from_getline sub {
return $data; # or undef when done()
};
$c->res->body($io);
And that works fine to do streaming, but it's blocking (I<pull>)
rather than asynchronous server push, so again you should be careful
not to run this application on non-blocking (and non-multiprocess)
server environments.
We expect that more web frameworks will appear that is focused on, or
existent frameworks will add support for, asynchronous and
non-blocking streaming interface.
=head3 Is psgi.streaming interface a requirement for the servers?
It is specified as B<SHOULD>, so unless there is a strong reason not
to implement the interface, all servers are encouraged to implement
this interface.
However, if you implement a PSGI server using an Perl XS interface for
the ultimate performance or integration with web servers like Apache
or nginx, or implement a sandbox like environment (like Google
AppEngine or Heroku) or distributed platform using tools like Gearman,
you might not want to implement this interface.
That's fine, and in that case applications relying on the streaming
interface can still use L<Plack::Middleware::BufferedStreaming> to
fallback to the buffered write on unsupported servers.
=head3 Why CGI-style environment variables instead of HTTP headers as a hash?
Most existing web application frameworks already have code or a handler
to run under the CGI environment. Using CGI-style hash keys instead of
HTTP headers makes it trivial for the framework developers to implement
an adapter to support PSGI. For instance, L<Catalyst::Engine::PSGI> is
only a few dozens lines different from L<Catalyst::Engine::CGI> and was
( run in 0.352 second using v1.01-cache-2.11-cpan-4d50c553e7e )