HTTP-Promise

 view release on metacpan or  search on metacpan

lib/HTTP/Promise/Parser.pm  view on Meta::CPAN

    my $bkp_version;
    # try-catch
    local $@;
    eval
    {
        if( $opts->{request} )
        {
            # We have to do this, because of a bug in TTP::Parser2::XS where HTTP/2 is not supported.
            # We save the value and replace it with one supported and we put it back after in
            # the data we return.
            # <https://rt.cpan.org/Ticket/Display.html?id=142808>
            if( index( $$str, 'HTTP/2' ) != -1 )
            {
                if( $$str =~ s,^((?:\S+)[[:blank:]\h]+(?:\S+)[[:blank:]\h]+HTTP/)(2(?:\.\d)?),${1}1.1, )
                {
                    $bkp_version = $2;
                }
            }
            $len = HTTP::Parser2::XS::parse_http_request( $$str, $r );
        }
        elsif( $opts->{response} )
        {
            if( index( $$str, 'HTTP/2' ) != -1 )
            {
                if( $$str =~ s,^(HTTP/)(2(?:\.\d)?),${1}1.1, )
                {
                    $bkp_version = $2;
                }
            }
            $len = HTTP::Parser2::XS::parse_http_response( $$str, $r );
        }
    };
    if( $@ )
    {
        return( $self->error({ code => 400, message => $@, class => $EXCEPTION_CLASS }) );
    }
    
    if( $len == -1 )
    {
        return( $self->error({ code => 400, message => 'Bad request', class => $EXCEPTION_CLASS }) );
    }
    elsif( $len == -2 && $max_headers_size > 0 && length( $$str ) > $max_headers_size )
    {
        # 431: HTTP request header fields too large
        # 413: Request entity too large
        return( $self->error({ code => 413, message => 'Incomplete and too long request', class => $EXCEPTION_CLASS }) );
    }
    # Which one is best?
    # 406 Unacceptable
    # 411 Length required
    # 417 Expectation failed
    # 422 Unprocessable entity
    # 425 Too early
    elsif( $len == -2 )
    {
        return( $self->error({ code => 425, message => 'Incomplete request, call again when there is more data.', class => $EXCEPTION_CLASS }) );
    }
    # response headers:
    # {
    #   "_content_length" => 15,
    #   "_keepalive"      => 0,
    #   "_message"        => "OK",
    #   "_protocol"       => "HTTP/1.0",
    #   "_status"         => 200,
    #   "content-length"  => [15],
    #   "content-type"    => ["text/plain"],
    #   "host"            => ["example.com"],
    #   "user-agent"      => ["hoge"],
    # }
    # request headers:
    # {
    #   "_content_length" => 27,
    #   "_keepalive"      => 1,
    #   "_method"         => "POST",
    #   "_protocol"       => "HTTP/1.1",
    #   "_query_string"   => "",
    #   "_request_uri"    => "/test",
    #   "_uri"            => "/test",
    #   "content-length"  => [27],
    #   "content-type"    => ["application/x-www-form-urlencoded"],
    #   "host"            => ["foo.example"],
    # }
    $r->{_protocol} = "HTTP/${bkp_version}" if( defined( $bkp_version ) );
    # warn( "HTTP::Parser2::XS->parse_headers_xs: bytes read ($len) differs from _content_length (", ( $r->{_content_length} // '' ), ")\n" ) if( defined( $r->{_content_length} ) && length( $r->{_content_length} ) && $len != $r->{_content_length} && ...
    my $def = { length => $len };
    # Sadly enough, HTTP::Parser2::XS does not provide the order of the header and
    # although we could find out ourself, it would defeat the purpose of using an XS module
    # so we default to alphabetical order
    # If this is really important, you can use parse_request method instead
    my $headers = $self->new_array;
    # Skip keys that start with _. They are private properties
    for( sort( grep( !/^_/, keys( %$r ) ) ) )
    {
        my $k = $_;
        $k =~ tr/-/_/ if( $opts->{convert_dash} );
        $headers->push( $k => $r->{ $_ } );
    }
    $def->{headers} = HTTP::Promise::Headers->new( @$headers );
    if( $opts->{request} )
    {
        @$def{qw( method protocol )} = @$r{qw( _method _protocol )};
        $def->{uri} = URI->new( $r->{_request_uri} ) if( exists( $r->{_request_uri} ) && length( $r->{_request_uri} ) );
    }
    elsif( $opts->{response} )
    {
        @$def{qw( code status protocol )} = @$r{qw( _status _message _protocol )};
    }
    $def->{version} = $self->parse_version( $r->{_protocol} ) || return( $self->pass_error );
    # It seems 
    while( substr( $$str, 0, $len ) =~ /$CRLF($CRLF)$/ )
    {
        $len -= length( $1 );
    }
    $def->{length} = $len;
    return( $def );
}

sub parse_multi_part
{
    my $self = shift( @_ );
    my $opts = $self->_get_args_as_hash( @_ );
    my $ent = $opts->{entity} ||
        return( $self->error({ code => 500, message => "No entity object was provided." }) );
    my $reader = $opts->{reader} ||
        return( $self->error({ code => 500, message => "No reader object was provided." }) );
    my $headers = $ent->headers ||
        return( $self->error({ code => 500, message => "No headers object found in entity object." }) );
    my $ct = $headers->content_type;
    my $h = $headers->new_field( 'Content-Type' => $ct ) || return( $self->pass_error( $headers->error ) );
    my $boundary = $h->boundary ||
        return( $self->error( "No boundary could be found in the Content-Type header '$ct'" ) );
    my $max_in_memory = $self->max_body_in_memory_size;
    my $default_mime = $DEFAULT_MIME_TYPE || 'application/octet-stream';



( run in 0.459 second using v1.01-cache-2.11-cpan-5511b514fd6 )