use URI::https;
use URI::QueryParam;
use HTTP::Headers;
+use Stream::Buffered;
+use Hash::MultiValue;
+use Scalar::Util;
use Moose;
sub _build_io_fh {
my $self = shift;
return $self->env->{'psgix.io'}
+ || (
+ $self->env->{'net.async.http.server.req'} &&
+ $self->env->{'net.async.http.server.req'}->stream) ## Until I can make ioasync cabal see the value of supportin psgix.io (jnap)
|| die "Your Server does not support psgix.io";
};
}
}
+has _use_hash_multivalue => (
+ is=>'ro',
+ required=>1,
+ default=> sub {0});
+
# Amount of data to read from input on each pass
our $CHUNKSIZE = 64 * 1024;
my $parameters = {};
my $body_parameters = $self->body_parameters;
my $query_parameters = $self->query_parameters;
+
+ ## setup for downstream plack
+ $self->env->{'plack.request.merged'} ||= do {
+ my $query = $self->env->{'plack.request.query'} || Hash::MultiValue->new;
+ my $body = $self->env->{'plack.request.body'} || Hash::MultiValue->new;
+ Hash::MultiValue->new($query->flatten, $body->flatten);
+ };
+
+ if($self->_use_hash_multivalue) {
+ return $self->env->{'plack.request.merged'}->clone; # We want a copy, in case your App is evil
+ }
+
# We copy, no references
foreach my $name (keys %$query_parameters) {
my $param = $query_parameters->{$name};
sub prepare_body {
my ( $self ) = @_;
- #warn "XXX ${\$self->_uploadtmp}" if $self->_has_uploadtmp;
+ # If previously applied middleware created the HTTP::Body object, then we
+ # just use that one.
if(my $plack_body = $self->env->{'plack.request.http.body'}) {
- warn "wtF" x 100;
$self->_body($plack_body);
- $self->_body->cleanup(1); # Make extra sure!
- $self->_body->tmpdir( $self->_uploadtmp )
- if $self->_has_uploadtmp;
- } else {
-
+ $self->_body->cleanup(1);
+ return;
}
- if ( my $length = $self->_read_length ) {
- unless ( $self->_body ) {
- my $type = $self->header('Content-Type');
- $self->_body(HTTP::Body->new( $type, $length ));
- $self->_body->cleanup(1); # Make extra sure!
- $self->_body->tmpdir( $self->_uploadtmp )
- if $self->_has_uploadtmp;
- }
+ # Define PSGI ENV placeholders, or for empty should there be no content
+ # body (typical in HEAD or GET). Looks like from Plack::Request that
+ # middleware would probably expect to see this, even if empty
- # Check for definedness as you could read '0'
- while ( defined ( my $buffer = $self->read() ) ) {
- $self->prepare_body_chunk($buffer);
- }
+ $self->env->{'plack.request.body'} = Hash::MultiValue->new;
+ $self->env->{'plack.request.upload'} = Hash::MultiValue->new;
- # paranoia against wrong Content-Length header
- my $remaining = $length - $self->_read_position;
- if ( $remaining > 0 ) {
- Catalyst::Exception->throw(
- "Wrong Content-Length value: $length" );
- }
- }
- else {
- # Defined but will cause all body code to be skipped
- $self->_body(0);
- }
-}
+ # If there is nothing to read, set body to naught and return. This
+ # will cause all body code to be skipped
-sub prepare_bodyXXX {
- my ( $self ) = @_;
- if(my $plack_body = $self->env->{'plack.request.http.body'}) {
-
+ return $self->_body(0) unless my $length = $self->_read_length;
- } else {
-
- }
+ # Unless the body has already been set, create it. Not sure about this
+ # code, how else might it be set, but this was existing logic.
- die "XXX ${\$self->_uploadtmp}" x1000; $self->_has_uploadtmp;
+ unless ($self->_body) {
+ my $type = $self->header('Content-Type');
+ $self->_body(HTTP::Body->new( $type, $length ));
+ $self->_body->cleanup(1);
- if ( my $length = $self->_read_length ) {
- unless ( $self->_body ) {
-
- ## If something plack middle already ready the body, just use
- ## that.
+ # JNAP: I'm not sure this is doing what we expect, but it also doesn't
+ # seem to be hurting (seems ->_has_uploadtmp is true more than I would
+ # expect.
- my $body;
- if(my $plack_body = $self->env->{'plack.request.http.body'}) {
- $body = $plack_body;
- } else {
- my $type = $self->header('Content-Type');
- $body = HTTP::Body->new($type, $length);
+ $self->_body->tmpdir( $self->_uploadtmp )
+ if $self->_has_uploadtmp;
+ }
- ## Play nice with Plak Middleware that looks for a body
- $self->env->{'plack.request.http.body'} = $body;
- $self->_body($body);
+ # Ok if we get this far, we have to read psgi.input into the new body
+ # object. Lets play nice with any plack app or other downstream, so
+ # we create a buffer unless one exists.
+
+ my $stream_buffer;
+ if ($self->env->{'psgix.input.buffered'}) {
+ # Be paranoid about previous psgi middleware or apps that read the
+ # input but didn't return the buffer to the start.
+ $self->env->{'psgi.input'}->seek(0, 0);
+ } else {
+ $stream_buffer = Stream::Buffered->new($length);
+ }
- $body->cleanup(1); # Make extra sure!
- $body->tmpdir( $self->_uploadtmp )
- if $self->_has_uploadtmp;
- }
- }
+ # Check for definedness as you could read '0'
+ while ( defined ( my $chunk = $self->read() ) ) {
+ $self->prepare_body_chunk($chunk);
+ $stream_buffer->print($chunk) if $stream_buffer;
+ }
- # Check for definedness as you could read '0'
- while ( defined ( my $buffer = $self->read() ) ) {
- $self->prepare_body_chunk($buffer);
- }
+ # Ok, we read the body. Lets play nice for any PSGI app down the pipe
- # paranoia against wrong Content-Length header
- my $remaining = $length - $self->_read_position;
- if ( $remaining > 0 ) {
- Catalyst::Exception->throw(
- "Wrong Content-Length value: $length" );
- }
+ if ($stream_buffer) {
+ $self->env->{'psgix.input.buffered'} = 1;
+ $self->env->{'psgi.input'} = $stream_buffer->rewind;
+ } else {
+ $self->env->{'psgi.input'}->seek(0, 0); # Reset the buffer for downstream middleware or apps
}
- else {
- # Defined but will cause all body code to be skipped
- $self->_body(0);
+
+ $self->env->{'plack.request.http.body'} = $self->_body;
+ $self->env->{'plack.request.body'} = Hash::MultiValue->from_mixed($self->_body->param);
+
+ # paranoia against wrong Content-Length header
+ my $remaining = $length - $self->_read_position;
+ if ( $remaining > 0 ) {
+ Catalyst::Exception->throw("Wrong Content-Length value: $length" );
}
}
-
sub prepare_body_chunk {
my ( $self, $chunk ) = @_;
$self->prepare_body if ! $self->_has_body;
return {} unless $self->_body;
- return $self->_body->param;
+ return $self->_use_hash_multivalue ?
+ $self->env->{'plack.request.body'}->clone :
+ $self->_body->param;
}
sub prepare_connection {