Skip to content

Commit

Permalink
added auto_decompress attribute to Mojo::Content
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Oct 22, 2014
1 parent 7ca8325 commit f2fbc50
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 28 deletions.
1 change: 1 addition & 0 deletions Changes
@@ -1,5 +1,6 @@

5.54 2014-10-22
- Added auto_decompress attribute to Mojo::Content.
- Fixed bug where Mojo::UserAgent would try to follow redirects for
protocols other than HTTP and HTTPS.

Expand Down
59 changes: 33 additions & 26 deletions lib/Mojo/Content.pm
Expand Up @@ -5,7 +5,7 @@ use Carp 'croak';
use Compress::Raw::Zlib qw(WANT_GZIP Z_STREAM_END);
use Mojo::Headers;

has [qw(auto_relax expect_close relaxed skip_body)];
has [qw(auto_decompress auto_relax expect_close relaxed skip_body)];
has headers => sub { Mojo::Headers->new };
has max_buffer_size => sub { $ENV{MOJO_MAX_BUFFER_SIZE} || 262144 };
has max_leftover_size => sub { $ENV{MOJO_MAX_LEFTOVER_SIZE} || 262144 };
Expand Down Expand Up @@ -124,7 +124,7 @@ sub parse {
# Chunked or relaxed content
if ($self->is_chunked || $self->relaxed) {
$self->{size} += length($self->{buffer} //= '');
$self->_uncompress($self->{buffer});
$self->_decompress($self->{buffer});
$self->{buffer} = '';
}

Expand All @@ -134,7 +134,7 @@ sub parse {
if ((my $need = ($len ||= 0) - $self->{size}) > 0) {
my $len = length $self->{buffer};
my $chunk = substr $self->{buffer}, 0, $need > $len ? $len : $need, '';
$self->_uncompress($chunk);
$self->_decompress($chunk);
$self->{size} += length $chunk;
}
$self->{state} = 'finished' if $len <= $self->progress;
Expand Down Expand Up @@ -206,6 +206,29 @@ sub _build_chunk {
return $crlf . sprintf('%x', length $chunk) . "\x0d\x0a$chunk";
}

sub _decompress {
my ($self, $chunk) = @_;

# No compression
return $self->emit(read => $chunk)
unless $self->auto_decompress && $self->is_compressed;

# Decompress
$self->{post_buffer} .= $chunk;
my $gz = $self->{gz}
//= Compress::Raw::Zlib::Inflate->new(WindowBits => WANT_GZIP);
my $status = $gz->inflate(\$self->{post_buffer}, my $out);
$self->emit(read => $out) if defined $out;
# Replace Content-Encoding with Content-Length
$self->headers->content_length($gz->total_out)->remove('Content-Encoding')
if $status == Z_STREAM_END;
# Check buffer size
@$self{qw(state limit)} = ('finished', 1)
if length($self->{post_buffer} // '') > $self->max_buffer_size;
}

sub _parse_chunked {
my $self = shift;

Expand Down Expand Up @@ -280,29 +303,6 @@ sub _parse_until_body {
$self->_parse_headers if ($self->{state} // '') eq 'headers';
}

sub _uncompress {
my ($self, $chunk) = @_;

# No compression
return $self->emit(read => $chunk)
unless $self->is_compressed && $self->auto_relax;

# Uncompress
$self->{post_buffer} .= $chunk;
my $gz = $self->{gz}
//= Compress::Raw::Zlib::Inflate->new(WindowBits => WANT_GZIP);
my $status = $gz->inflate(\$self->{post_buffer}, my $out);
$self->emit(read => $out) if defined $out;
# Replace Content-Encoding with Content-Length
$self->headers->content_length($gz->total_out)->remove('Content-Encoding')
if $status == Z_STREAM_END;
# Check buffer size
@$self{qw(state limit)} = ('finished', 1)
if length($self->{post_buffer} // '') > $self->max_buffer_size;
}

1;

=encoding utf8
Expand Down Expand Up @@ -378,6 +378,13 @@ Emitted when a new chunk of content arrives.
L<Mojo::Content> implements the following attributes.
=head2 auto_decompress
my $bool = $content->auto_decompress;
$content = $content->auto_decompress($bool);
Decompress content automatically if L</"is_compressed"> is true.
=head2 auto_relax
my $bool = $content->auto_relax;
Expand Down
2 changes: 1 addition & 1 deletion lib/Mojo/Message/Response.pm
Expand Up @@ -99,7 +99,7 @@ sub extract_start_line {

my $content = $self->content;
$content->skip_body(1) if $self->code($2)->is_empty;
$content->auto_relax(1) unless defined $content->auto_relax;
defined $content->$_ or $content->$_(1) for qw(auto_decompress auto_relax);
$content->expect_close(1) if $1 eq '1.0';
return !!$self->version($1)->message($3);
}
Expand Down
16 changes: 16 additions & 0 deletions t/mojo/user_agent.t
Expand Up @@ -345,6 +345,22 @@ ok !$tx->success, 'not successful';
is $tx->error->{message}, 'Not Found', 'right error';
is $tx->error->{code}, 404, 'right status';

# Compressed response
$tx = $ua->build_tx(GET => '/echo' => 'Hello GZip!');
$tx = $ua->start($ua->build_tx(GET => '/echo' => 'Hello GZip!'));
ok $tx->success, 'successful';
is $tx->res->code, 200, 'right status';
is $tx->res->headers->content_encoding, undef, 'no "Content-Encoding" value';
is $tx->res->body, 'Hello GZip!', 'right content';
$tx = $ua->build_tx(GET => '/echo' => 'Hello GZip!');
$tx->res->content->auto_decompress(0);
$tx = $ua->start($tx);
ok $tx->success, 'successful';
is $tx->res->code, 200, 'right status';
is $tx->res->headers->content_encoding, 'gzip',
'right "Content-Encoding" value';
isnt $tx->res->body, 'Hello GZip!', 'different content';

# Fork safety
$tx = $ua->get('/');
is $tx->res->body, 'works!', 'right content';
Expand Down
2 changes: 1 addition & 1 deletion t/mojolicious/websocket_lite_app.t
Expand Up @@ -189,7 +189,7 @@ $t->message_ok->message_is({binary => 'a' x 50000});
ok length $payload < 262145, 'message has been compressed';
$t->finish_ok->finished_ok(1005);

# Compressed message exceeding the limit when uncompressed
# Compressed message exceeding the limit when decompressed
$t->websocket_ok(
'/echo' => {'Sec-WebSocket-Extensions' => 'permessage-deflate'})
->header_is('Sec-WebSocket-Extensions' => 'permessage-deflate')
Expand Down

0 comments on commit f2fbc50

Please sign in to comment.