|
NAMEFeersum - A PSGI engine for Perl based on EV/libevSYNOPSISuse Feersum; my $ngn = Feersum->endjinn; # singleton $ngn->use_socket($io_socket); # register a PSGI handler $ngn->psgi_request_handler(sub { my $env = shift; return [200, ['Content-Type'=>'text/plain'], ["You win one cryptosphere!\n"]]; }); # register a Feersum handler: $ngn->request_handler(sub { my $req = shift; my $t; $t = EV::timer 2, 0, sub { $req->send_response( 200, ['Content-Type' => 'text/plain'], \"You win one cryptosphere!\n" ); undef $t; }; }); DESCRIPTIONFeersum is an HTTP server built on EV. It fully supports the PSGI 1.03 spec including the "psgi.streaming" interface and is compatible with Plack. PSGI 1.1, which has yet to be published formally, is also supported. Feersum also has its own "native" interface which is similar in a lot of ways to PSGI, but is not compatible with PSGI or PSGI middleware.Feersum uses a single-threaded, event-based programming architecture to scale and can handle many concurrent connections efficiently in both CPU and RAM. It skips doing a lot of sanity checking with the assumption that a "front-end" HTTP/HTTPS server is placed between it and the Internet. How It WorksAll of the request-parsing and I/O marshalling is done using C or XS code. HTTP parsing is done by picohttpparser, which is the core of HTTP::Parser::XS. The network I/O is done via the libev library. This is made possible by "EV::MakeMaker", which allows extension writers to link against the same libev that "EV" is using. This means that one can write an evented app using "EV" or AnyEvent from Perl that completely co-operates with the server's event loop.Since the Perl "app" (handler) is executed in the same thread as the event loop, one need to be careful to not block this thread. Standard techniques include using AnyEvent or EV idle and timer watchers, using Coro to multitask, and using sub-processes to do heavy lifting (e.g. AnyEvent::Worker and AnyEvent::DBI). Feersum also attempts to do as little copying of data as possible. Feersum uses the low-level "writev" system call to avoid having to copy data into a buffer. For response data, references to scalars are kept in order to avoid copying the string values (once the data is written to the socket, the reference is dropped and the data is garbage collected). A trivial hello-world handler can process in excess of 5000 requests per second on a 4-core Intel(R) Xeon(R) E5335 @ 2.00GHz using TCPv4 on the loopback interface, OS Ubuntu 6.06LTS, Perl 5.8.7. Your mileage will likely vary. For even faster results, Feersum can support very simple pre-forking (See feersum, Feersum::Runner or Plack::Handler::Feersum for details). INTERFACEThere are two handler interfaces for Feersum: The PSGI handler interface and the "Feersum-native" handler interface. The PSGI handler interface is fully PSGI 1.03 compatible and supports "psgi.streaming". The "psgix.input.buffered" and "psgix.io" features of PSGI 1.1 are also supported. The Feersum-native handler interface is "inspired by" PSGI, but does some things differently for speed.Feersum will use "Transfer-Encoding: chunked" for HTTP/1.1 clients and "Connection: close" streaming as a fallback. Technically "Connection: close" streaming isn't part of the HTTP/1.0 or 1.1 spec, but many browsers and agents support it anyway. Currently POST/PUT does not stream input, but read() can be called on "psgi.input" to get the body (which has been buffered up before the request callback is called and therefore will never block). Likely "read()" will change to raise EAGAIN responses and allow for a callback to be registered on the arrival of more data. (The "psgix.input.buffered" env var is set to reflect this). PSGI interfaceFeersum fully supports the PSGI 1.03 spec including "psgi.streaming".See also Plack::Handler::Feersum, which provides a way to use Feersum with plackup and Plack::Runner. Call "psgi_request_handler($app)" to register $app as a PSGI handler. my $app = do $filename; Feersum->endjinn->psgi_request_handler($app); The env hash passed in will always have the following keys in addition to dynamic ones: psgi.version => [1,0], psgi.nonblocking => 1, psgi.multithread => '', # i.e. false psgi.multiprocess => '', psgi.streaming => 1, psgi.errors => \*STDERR, SCRIPT_NAME => "", Feersum adds these extensions (see below for info) psgix.input.buffered => 1, psgix.output.buffered => 1, psgix.body.scalar_refs => 1, psgix.output.guard => 1, psgix.io => \$magical_io_socket, Note that SCRIPT_NAME is always blank (but defined). PATH_INFO will contain the path part of the requested URI. For requests with a body (e.g. POST) "psgi.input" will contain a valid file-handle. Feersum currently passes "undef" for psgi.input when there is no body to avoid unnecessary work. my $r = delete $env->{'psgi.input'}; $r->read($body, $env->{CONTENT_LENGTH}); # optional: choose to stop receiving further input, discard buffers: $r->close(); The "psgi.streaming" interface is fully supported, including the writer-object "poll_cb" callback feature defined in PSGI 1.03. Note that poll_cb is removed from the preliminary PSGI 1.1 spec. Feersum calls the poll_cb callback after all data has been flushed out and the socket is write-ready. The data is buffered until the callback returns at which point it will be immediately flushed to the socket. my $app = sub { my $env = shift; return sub { my $respond = shift; my $w = $respond->([ 200, ['Content-Type' => 'application/json'] ]); my $n = 0; $w->poll_cb(sub { $_[0]->write(get_next_chunk()); # will also unset the poll_cb: $_[0]->close if ($n++ >= 100); }); }; }; Note that "$w->close()" will be called when the last reference to the writer is dropped. PSGI extensions
The Feersum-native interfaceThe Feersum-native interface is inspired by PSGI, but is inherently incompatible with it. Apps written against this API will not work as a PSGI app.This interface may have removals and is not stable until Feersum reaches version 1.0, at which point the interface API will become stable and will only change for bug fixes or new additions. The "stable" and will retain backwards compatibility until at least the next major release. The main entry point is a sub-ref passed to "request_handler". This sub is passed a reference to an object that represents an HTTP connection. Currently the request_handler is called during the "check" and "idle" phases of the EV event loop. The handler is always called after request headers have been read. Currently, the handler will only be called after a full request entity has been received for POST/PUT/etc. The simplest way to send a response is to use "send_response": my $req = shift; $req->send_response(200, \@headers, ["body ", \"parts"]); Or, if the app has everything packed into a single scalar already, just pass it in by reference. my $req = shift; $req->send_response(200, \@headers, \"whole body"); Both of the above will generate "Content-Length" header (replacing any that were pre-defined in @headers). An environment hash is easy to obtain, but is a method call instead of a parameter to the callback. (In PSGI, there is no $req object; the env hash is the first parameter to the callback). The hash contains the same items as it would for a PSGI handler (see above for those). my $req = shift; my $env = $req->env(); To read input from a POST/PUT, use the "psgi.input" item of the env hash. if ($req->{REQUEST_METHOD} eq 'POST') { my $body = ''; my $r = delete $env->{'psgi.input'}; $r->read($body, $env->{CONTENT_LENGTH}); # optional: $r->close(); } Starting a response in stream mode enables the "write()" method (which really acts more like a buffered 'print'). Calls to "write()" will never block. my $req = shift; my $w = $req->start_streaming(200, \@headers); $w->write(\"this is a reference to some shared chunk\n"); $w->write("regular scalars are OK too\n"); $w->close(); # close off the stream The writer object supports "poll_cb" as also specified in PSGI 1.03. Feersum will call the callback only when all data has been flushed out at the socket level. Use "close()" or unset the handler ("$w->poll_cb(undef)") to stop the callback from getting called. my $req = shift; my $w = $req->start_streaming( "200 OK", ['Content-Type' => 'application/json']); my $n = 0; $w->poll_cb(sub { # $_[0] is a copy of $w so a closure doesn't need to be made $_[0]->write(get_next_chunk()); $_[0]->close if ($n++ >= 100); }); Note that "$w->close()" will be called when the last reference to the writer is dropped. METHODSThese are methods on the global Feersum singleton.
GRITTY DETAILSCompile Time OptionsThere are a number of constants at the top of Feersum.xs. If you change any of these, be sure to note that in any bug reports.
BUGSPlease report bugs using http://github.com/stash/Feersum/issues/Keep-alive is ignored completely. Currently there's no way to limit the request entity length of a streaming POST/PUT/etc. This could lead to a DoS attack on a Feersum server. Suggested remedy is to only run Feersum behind some other web server and to use that to limit the entity size. Although not explicitly a bug, the following may cause undesirable behavior. Feersum will have set SIGPIPE to be ignored by the time your handler gets called. If your handler needs to detect SIGPIPE, be sure to do a "local $SIG{PIPE} = ..." (perlipc) to make it active just during the necessary scope. SEE ALSOhttp://en.wikipedia.org/wiki/Feersum_EndjinnFeersum Git: "http://github.com/stash/Feersum" "git://github.com/stash/Feersum.git" picohttpparser Git: "http://github.com/kazuho/picohttpparser" "git://github.com/kazuho/picohttpparser.git" AUTHORJeremy Stashewsky, "stash@cpan.org"THANKSTatsuhiko Miyagawa for PSGI and Plack.Marc Lehmann for EV and AnyEvent (not to mention JSON::XS and Coro). Kazuho Oku for picohttpparser. Luke Closs (lukec), Scott McWhirter (konobi), socialtexters and van.pm for initial feedback and ideas. Audrey Tang and Graham Termarsch for XS advice. Hans Dieter Pearcey (confound) for docs and packaging guidance. For bug reports: Chia-liang Kao (clkao), Lee Aylward (leedo) Audrey Tang (au) for flash socket policy support. COPYRIGHT AND LICENSECopyright (C) 2011 by Jeremy StashewskyPortions Copyright (C) 2010 Socialtext Inc. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.7 or, at your option, any later version of Perl 5 you may have available. picohttpparser is Copyright 2009 Kazuho Oku. It is released under the same terms as Perl itself.
Visit the GSP FreeBSD Man Page Interface. |