Skip to content

Commit

Permalink
improved resilience of HTTP parser
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Sep 26, 2011
1 parent 4494d01 commit a777d8d
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 44 deletions.
3 changes: 2 additions & 1 deletion Changes
@@ -1,6 +1,6 @@
This file documents the revision history for Perl extension Mojolicious.

1.99 2011-09-26 00:00:00
1.99 2011-09-27 00:00:00
- Deprecated direct hash access to the flash in
Mojolicious::Controller.
- Added EXPERIMENTAL group function to Mojolicious::Lite.
Expand All @@ -17,6 +17,7 @@ This file documents the revision history for Perl extension Mojolicious.
to be called as a class method.
- Improved documentation.
- Improved CSS of some built-in templates.
- Improved resilience of HTTP parser.
- Fixed CSS of built-in exception template.
- Fixed close event bug in Mojo::IOLoop.
- Fixed small redirect_to bug. (judofyr, sri)
Expand Down
60 changes: 30 additions & 30 deletions lib/Mojo/Content.pm
Expand Up @@ -75,15 +75,15 @@ sub generate_body_chunk {
my ($self, $offset) = @_;

# Callback
if (!delete $self->{delay} && !length $self->{b2}) {
if (!delete $self->{delay} && !length $self->{body_buffer}) {
my $cb = delete $self->{drain};
$self->$cb($offset) if $cb;
}

# Get chunk
my $chunk = $self->{b2};
my $chunk = $self->{body_buffer};
$chunk = '' unless defined $chunk;
$self->{b2} = '';
$self->{body_buffer} = '';

# EOF or delay
return $self->{eof} ? '' : undef unless length $chunk;
Expand All @@ -99,13 +99,13 @@ sub get_header_chunk {
my ($self, $offset) = @_;

# Normal headers
my $copy = $self->{b1} ||= $self->_build_headers;
my $copy = $self->{header_buffer} ||= $self->_build_headers;
return substr($copy, $offset, CHUNK_SIZE);
}

sub has_leftovers {
my $self = shift;
return 1 if length $self->{b2} || length $self->{b1};
return 1 if length $self->{buffer} || length $self->{pre_buffer};
return;
}

Expand Down Expand Up @@ -146,10 +146,10 @@ sub leftovers {

# Chunked leftovers are in the chunked buffer, and so are those from a
# HEAD request
return $self->{b1} if length $self->{b1};
return $self->{pre_buffer} if length $self->{pre_buffer};

# Normal leftovers
return $self->{b2};
return $self->{buffer};
}

sub parse {
Expand Down Expand Up @@ -181,19 +181,19 @@ sub parse {

# Not chunked, pass through to second buffer
else {
$self->{real_size} += length $self->{b1};
$self->{b2} .= $self->{b1};
$self->{b1} = '';
$self->{real_size} += length $self->{pre_buffer};
$self->{buffer} .= $self->{pre_buffer};
$self->{pre_buffer} = '';
}

# Custom body parser callback
if (my $cb = $self->on_read) {

# Chunked or relaxed content
if ($self->is_chunked || $self->relaxed) {
$self->{b2} = '' unless defined $self->{b2};
$self->$cb($self->{b2});
$self->{b2} = '';
$self->{buffer} = '' unless defined $self->{buffer};
$self->$cb($self->{buffer});
$self->{buffer} = '';
}

# Normal content
Expand All @@ -206,7 +206,7 @@ sub parse {

# Slurp
if ($need > 0) {
my $chunk = substr $self->{b2}, 0, $need, '';
my $chunk = substr $self->{buffer}, 0, $need, '';
$self->{size} = $self->{size} + length $chunk;
$self->$cb($chunk);
}
Expand Down Expand Up @@ -239,20 +239,20 @@ sub parse_until_body {
my ($self, $chunk) = @_;

# Prepare first buffer
$self->{b1} = '' unless defined $self->{b1};
$self->{raw_size} = 0 unless exists $self->{raw_size};
$self->{pre_buffer} = '' unless defined $self->{pre_buffer};
$self->{raw_size} = 0 unless exists $self->{raw_size};

# Add chunk
if (defined $chunk) {
$self->{raw_size} += length $chunk;
$self->{b1} .= $chunk;
$self->{pre_buffer} .= $chunk;
}

# Parser started
unless ($self->{state}) {

# Update size
$self->{header_size} = $self->{raw_size} - length $self->{b1};
$self->{header_size} = $self->{raw_size} - length $self->{pre_buffer};

# Headers
$self->{state} = 'headers';
Expand All @@ -277,8 +277,8 @@ sub write {

# Add chunk
if (defined $chunk) {
$self->{b2} = '' unless defined $self->{b2};
$self->{b2} .= $chunk;
$self->{body_buffer} = '' unless defined $self->{body_buffer};
$self->{body_buffer} .= $chunk;
}

# Delay
Expand Down Expand Up @@ -343,15 +343,15 @@ sub _parse_chunked {
}

# New chunk (ignore the chunk extension)
while ($self->{b1} =~ /^((?:\x0d?\x0a)?([\da-fA-F]+).*\x0d?\x0a)/) {
while ($self->{pre_buffer} =~ /^((?:\x0d?\x0a)?([\da-fA-F]+).*\x0d?\x0a)/) {
my $header = $1;
my $len = hex($2);

# Whole chunk
if (length($self->{b1}) >= (length($header) + $len)) {
if (length($self->{pre_buffer}) >= (length($header) + $len)) {

# Remove header
substr $self->{b1}, 0, length $header, '';
substr $self->{pre_buffer}, 0, length $header, '';

# Last chunk
if ($len == 0) {
Expand All @@ -361,10 +361,10 @@ sub _parse_chunked {

# Remove payload
$self->{real_size} += $len;
$self->{b2} .= substr $self->{b1}, 0, $len, '';
$self->{buffer} .= substr $self->{pre_buffer}, 0, $len, '';

# Remove newline at end of chunk
$self->{b1} =~ s/^(\x0d?\x0a)//;
$self->{pre_buffer} =~ s/^(\x0d?\x0a)//;
}

# Not a whole chunk, wait for more data
Expand All @@ -381,8 +381,8 @@ sub _parse_chunked_trailing_headers {

# Parse
my $headers = $self->headers;
$headers->parse($self->{b1});
$self->{b1} = '';
$headers->parse($self->{pre_buffer});
$self->{pre_buffer} = '';

# Done
if ($headers->is_done) {
Expand All @@ -405,14 +405,14 @@ sub _parse_headers {

# Parse
my $headers = $self->headers;
$headers->parse($self->{b1});
$self->{b1} = '';
$headers->parse($self->{pre_buffer});
$self->{pre_buffer} = '';

# Done
if ($headers->is_done) {
my $leftovers = $headers->leftovers;
$self->{header_size} = $self->{raw_size} - length $leftovers;
$self->{b1} = $leftovers;
$self->{pre_buffer} = $leftovers;
$self->{state} = 'body';
}
}
Expand Down
20 changes: 10 additions & 10 deletions lib/Mojo/Content/MultiPart.pm
Expand Up @@ -177,19 +177,19 @@ sub _parse_multipart_body {
my ($self, $boundary) = @_;

# Whole part in buffer
my $pos = index $self->{b2}, "\x0d\x0a--$boundary";
my $pos = index $self->{buffer}, "\x0d\x0a--$boundary";
if ($pos < 0) {
my $len = length($self->{b2}) - (length($boundary) + 8);
my $len = length($self->{buffer}) - (length($boundary) + 8);
return unless $len > 0;

# Store chunk
my $chunk = substr $self->{b2}, 0, $len, '';
my $chunk = substr $self->{buffer}, 0, $len, '';
$self->parts->[-1] = $self->parts->[-1]->parse($chunk);
return;
}

# Store chunk
my $chunk = substr $self->{b2}, 0, $pos, '';
my $chunk = substr $self->{buffer}, 0, $pos, '';
$self->parts->[-1] = $self->parts->[-1]->parse($chunk);
$self->{multi_state} = 'multipart_boundary';
return 1;
Expand All @@ -199,8 +199,8 @@ sub _parse_multipart_boundary {
my ($self, $boundary) = @_;

# Boundary begins
if ((index $self->{b2}, "\x0d\x0a--$boundary\x0d\x0a") == 0) {
substr $self->{b2}, 0, length($boundary) + 6, '';
if ((index $self->{buffer}, "\x0d\x0a--$boundary\x0d\x0a") == 0) {
substr $self->{buffer}, 0, length($boundary) + 6, '';

# New part
push @{$self->parts}, Mojo::Content::Single->new(relaxed => 1);
Expand All @@ -210,8 +210,8 @@ sub _parse_multipart_boundary {

# Boundary ends
my $end = "\x0d\x0a--$boundary--";
if ((index $self->{b2}, $end) == 0) {
substr $self->{b2}, 0, length $end, '';
if ((index $self->{buffer}, $end) == 0) {
substr $self->{buffer}, 0, length $end, '';

# Done
$self->{state} = $self->{multi_state} = 'done';
Expand All @@ -224,9 +224,9 @@ sub _parse_multipart_preamble {
my ($self, $boundary) = @_;

# Replace preamble with carriage return and line feed
my $pos = index $self->{b2}, "--$boundary";
my $pos = index $self->{buffer}, "--$boundary";
unless ($pos < 0) {
substr $self->{b2}, 0, $pos, "\x0d\x0a";
substr $self->{buffer}, 0, $pos, "\x0d\x0a";

# Parse boundary
$self->{multi_state} = 'multipart_boundary';
Expand Down
6 changes: 3 additions & 3 deletions lib/Mojo/Content/Single.pm
Expand Up @@ -65,8 +65,8 @@ sub parse {

# Chunked body or relaxed content
if ($self->is_chunked || $self->relaxed) {
$self->asset->add_chunk($self->{b2});
$self->{b2} = '';
$self->asset->add_chunk($self->{buffer});
$self->{buffer} = '';
}

# Normal body
Expand All @@ -76,7 +76,7 @@ sub parse {
my $len = $self->headers->content_length || 0;
my $asset = $self->asset;
my $need = $len - $asset->size;
$asset->add_chunk(substr $self->{b2}, 0, $need, '') if $need > 0;
$asset->add_chunk(substr $self->{buffer}, 0, $need, '') if $need > 0;

# Done
$self->{state} = 'done' if $len <= $self->progress;
Expand Down

0 comments on commit a777d8d

Please sign in to comment.