The PSGI specification defines the HTTP response as consisting of three parts, the third of which may be either an array reference or a filehandle. The filehandle may be:
An IO::Handle-like object or a built-in filehandle.
And the spec goes on to say:
Servers MAY check if the body is a real filehandle using fileno and Scalar::Util::reftype and if it's a real filehandle that has a file descriptor, it MAY optimize the file serving using techniques like sendfile(2).
Now I've cobbled together a command-line example using plackup
(Plack version 0.9978), and it appears that checking if the body is a real filehandle results in a fatal error:
Can't locate object method "FILENO" via package "IO::Scalar" at /usr/lib/perl5/5.10/i686-cygwin/IO/Handle.pm line 390
Here's the command-line example:
plackup -MData::Dumper -MIO::Scalar -e \
'sub { $env=shift; return [200, [], IO::Scalar->new(\Dumper $env) ] }'
Of course I could just not use a filehandle:
plackup --port 9999 -MData::Dumper -e \
'sub { $env=shift; return [200, [], [Dumper $env] ] }'
But I'm interested in what works and what doesn't. So shouldn't Plack exercise more caution when calling FILENO
on the handle so it wouldn't run into an exception?
And to add another one:
plackup --port 9999 -MData::Dumper -e \
'sub{$env=shift; $s=Dumper $env; open $fh,q(<),\$s or die; return [200,[],$fh ]}'
Looks like the filehandle isn't recognized as such. The error message is:
body should be an array ref or filehandle at /usr/lib/perl5/site_perl/5.10/Plack/Middleware/StackTrace.pm line 35
Update:
As ysth stated in his answer, the following will work (at least on 5.10.1 on Cygwin):
plackup -p 9999 -MData::Dumper -MIO::String -e \
'sub { return [200, [], IO::String->new(\Dumper shift) ] }'
But clearly, there is an issue someplace as can be seen from the failing examples, and it will be reported once I've made up my mind what it actually is.
This appears to be a bug in Plack. It tries to figure out if it has a real filehandle, via fileno, and if not it will only accept objects with a getline
method. This misses out on both tied filehandles without FILENO
defined (valid, if impolite) and in memory filehandles which do not have a valid fileno nor are they blessed objects. You can see it in the logic in Plack::Middleware::Lint->validate_res
and Plack::Util->is_real_fh
.
I'd report it to Plack as a bug.
Meanwhile, you can work around the problem in IO::Scalar by defining IO::Scalar::FILENO to return undef.
sub IO::Scalar::FILENO { return }
This would be an improvement to IO::Scalar, but it hasn't been updated in six years so I wouldn't hold my breath.
To allow in memory filehandles, you can trick Plack by blessing the filehandle. Sometime between opening it and handing it off, do this:
bless $fh, "IO::Handle";
That's harmless as any filehandle will respond to IO::Handle methods anyway. But also please do report it as a bug.