157 lines
4.0 KiB
Perl
157 lines
4.0 KiB
Perl
package Mojo::Content::Single;
|
|
use Mojo::Base 'Mojo::Content';
|
|
|
|
use Mojo::Asset::Memory;
|
|
use Mojo::Content::MultiPart;
|
|
|
|
has asset => sub { Mojo::Asset::Memory->new(auto_upgrade => 1) };
|
|
has auto_upgrade => 1;
|
|
|
|
sub body_contains { shift->asset->contains(shift) >= 0 }
|
|
|
|
sub body_size {
|
|
my $self = shift;
|
|
return ($self->headers->content_length || 0) if $self->is_dynamic;
|
|
return $self->{body_size} //= $self->asset->size;
|
|
}
|
|
|
|
sub clone {
|
|
my $self = shift;
|
|
return undef unless my $clone = $self->SUPER::clone();
|
|
return $clone->asset($self->asset);
|
|
}
|
|
|
|
sub get_body_chunk {
|
|
my ($self, $offset) = @_;
|
|
return $self->generate_body_chunk($offset) if $self->is_dynamic;
|
|
return $self->asset->get_chunk($offset);
|
|
}
|
|
|
|
sub new {
|
|
my $self = shift->SUPER::new(@_);
|
|
$self->{read} = $self->on(read => sub { $_[0]->asset($_[0]->asset->add_chunk($_[1])) });
|
|
return $self;
|
|
}
|
|
|
|
sub parse {
|
|
my $self = shift;
|
|
|
|
# Parse headers
|
|
$self->_parse_until_body(@_);
|
|
|
|
# Parse body
|
|
return $self->SUPER::parse unless $self->auto_upgrade && defined $self->boundary;
|
|
|
|
# Content needs to be upgraded to multipart
|
|
$self->unsubscribe(read => $self->{read});
|
|
my $multi = Mojo::Content::MultiPart->new(%$self);
|
|
$self->emit(upgrade => $multi);
|
|
return $multi->parse;
|
|
}
|
|
|
|
1;
|
|
|
|
=encoding utf8
|
|
|
|
=head1 NAME
|
|
|
|
Mojo::Content::Single - HTTP content
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use Mojo::Content::Single;
|
|
|
|
my $single = Mojo::Content::Single->new;
|
|
$single->parse("Content-Length: 12\x0d\x0a\x0d\x0aHello World!");
|
|
say $single->headers->content_length;
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
L<Mojo::Content::Single> is a container for HTTP content, based on L<RFC 7230|https://tools.ietf.org/html/rfc7230> and
|
|
L<RFC 7231|https://tools.ietf.org/html/rfc7231>.
|
|
|
|
=head1 EVENTS
|
|
|
|
L<Mojo::Content::Single> inherits all events from L<Mojo::Content> and can emit the following new ones.
|
|
|
|
=head2 upgrade
|
|
|
|
$single->on(upgrade => sub ($single, $multi) {...});
|
|
|
|
Emitted when content gets upgraded to a L<Mojo::Content::MultiPart> object.
|
|
|
|
$single->on(upgrade => sub ($single, $multi) {
|
|
return unless $multi->headers->content_type =~ /multipart\/([^;]+)/i;
|
|
say "Multipart: $1";
|
|
});
|
|
|
|
=head1 ATTRIBUTES
|
|
|
|
L<Mojo::Content::Single> inherits all attributes from L<Mojo::Content> and implements the following new ones.
|
|
|
|
=head2 asset
|
|
|
|
my $asset = $single->asset;
|
|
$single = $single->asset(Mojo::Asset::Memory->new);
|
|
|
|
The actual content, defaults to a L<Mojo::Asset::Memory> object with L<Mojo::Asset::Memory/"auto_upgrade"> enabled.
|
|
|
|
=head2 auto_upgrade
|
|
|
|
my $bool = $single->auto_upgrade;
|
|
$single = $single->auto_upgrade($bool);
|
|
|
|
Try to detect multipart content and automatically upgrade to a L<Mojo::Content::MultiPart> object, defaults to a true
|
|
value.
|
|
|
|
=head1 METHODS
|
|
|
|
L<Mojo::Content::Single> inherits all methods from L<Mojo::Content> and implements the following new ones.
|
|
|
|
=head2 body_contains
|
|
|
|
my $bool = $single->body_contains('1234567');
|
|
|
|
Check if content contains a specific string.
|
|
|
|
=head2 body_size
|
|
|
|
my $size = $single->body_size;
|
|
|
|
Content size in bytes.
|
|
|
|
=head2 clone
|
|
|
|
my $clone = $single->clone;
|
|
|
|
Return a new L<Mojo::Content::Single> object cloned from this content if possible, otherwise return C<undef>.
|
|
|
|
=head2 get_body_chunk
|
|
|
|
my $bytes = $single->get_body_chunk(0);
|
|
|
|
Get a chunk of content starting from a specific position. Note that it might not be possible to get the same chunk
|
|
twice if content was generated dynamically.
|
|
|
|
=head2 new
|
|
|
|
my $single = Mojo::Content::Single->new;
|
|
my $single = Mojo::Content::Single->new(asset => Mojo::Asset::File->new);
|
|
my $single = Mojo::Content::Single->new({asset => Mojo::Asset::File->new});
|
|
|
|
Construct a new L<Mojo::Content::Single> object and subscribe to event L<Mojo::Content/"read"> with default content
|
|
parser.
|
|
|
|
=head2 parse
|
|
|
|
$single = $single->parse("Content-Length: 12\x0d\x0a\x0d\x0aHello World!");
|
|
my $multi = $single->parse("Content-Type: multipart/form-data\x0d\x0a\x0d\x0a");
|
|
|
|
Parse content chunk and upgrade to L<Mojo::Content::MultiPart> object if necessary.
|
|
|
|
=head1 SEE ALSO
|
|
|
|
L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
|
|
|
|
=cut
|