—##----------------------------------------------------------------------------
## Asynchronous HTTP Request and Promise - ~/lib/HTTP/Promise/Headers/Range.pm
## Version v0.1.0
## Copyright(c) 2022 DEGUEST Pte. Ltd.
## Author: Jacques Deguest <jack@deguest.jp>
## Created 2022/05/08
## Modified 2022/05/08
## All rights reserved.
##
##
## This program is free software; you can redistribute it and/or modify it
## under the same terms as Perl itself.
##----------------------------------------------------------------------------
package
HTTP::Promise::Headers::Range;
BEGIN
{
our
$VERSION
=
'v0.1.0'
;
};
use
strict;
use
warnings;
sub
init
{
my
$self
=
shift
(
@_
);
$self
->{ranges} = [];
$self
->{unit} =
'bytes'
;
@_
= ()
if
(
@_
== 1 &&
$self
->_is_a(
$_
[0] =>
'Module::Generic::Null'
) );
if
(
@_
)
{
my
$this
=
shift
(
@_
);
my
(
$unit
,
$ref
);
if
(
$self
->_is_array(
$this
) )
{
$ref
=
$this
;
}
else
{
return
(
$self
->error(
"Bad argument provided '$this'. You can provide either an array reference or a string."
) )
if
(
ref
(
$this
) && !overload::Method(
$this
,
'""'
) );
$this
=~ s/^[[:blank:]\h]+|[[:blank:]\h]+$//g;
if
(
$this
=~ s/^([a-zA-Z][a-zA-Z\_]+)[[:blank:]\h]*=[[:blank:]\h]*// )
{
$unit
= $1;
}
$ref
= [
split
( /[[:blank:]\h]*,[[:blank:]\h]*/,
"$this"
)];
}
my
$ranges
=
$self
->new_array;
foreach
(
@$ref
)
{
my
(
$start
,
$end
) =
split
( /[[:blank:]\h]*-[[:blank:]\h]*/ );
$start
=
undef
if
( !
length
(
$start
) );
$end
=
undef
if
( !
length
(
$end
) );
my
$range
= HTTP::Promise::Headers::Range::StartEnd->new(
$start
,
$end
);
$ranges
->
push
(
$range
);
}
$self
->ranges(
$ranges
);
}
$self
->{_init_strict_use_sub} = 1;
$self
->SUPER::init(
@_
) ||
return
(
$self
->pass_error );
$self
->_field_name(
'Range'
);
return
(
$self
);
}
sub
as_string
{
my
$self
=
shift
(
@_
);
# In the sprintf, we use %s and not %d, because start or end may be undef, and it would inadvertently result in 0
return
(
$self
->unit->
scalar
.
'='
.
$self
->ranges->
map
(
sub
{
sprintf
(
'%s-%s'
, (
$_
->start //
''
), (
$_
->end //
''
) ); })->
join
(
', '
)->
scalar
);
}
sub
new_range { HTTP::Promise::Headers::Range::StartEnd->new(
@_
) };
sub
ranges {
return
(
shift
->_set_get_array_as_object(
'ranges'
,
@_
) ); }
sub
unit {
return
(
shift
->_set_get_scalar_as_object(
'unit'
,
@_
) ); }
{
package
HTTP::Promise::Headers::Range::StartEnd;
BEGIN
{
};
sub
init
{
my
$self
=
shift
(
@_
);
my
@args
=
@_
;
@$self
{
qw( start end )
} =
splice
(
@_
, 0, 2 );
return
(
$self
->SUPER::init(
@_
) );
}
sub
end {
return
(
shift
->_set_get_number(
'end'
,
@_
) ); }
sub
start {
return
(
shift
->_set_get_number(
'start'
,
@_
) ); }
}
1;
# NOTE: POD
__END__
=encoding utf-8
=head1 NAME
HTTP::Promise::Headers::Range - Range Header Field
=head1 SYNOPSIS
use HTTP::Promise::Headers::Range;
my $range = HTTP::Promise::Headers::Range->new ||
die( HTTP::Promise::Headers::Range->error, "\n" );
$range->unit( 'bytes' ):
$range->ranges->push( $range->new_range( 200, 1000 ) );
my $start = $range->ranges->first->start; # 200
my $end = $range->ranges->first->end; # 1000
$range->ranges->push( $range->new_range( 1001, 2000 ) );
say $range->as_string;
# or
say "$range";
# bytes=200-1000, 1001-2000
=head1 VERSION
v0.1.0
=head1 DESCRIPTION
The following is an extract from Mozilla documentation.
The Range HTTP request header indicates the part of a document that the server should return.
Example:
# Getting multiple ranges
Range: bytes=200-1000, 2000-6576, 19000-
# The last 500 bytes
Range: bytes=0-499, -500
Range: bytes=200-
Range: bytes=200-1000
Range: bytes=200-1000, 1001-2000
Range: bytes=200-1000, 1001-2000, 2001-3000
Range: bytes=-4321
=head1 METHODS
=head2 as_string
Returns a string representation of the C<Range> object.
=head2 new_range
Provided with a start and and offset, and this will return a new C<HTTP::Promise::Headers::Range::StartEnd> object.
This object has two methods: C<start> and C<end> each capable of setting or returning its value, which may be C<undef>
=head2 ranges
Sets or gets the L<array object|Module::Generic::Array> that contains all the C<HTTP::Promise::Headers::Range::StartEnd> objects (see below for a descriptions). Thus you can use all the methods from L<Module::Generic::Array> to manipulate the range objects.
=head2 unit
The unit in which ranges are specified. This is usually C<bytes>.
=head1 HTTP::Promise::Headers::Range::StartEnd
=head2 end
Sets or gets the end of the range as a L<number object|Module::Generic::Number>
=head2 start
Sets or gets the start of the range as a L<number object|Module::Generic::Number>
=head1 AUTHOR
Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
=head1 SEE ALSO
See also L<rfc7233, section 3.1|https://tools.ietf.org/html/rfc7233#section-3.1> and L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range>
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>
=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