HTTP-Promise

 view release on metacpan or  search on metacpan

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

sub decoded_content
{
    my $self = shift( @_ );
    my $opts = $self->_get_args_as_hash( @_ );
    $opts->{charset_strict} //= 0;
    my $old_fatal = $self->fatal;
    $self->fatal( $opts->{raise_error} ? 1 : 0 );
    my $body = $self->decode_content( ( scalar( keys( %$opts ) ) ? $opts : () ) );
    return( $self->pass_error ) if( !defined( $body ) );
    # There is no entity or no body
    if( !$body )
    {
        return( $self->new_scalar );
    }
    $self->fatal( $old_fatal );
    my $dummy = '';
    return( $opts->{ref} ? \$dummy : $dummy ) if( $body->is_empty );
    unless( $opts->{binmode} )
    {
        # Need to explicitly provide the body to get the encoding from, otherwise, io_encoding()
        # would get the default one, which might not yet be replaced with its decoded version.
        my $enc = $self->entity->io_encoding( body => $body, charset_strict => $opts->{charset_strict} );
        $opts->{binmode} = $enc if( $enc );
    }
    
    # Because of an edge case where open with :binmode(utf-8) layer does not decode properly \x{FF}
    # but Encode::decode( 'utf-8', $buff ) does, and since the body is loaded into a string
    # anyway, we first read the data as raw and then decode it with Encode
    my $binmode;
    if( exists( $opts->{binmode} ) && 
        length( $opts->{binmode} ) && 
        lc( substr( $opts->{binmode}, 0, 3 ) ) eq 'utf' )
    {
        $binmode = delete( $opts->{binmode} );
        $opts->{binmode} = 'raw';
    }
    my $content = $body->as_string( ( scalar( keys( %$opts ) ) ? $opts : () ) );
    if( !defined( $content ) )
    {
        return( $self->pass_error( $body->error ) );
    }
    if( defined( $binmode ) )
    {
        $self->_load_class( 'Encode' ) || return( $self->pass_error );
        # try-catch
        local $@;
        eval
        {
            $$content = Encode::decode( $binmode, $$content, ( Encode::FB_DEFAULT | Encode::LEAVE_SRC ) );
        };
        if( $@ )
        {
            return( $self->error( "Error decoding body content with character encoding '$binmode': $@" ) );
        }
    }
    
    # $content is a scalar object that stringifies
    if( $self->headers->content_is_xml )
    {
        # Get rid of the XML encoding declaration if present (\x{FEFF})
        $$content =~ s/^\N{BOM}//;
        if( $$content =~ m/^(?<decl>[[:blank:]\h\v]*<\?xml(.*?)\?>)/is )
        {
            substr( $$content, 0, length( $+{decl} ) ) =~ s{
                [[:blank:]\h\v]+
                encoding[[:blank:]\h\v]*=[[:blank:]\h\v]*
                (?<quote>["'])
                (?<encoding>(?>\\\g{quote}|(?!\g{quote}).)*+)
                \g{quote}
            }
            {}xmis;
        }
    }
    return( $content );
}

sub decoded_content_utf8
{
    my $self = shift( @_ );
    my $opts = $self->_get_args_as_hash( @_ );
    $opts->{binmode} = 'utf-8';
    my $data = $self->decoded_content( $opts );
    if( !defined( $data ) && $self->error )
    {
        return( $self->pass_error );
    }

    if( $self->headers->content_is_xml )
    {
        # Get rid of the XML encoding declaration if present
        $$data =~ s/^\x{FEFF}//;
        
        if( $$data =~ /^(\s*<\?xml[^\x00]*?\?>)/ )
        {
            substr( $$data, 0, length($1)) =~ s/\sencoding\s*=\s*(["']).*?\1//;
        }
    }
    return( $data );
}

sub decoded_json
{
    my $self = shift( @_ );
    my $opts = $self->_get_args_as_hash( @_ );
    $opts->{binmode} = 'utf-8';
    my $data = $self->decoded_content_utf8( $opts );
    return( $self->pass_error ) if( !defined( $data ) && $self->error );
    return if( !defined( $data ) );
    if( $self->headers->content_is_json )
    {
        local $@;
        # try-catch
        my $ref = $self->new_json_safe->relaxed->decode( $$data );
        if( !defined( $ref ) && $self->error )
        {
            my $err = $self->error->message;
            return( $self->error( "Error decoding JSON payload: $err\nPayload was: $$data" ) );
        }
        return( $ref );
    }
    return( $$data );

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


    my $m = HTTP::Promise::Message->new([
        'Content-Type' => 'text/plain; charset=utf-8',
        ],
        "\x{E3}\x{81}\x{8A}\x{E6}\x{97}\x{A9}\x{E3}\x{81}\x{86}\x{EF}\x{BC}\x{81}\x{A}",
    );
    my $content = $m->content;

C<$content> would contain undecoded utf-8 bytes, i.e. not in perl's internal representation. Indeed, charset is never decoded. If you want the charset decoded content, use L</decoded_content>, which will guess the content charset to decode it into pe...

    my $decoded_content = $m->decoded_content( binmode => 'utf-8' );

or

    my $decoded_content = $m->decoded_content_utf8;

See L</decoded_content> for more information.

=head2 content_charset

This is a convenient method that calls L<HTTP::Promise::Entity/content_charset> and returns the result.

This method attempts at guessing the content charset of the entity body.

It returns a string representing the content charset, possibly empty if nothing was found, or upon error, sets an L<error|Module::Generic/error> and returns C<undef>.

=head2 content_ref

This sets or gets the content as a scalar reference.

In assignment mode, this takes a scalar reference and pass it to L</content> and returns the L<body object|HTTP::Promise::Body>

Otherwise, this returns the content as L<scalar object|Module::Generic::Scalar>.

If an error occurs, this sets an L<error|Module::Generic/error> and returns C<undef>.

=head2 decodable

This gets an L<array object|Module::Generic::Array> of all supported and installed decodings on the system, by calling L<HTTP::Promise::Stream/decodable>

=head2 decode

This decodes the HTTP message body and return true.

If there is no C<Content-Encoding> set, or the entity body is empty, or the entity body already has been decoded, this does nothing obviously. Otherwise, this calls L<HTTP::Promise::Entity/decode_body> passing it the encodings as an array reference.

If an error occurs, this sets an L<error|Module::Generic/error> and returns C<undef>.

=head2 decode_content

This is similar to </decode>, except that it takes an hash or hash reference of options passed to L<HTTP::Promise::Entity/decode_body>, notably C<replace>, which if true will replace the body by its decoded version and if false will return a new body...

This returns the entity body object upon success, or upon error, sets an L<error|Module::Generic/error> and returns C<undef>.

=head2 decoded_content

This takes an hash or hash reference of options and returns the decoded representation of the body, including charset.

This calls L</decode_content>, passing it the options provided, to decompress the entity body if necessary. Then, unless the C<binmode> option was provided, this calls L<HTTP::Promise::Entity/io_encoding> to guess the charset encoding, and set the C<...

If the entity body is an xml file, any C<BOM> (Byte Order Mark) will be removed.

This returns the content as a L<scalar object|Module::Generic::Scalar>, or upon error, sets an L<error|Module::Generic/error> and returns C<undef>.

Supported options are:

=over 4

=item * C<binmode>

The L<PerlIO> encoding to apply to decode the data.

If not provided, this will be guessed by calling L<HTTP::Promise::Entity/io_encoding>

=item * C<charset_strict>

If true, this will returns an error if there is some issues with the content charset. By default, this is false, making it lenient, especially with malformed utf-8.

=item * C<raise_error>

When set to true, this will cause this method to die upon error. Default is false.

=back

=head2 decoded_content_utf8

This calls L</decoded_content>, but this sets the C<binmode> option to C<utf-8>.

It returns whatever L</decode_content> returns.

=head2 decoded_json

If the content type of the response is C<application/json>, this will call L</decoded_content_utf8> and decode the JSON payload and return the hash reference. If an error occurred, it will set L<an error|Module::Generic/error> and return C<undef> in ...

=head2 dump

This takes an hash or hash reference of options and either print the resulting dump on the C<STDOUT> in void content, or returns a string representation of the HTTP message, or upon error, sets an L<error|Module::Generic/error> and returns C<undef>.

Supported options are:

=over 4

=item * C<maxlength>

The maximum amount of body data in bytes to display.

=item * C<no_content>

The string to use when there is no entity body data.

=item * C<prefix>

A string to be added at the beginning of each line of the data returned.

=item * C<preheader>

An arbitrary string to add before the HTTP headers, typically the HTTP C<start line>

=back

    # Returns a string



( run in 1.225 second using v1.01-cache-2.11-cpan-39bf76dae61 )