NAME
Feersum::Connection::Handle - PSGI-style reader/writer objects.
SYNOPSIS
For read handles:
my $buf;
my $r = delete $env->{'psgi.input'};
$r->read($buf, 1, 1); # read the second byte of input without moving offset
$r->read($buf, $env->{CONTENT_LENGTH}); # append the whole input
my $line = $r->getline; # or <$r>: one record, honouring $/
$r->close(); # discards any un-read() data
# assuming the handle is "open":
$r->seek(2,SEEK_CUR); # returns 1, discards skipped bytes
$r->seek(-1,SEEK_CUR); # returns 0, can't seek back
$r->poll_cb(sub { .... });
For write handles:
$w->write("scalar");
$w->write(\"scalar ref");
$w->write_array(\@some_stuff);
$w->poll_cb(sub {
# use $_[0] instead of $w to avoid a closure
$_[0]->write(\"some data");
# can close() or unregister the poll_cb in here
$_[0]->close();
});
For both:
$h->response_guard(guard { response_is_complete() });
DESCRIPTION
See the PSGI spec for more information on how read/write handles are used (The Delayed Response and Streaming Body section has details on the writer).
METHODS
Reader methods
The reader is obtained via $env->{'psgi.input'}.
$r->read($buf, $len)-
Read up to
$lenmore bytes of the request body and append them to$buf; unlike Perl'sread,$bufis not truncated first, so a drain loop must use a fresh scalar (or clear it) each time round:my $body = ''; while ($r->read(my $chunk, 4096)) { $body .= $chunk }Never read into a buffer that outlives the request (a closure variable or a global): it accumulates, and on a keep-alive connection a later handler can see an earlier client's body in front of its own. The same goes for the reader itself: it wraps the connection, not one request, and kept past its response it reads the next request's body.
An optional third argument is an offset into the input to read from without advancing the current position. Returns the number of bytes read, or 0 at end of input.
Never blocks: the whole body is buffered before the handler runs (use
poll_cbfor incremental delivery).psgix.input.bufferedis still not set, as that flag also promises a rewindable handle and this one is forward-only; consumers such as Plack::Request buffer the body themselves when it is absent. $r->getline()-
Read one record from the input, following
readline/<$fh>semantics for$/: the default"\n"and any plain-string separator return one record including the separator (the final record may lack it),local $/ = undefslurps all remaining input,\$nreads fixed-size records, and""reads paragraphs. Returnsundefat end of input. Likeread(), it never blocks, and it stops at the end of the current request's body. $r->getlines()-
All remaining records as a list. Croaks in scalar context, as "getlines" in IO::Handle does.
The reader also overloads
<>in both contexts:my $line = <$r>; # one record my @lines = <$r>; # all remaining records (perl 5.18+) my $body = do { local $/; <$r> }; # slurpBefore perl 5.18 an overloaded
<>does not see list context andmy @lines = <$r>yields a single record; usegetlinesthere. $r->seek(...)-
Seeking is partially supported. Feersum discards skipped-over bytes to conserve memory. Note: SEEK_SET is treated the same as SEEK_CUR (always relative to the current position), since the underlying buffer is consumed as it is read and absolute positioning is not supported.
$r->seek(0,SEEK_CUR); # returns 1 $r->seek(-1,SEEK_CUR); # returns 0 $r->seek(-1,SEEK_SET); # returns 0 $r->seek(2,SEEK_CUR); # returns 1, discards 2 bytes $r->seek(42,SEEK_SET); # same as SEEK_CUR: discards 42 bytes $r->seek(-8,SEEK_END); # returns 1 if room, discards skipped bytes $r->close()-
Discards the remainder of the input buffer. It does not affect connection reuse: when the request body has already been received in full (the usual case) the connection stays eligible for keep-alive and an already-pipelined next request is still served.
$r->poll_cb(sub { .... })-
Register a callback to be called when more request body data is available. The callback receives the Reader object as its argument. On a normal HTTP/1.x or HTTP/2 request the handler runs only after the whole body has arrived, so the callback drains an already-complete buffer; it becomes a true incremental reader only after
io()/psgix.iotakes over the byte stream. See "psgi.input" in Feersum.
Writer methods.
The writer is obtained under PSGI by sending a code/headers pair to the "starter" callback. Under Feersum, calls to $req->start_streaming return one.
$w->write("scalar")-
Send the scalar as a chunk of the streaming response body. For HTTP/1.1 clients this uses
Transfer-Encoding: chunkedframing; for HTTP/1.0 (Connection: close) streaming the data is written without chunk framing.The calls to
$w->write()will never block and data is buffered until transmitted. This behaviour is indicated bypsgix.output.bufferedin the PSGI env hash (Twiggy supports this too, for example).Zero-copy: do not modify a scalar after passing it to
write(). Feersum keeps a reference to your scalar and points the pendingwritev()at its string buffer, and transmission happens after your handler (or callback) returns, so modifying the scalar in the meantime changes, or frees, the memory about to be sent:# WRONG - all five chunks arrive as "line 5" my $line; for my $i (1 .. 5) { $line = sprintf("line %d\n", $i); $w->write($line); } # RIGHT - a fresh scalar per write for my $i (1 .. 5) { $w->write(sprintf("line %d\n", $i)); }Expressions and literals are always safe; only scalars you still hold are at risk, and they are yours again once the data has gone out (from
$w->close()onwards, or inside apoll_cbthat fires after the buffer drained past them). The same applies to$w->write(\$scalar),$w->write_arrayand scalar-ref bodies passed to$req->send_response. $w->write(\"scalar ref")-
Works just like
write("scalar")above, including the zero-copy contract: the referenced scalar must not be modified until the response completes. This extension is indicated bypsgix.body.scalar_refsin the PSGI env hash. $w->write_array(\@array)-
Pass in an array-ref and it works much like the two
write()calls above, except it's way more efficient than callingwrite()over and over. Undefined elements of the array are ignored. The zero-copy contract applies to every element: neither the array nor the scalars in it may be modified until the response completes. $w->close()-
Close the HTTP response (which triggers the "T-E: chunked" terminating chunk to be sent). This method is implicitly called when the last reference to the writer is dropped.
$w->poll_cb(sub { .... })-
Register a callback to be called when the write buffer drains to (or below) the server's
wbuf_low_waterthreshold (default: 0, i.e. empty). Pass inundefto unset. The sub can callclose().The writer is passed as the first and only argument; use
$_[0]rather than closing over$wto avoid a circular reference. That argument is good for the duration of the call only and croaks "Handle is closed" if used later; the writerstart_streaminggave you stays valid.Each call is an invitation, not a busy-poll: a callback with nothing to say yet can return without writing, and the response is parked and re-invited on a backoff (roughly 1ms doubling to 100ms, reset by any write). Writing from another event source resumes the stream immediately, which is the recommended shape for relays and SSE. A zero-length
$w->write("")counts as engagement and requests an immediate re-invitation, so use it only when data really is imminent. A parked response has no write deadline; a disconnected client is normally noticed at the next paced retry (on TLS only for an abrupt close; a graceful close_notify is noticed on the next write). These rules apply identically to HTTP/1.x and HTTP/2.Something else must keep the writer alive: dropping the last reference closes it (and clears the poll callback), so a writer held only by a lexical in the handler is closed the moment the handler returns and sends an empty response. Stash it for as long as the response is meant to last.
$w->sendfile($fh [, $offset, $length])-
Send file contents using zero-copy sendfile(2) system call. Linux only. The file handle should be a regular file opened for reading. After calling sendfile(), call
close()on the writer.The response must carry an explicit
Content-Length; sendfile() croaks otherwise, since the file bytes go out verbatim and cannot be chunk-framed.Optional
$offset(bytes to skip from start, default 0) and$length(bytes to send, default remainder of file) allow sending a portion of the file.Note: Not supported for HTTP/2 responses. Use
write()instead.my $w = $req->start_streaming(200, [ 'Content-Type' => 'application/octet-stream', 'Content-Length' => -s $filename, ]); open my $fh, '<', $filename or die $!; $w->sendfile($fh); close $fh; $w->close();
Common methods.
Methods in common to both types of handles.
$h->return_from_psgix_io($io)-
Returns control of the socket back to Feersum after
psgix.iowas used. This is the PSGI-handle equivalent of$req->return_from_io($io)on the connection object. As there, the hand-back takes a private duplicate of the descriptor, so$iostays usable and is safe to release at any time. See "$req->return_from_io($io)" in Feersum::Connection. $h->response_guard($guard)-
Register a guard to be triggered when the response is completely sent and the socket is closed. A "guard" in this context is some object that will do something interesting in its DESTROY/DEMOLISH method. For example, Guard.
On a keepalive connection this is not when the response finishes. The guard is released at whichever comes first: the next
response_guard()call on the same connection, which replaces it, or the connection closing. So an app that registers one on every request sees request N's guard fire during request N+1's handler, and the last one only at close. Do not use a guard to release a per-request resource (a database handle, a rate-limit slot) unless keepalive is off, or it stays held for the life of the connection.The guard is *not* attached to this handle object; the guard is attached to the response.
psgix.output.guardis the PSGI-env extension that indicates this method. $h->fileno-
Returns the file descriptor number for this connection.
AUTHOR
Jeremy Stashewsky, stash@cpan.org
COPYRIGHT AND LICENSE
Copyright (C) 2010 by Jeremy Stashewsky & 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.14 or, at your option, any later version of Perl 5 you may have available.