[Catalyst] Plack::Middleware::ContentLength problem
neil.lunn
neil at mylunn.id.au
Sat Dec 21 04:34:24 GMT 2013
Considering a possible option to remove code from Catalyst that inspects =
the content length of the response body, I was looking the the code for =
Plack::Middleware::ContentLength =
<http://cpansearch.perl.org/src/MIYAGAWA/Plack-1.0030/lib/Plack/Middleware/=
ContentLength.pm>. =
The meat is really in Plack::Util, and as a dependency of =
HTTP::Server::PSGI there is no additional dependency for Catalyst. So it =
could even be a default middleware, retaining (replacing) what Catalyst =
already does.
While the implementation is similar to the Catalyst code there is a bit =
more to it, mostly in Plack::Util::is_real_fh. The function does a =
number of checks to determine whether the filehandle is real before =
handing over to filestat -s
to get the size. There are noted problems with things that may look like =
a filehandle but are not specifically a typical filehandle, so this is =
valid. But there may be cases where you want to do so. My article today =
actually (http://www.catalystframework.org/calendar/2013/21), even =
though I'm actually talking here about the above case.
So I can play with the filehandle implementation class enough to get the =
"is_real_fh" test to return true. But there seems to be a problem in =
implementing the middleware.
From the test code I get the right result. But note the inclusion order =
of Plack::Util. If that is loaded before the class
that does the required mucking around, the function passes, if not the =
function will fail. This all revolves around overriding the built-in =
"fileno" as this is used in "is_real_fh".
So it seems the same issue is happening with loading order when the =
middleware is applied to a PSGI app. But I'm not sure how or whether =
there is some other funny thing going on.
Any Ideas? Pointers?
Neil
Test case code. Also with another version of is_real_fh with extended =
debugging.
#!/usr/bin/env perl
use Modern::Perl;
use Scalar::Util;
#use Plack::Util;
use MyApp::FunnyIO::Domain::GzipData;
use MyApp::FunnyIO::Domain::FunnyIO;
use Plack::Util;
my $comp =3D MyApp::FunnyIO::Domain::GzipData->new->getData();
my $body =3D MyApp::FunnyIO::Domain::FunnyIO->new( \$comp );
if ( -p $body or -c _ or -b _ ) {
say "nope";
} else {
say "yep";
}
my $reftype =3D Scalar::Util::reftype($body);
if ( $reftype eq 'IO' or $reftype eq 'GLOB' && *{$body}{IO} ) {
say "yep";
} else {
say "nope";
}
my $m_fileno =3D $body->fileno;
say "defined" if defined $m_fileno;
say "->fileno " . $m_fileno;
if ( Plack::Util::is_real_fh( $body ) ) {
say "yep i am real";
} else {
say "nope not real";
}
say ref( $body );
my $f_fileno =3D fileno( $body );
say "defined" if defined $f_fileno;
say "fileno " . $f_fileno;
say -s $body;
my $size =3D Plack::Util::content_length( $body );
say $size;
package MyTest;
use overload();
sub TRUE { 1=3D=3D1 }
sub FALSE { !TRUE }
sub is_real_fh {
my $fh =3D shift;
{
no warnings 'uninitialized';
if ( -p $fh or -c _ or -b _ ) {
warn "do not like file test";
return FALSE;
}
}
my $reftype =3D Scalar::Util::reftype($fh) or return;
if ( $reftype eq 'IO'
or $reftype eq 'GLOB' && *{$fh}{IO}
) {
my $m_fileno =3D $fh->fileno;
unless( defined $m_fileno ) {
warn "m_fileno not defined";
return FALSE;
}
unless( $m_fileno >=3D 0 ) {
warn "m_fileno less than 0";
return FALSE;
}
my $f_fileno =3D fileno( $fh );
unless( defined $f_fileno ) {
warn "f_fileno not defined";
return FALSE;
}
unless( $f_fileno >=3D 0 ) {
warn "f_fileno less than 0";
return FALSE;
}
return TRUE;
} else {
warn "Don't like GLOB type";
return FALSE;
}
And the code for the Filehandle ('like') class:
package MyApp::FunnyIO::Domain::FunnyIO;
use Moose;
use Moose::Exporter;
use MooseX::NonMoose::InsideOut;
extends 'IO::Uncompress::Gunzip';
use IO::Scalar;
use namespace::sweep;
BEGIN {
*CORE::GLOBAL::fileno =3D sub {
my $fh =3D shift;
return 0 if ( ref $fh eq 'MyApp::FunnyIO::Domain::FunnyIO' );
return CORE::fileno($fh);
};
}
use overload
'bool' =3D> sub {1},
'-X' =3D> \&myFileTest;
Moose::Exporter->setup_import_methods(
as_is =3D> ['fileno'],
);
has '_content' =3D> ( is =3D> 'ro' );
sub fileno {
my $fh =3D shift;
return fileno($fh);
}
sub myFileTest {
my ( $self, $arg ) =3D @_;
if ( $arg eq "s" ) {
my $io =3D IO::Scalar->new( $self->_content );
$io->seek( -4, 2 );
$io->read( my $buf, 4 );
return unpack( 'V', $buf );
} elsif ( $arg eq "p" ) {
return undef;
} else {
die "Got: $arg Only implementing a size operator at this time";
}
}
around BUILDARGS =3D> sub {
my ( $orig, $class, $ref ) =3D @_;
return $class->$orig({ '_content' =3D> $ref });
};
sub FOREIGNBUILDARGS {
my ( $class, $args ) =3D @_;
return $args;
}
no Moose;
__PACKAGE__->meta->make_immutable;
1;
And for a simple PSGI app:
use MyApp::FunnyIO::Domain::GzipData;
use MyApp::FunnyIO::Domain::FunnyIO;
use Plack::Builder;
#use Plack::Util;
#use Plack::Middleware::ContentLength;
builder {
enable 'Plack::Middleware::ContentLength';
my $app =3D sub {
my $env =3D shift;
my $comp =3D MyApp::FunnyIO::Domain::GzipData->new->getData();
my $body =3D MyApp::FunnyIO::Domain::FunnyIO->new( \$comp );
return [
'200',
[ 'Content-Type' =3D> 'text/plain' ],
$body,
];
};
}
---
This email is free from viruses and malware because avast! Antivirus protec=
tion is active.
http://www.avast.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.scsys.co.uk/pipermail/catalyst/attachments/20131221/55c42=
5f4/attachment.htm
More information about the Catalyst
mailing list