The Perl Toolchain Summit 2025 Needs You: You can help 🙏 Learn more

##----------------------------------------------------------------------------
## Asynchronous HTTP Request and Promise - ~/lib/HTTP/Promise/Entity/Body.pm
## Version v0.1.0
## Copyright(c) 2022 DEGUEST Pte. Ltd.
## Author: Jacques Deguest <jack@deguest.jp>
## Created 2022/04/19
## Modified 2022/04/19
## All rights reserved
##
## This program is free software; you can redistribute it and/or modify it
## under the same terms as Perl itself.
##----------------------------------------------------------------------------
BEGIN
{
use strict;
use warnings;
use vars qw( $VERSION $EXCEPTION_CLASS );
our $EXCEPTION_CLASS = 'HTTP::Promise::Exception';
our $VERSION = 'v0.1.0';
};
use strict;
sub as_lines
{
my $self = shift( @_ );
my $io = $self->open( 'r' ) || return( $self->pass_error );
my $lines = $self->new_array;
local $_;
while( defined( $_ = $io->getline ) )
{
$lines->push( $_ );
}
$io->close;
return( $lines );
}
# Very dangerous to use indiscriminately when dealing with large data stored on file
sub as_string
{
my $self = shift( @_ );
my $opts = $self->_get_args_as_hash( @_ );
my $str = $self->new_scalar;
my $params = {};
for( qw( binmode debug ) )
{
$params->{ $_ } = $opts->{ $_ } if( exists( $opts->{ $_ } ) && $opts->{ $_ } );
}
my( $pos, $io );
if( $self->can( 'opened' ) && ( $io = $self->opened ) )
{
$pos = $self->tell;
# Rewind
$self->seek(0,0);
}
else
{
$io = $self->open( 'r', ( scalar( keys( %$params ) ) ? $params : () ) ) || return( $self->pass_error );
}
my( $buff, $nread );
while( $nread = $io->read( $buff, 8192 ) )
{
$$str .= $buff;
}
if( defined( $pos ) )
{
$self->seek( $pos, 0 );
}
return( $str );
}
# sub binmode { return( shift->_set_get_boolean( 'binmode', @_ ) ); }
sub data { return( shift->as_string( @_ ) ); }
sub dup { return( shift->clone( @_ ) ); }
# sub open { return; }
sub path { return; }
sub print
{
my $self = shift( @_ );
my $fh = shift( @_ );
my $opts = $self->_get_args_as_hash( @_ );
my $nread;
# Get output filehandle, and ensure that it's a printable object
return( $self->error( "Filehandle provided ($fh) is not a proper filehandle and its not a HTTP::Promise::IO object." ) ) if( !$self->_is_glob( $fh ) && !$self->_is_a( $fh => 'HTTP::Promise::IO' ) );
my $params = {};
$params->{binmode} = $opts->{binmode} if( exists( $opts->{binmode} ) && $opts->{binmode} );
# Write it
my $buff = '';
my $io = $self->open( 'r', ( scalar( keys( %$params ) ) ? $params : () ) ) || return( $self->pass_error );
while( $nread = $io->read( $buff, 8192 ) )
{
print( $fh $buff ) || return( $self->error( "Unable to write to filehandle '$fh': $!" ) );
}
$io->close;
return( defined( $nread ) );
}
sub purge { return; }
# NOTE: sub FREEZE is inherited
sub STORABLE_freeze { CORE::return( CORE::shift->FREEZE( @_ ) ); }
sub STORABLE_thaw { CORE::return( CORE::shift->THAW( @_ ) ); }
# NOTE: sub THAW is inherited
# NOTE: HTTP::Promise::Body::File package
BEGIN
{
use strict;
use warnings;
use parent -norequire, qw( HTTP::Promise::Body Module::Generic::File );
use vars qw( $EXCEPTION_CLASS );
our $EXCEPTION_CLASS = 'HTTP::Promise::Exception';
};
use strict;
sub new { return( shift->Module::Generic::File::new( @_ ) ); }
sub init
{
my $self = shift( @_ );
$self->{_init_strict_use_sub} = 1;
$self->{_exception_class} = $EXCEPTION_CLASS;
$self->Module::Generic::File::init( @_ ) || return( $self->pass_error );
return( $self );
}
sub path { return( shift->filename( @_ ) ); }
sub purge { return( shift->unlink ); }
sub FREEZE { CORE::return( CORE::shift->Module::Generic::File::FREEZE( @_ ) ); }
sub STORABLE_freeze { CORE::return( CORE::shift->Module::Generic::File::STORABLE_freeze( @_ ) ); }
# NOTE: sub STORABLE_thaw is inherited
# NOTE: sub THAW is inherited
# NOTE: HTTP::Promise::Body::Scalar package
BEGIN
{
use strict;
use warnings;
use parent -norequire, qw( HTTP::Promise::Body Module::Generic::Scalar );
use vars qw( $EXCEPTION_CLASS );
use overload (
'""' => sub{ $_[0] },
bool => sub{1},
fallback => 1,
);
our $EXCEPTION_CLASS = 'HTTP::Promise::Exception';
};
use strict;
# sub new { return( shift->Module::Generic::Scalar::new( @_ ) ); }
sub new
{
my $this = shift( @_ );
my $new = $this->Module::Generic::Scalar::new( @_ );
return( $this->pass_error( $this->Module::Generic::Scalar::error ) ) if( !defined( $new ) );
return( $new );
}
sub as_string { return( @_ > 1 ? shift->SUPER::as_string( @_ ) : $_[0]->new_scalar( $_[0] ) ); }
sub checksum_md5
{
my $self = shift( @_ );
$self->_load_class( 'Crypt::Digest::MD5' ) || return( $self->pass_error );
return( Crypt::Digest::MD5::md5_hex( $$self ) );
}
sub error { return( shift->Module::Generic::Scalar::error( @_ ) ); }
sub pass_error { return( shift->Module::Generic::Scalar::pass_error( @_ ) ); }
sub purge { return( shift->Module::Generic::Scalar::reset( @_ ) ); }
sub set { return( shift->Module::Generic::Scalar::set( @_ ) ); }
sub FREEZE { return( shift->Module::Generic::Scalar::FREEZE( @_ ) ); }
# NOTE: sub STORABLE_freeze is inherited
# NOTE: sub STORABLE_thaw is inherited
sub THAW { return( shift->Module::Generic::Scalar::THAW( @_ ) ); }
# NOTE: HTTP::Promise::Body::InCore package
BEGIN
{
use strict;
use warnings;
use parent -norequire, qw( HTTP::Promise::Body::Scalar );
use vars qw( $EXCEPTION_CLASS );
our $EXCEPTION_CLASS = 'HTTP::Promise::Exception';
};
use strict;
sub init
{
my $self = shift( @_ );
my $data = shift( @_ );
# nothing
if( !defined( $data ) ||
# simple scalar or a scalar object
( !ref( $data ) || ( $self->_is_scalar( $data ) && overload::Method( $data => '""' ) ) ) ||
# or a scalar reference
ref( $data ) eq 'SCALAR' )
{
# pass through
}
elsif( $self->_is_array( $data ) )
{
$data = join( '', @$data );
}
else
{
return( $self->error( "Data of type '", ref( $data ), "' is unsupported." ) );
}
$self->SUPER::init( $data, @_ ) || return( $self->pass_error );
return( $self );
}
1;
# NOTE: POD
__END__
=encoding utf-8
=head1 NAME
HTTP::Promise::Body - HTTP Message Body Class
=head1 SYNOPSIS
use HTTP::Promise::Body;
my $body = HTTP::Promise::Body->new ||
die( HTTP::Promise::Body->error, "\n" );
=head1 VERSION
v0.1.0
=head1 DESCRIPTION
This class represents an entity body.
Here is how it fits in overall relation with other classes.
+-------------------------+ +--------------------------+
| | | |
| HTTP::Promise::Request | | HTTP::Promise::Response |
| | | |
+------------|------------+ +-------------|------------+
| |
| |
| |
| +------------------------+ |
| | | |
+--- HTTP::Promise::Message |---+
| |
+------------|-----------+
|
|
+------------|-----------+
| |
| HTTP::Promise::Entity |
| |
+------------|-----------+
|
|
+------------|-----------+
| |
| HTTP::Promise::Body |
| |
+------------------------+
=head1 METHODS
=head2 as_lines
Returns a new L<array object|Module::Generic::Array> containing the body lines.
=head2 as_string
Returns the body data as a L<scalar object|Module::Generic::Scalar>.
Be mindful about the size of the body before you load it all in memory. You can get the size of the body with C<< $body->length >>
=head2 data
This is just an alias for L</as_string>
=head2 dup
This is an alias for L<Module::Generic/clone>, which is inherited by this class.
=head2 path
This is a no-op and is superseded by inheriting classes.
=head2 print
Provided with a filehandle, or an L<HTTP::Promise::IO> object and an hash or hash reference of options and this will print the body data to and returns true if it was successful, or sets an L<error|Module::Generic/error> and returns C<undef>
=head2 purge
This is a no-op and is superseded by inheriting classes.
=head1 AUTHOR
Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
=head1 SEE ALSO
L<HTTP::Promise>, L<HTTP::Promise::Request>, L<HTTP::Promise::Response>, L<HTTP::Promise::Message>, L<HTTP::Promise::Entity>, L<HTTP::Promise::Headers>, L<HTTP::Promise::Body>, L<HTTP::Promise::Body::Form>, L<HTTP::Promise::Body::Form::Data>, L<HTTP::Promise::Body::Form::Field>, L<HTTP::Promise::Status>, L<HTTP::Promise::MIME>, L<HTTP::Promise::Parser>, L<HTTP::Promise::IO>, L<HTTP::Promise::Stream>, L<HTTP::Promise::Exception>
L<Module::Generic::File>, L<Module::Generic::Scalar>, L<Module::Generic::File::IO>, L<Module::Generic::Scalar::IO>
L<PerlIO::scalar>
=head1 COPYRIGHT & LICENSE
Copyright(c) 2022 DEGUEST Pte. Ltd.
All rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
=cut