Skip to content

Commit

Permalink
added max_lines attribute to Mojo::Headers
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Jan 11, 2015
1 parent 8cb57c5 commit 8e91547
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 34 deletions.
1 change: 1 addition & 0 deletions Changes
Expand Up @@ -2,6 +2,7 @@
5.72 2015-01-11
- Added EXPERIMENTAL support for case-insensitive attribute selectors like
[foo="bar" i] to Mojo::DOM::CSS.
- Added max_lines attribute to Mojo::Headers.
- Improved Mojo::Reactor::EV to update the current time before starting a
timer.
- Improved error messages for start-line and header limits.
Expand Down
21 changes: 15 additions & 6 deletions lib/Mojo/Headers.pm
Expand Up @@ -4,6 +4,7 @@ use Mojo::Base -base;
use Mojo::Util 'monkey_patch';

has max_line_size => sub { $ENV{MOJO_MAX_LINE_SIZE} || 10240 };
has max_lines => sub { $ENV{MOJO_MAX_LINES} || 128 };

# Common headers
my %NORMALCASE = map { lc($_) => $_ } (
Expand Down Expand Up @@ -85,12 +86,13 @@ sub parse {
$self->{state} = 'headers';
$self->{buffer} .= shift // '';
my $headers = $self->{cache} ||= [];
my $max = $self->max_line_size;
my $size = $self->max_line_size;
my $lines = $self->max_lines;
while ($self->{buffer} =~ s/^(.*?)\x0d?\x0a//) {
my $line = $1;

# Check line size limit
if (($self->{size} += $+[0]) > $max) {
if ($+[0] > $size || @$headers >= $lines) {
@$self{qw(state limit)} = ('finished', 1);
return $self;
}
Expand All @@ -110,8 +112,7 @@ sub parse {
}

# Check line size limit
@$self{qw(state limit)} = ('finished', 1)
if (($self->{size} ||= 0) + length $self->{buffer}) > $max;
@$self{qw(state limit)} = ('finished', 1) if length $self->{buffer} > $size;

return $self;
}
Expand Down Expand Up @@ -182,8 +183,16 @@ L<Mojo::Headers> implements the following attributes.
my $size = $headers->max_line_size;
$headers = $headers->max_line_size(1024);
Maximum size of all header lines combined in bytes, defaults to the value of
the C<MOJO_MAX_LINE_SIZE> environment variable or C<10240> (10KB).
Maximum header line size in bytes, defaults to the value of the
C<MOJO_MAX_LINE_SIZE> environment variable or C<10240> (10KB).
=head2 max_lines
my $num = $headers->max_lines;
$headers = $headers->max_lines(256);
Maximum number of header lines, defaults to the value of the C<MOJO_MAX_LINES>
environment variable or C<128>.
=head1 METHODS
Expand Down
20 changes: 10 additions & 10 deletions lib/Mojolicious/Guides/FAQ.pod
Expand Up @@ -135,26 +135,26 @@ In L<Mojolicious> this event loop is L<Mojo::IOLoop>.

To protect your applications from excessively large requests and responses,
our HTTP parser has a cap after which it will automatically stop accepting new
data, and in most cases force the connection to be closed. This limit is
around 10MB by default, you can use the attribute
L<Mojo::Message/"max_message_size"> or C<MOJO_MAX_MESSAGE_SIZE> environment
variable to change this value.
data, and in most cases force the connection to be closed. This limit is 10MB
by default, you can use the attribute L<Mojo::Message/"max_message_size"> or
C<MOJO_MAX_MESSAGE_SIZE> environment variable to change this value.

=head2 What does the error "Maximum start-line size exceeded" mean?

This is a very similar protection mechanism to the one described in the
previous answer, but a little more specific. It limits the maximum length of
the start-line for HTTP requests and responses. This limit is around 10KB by
default, you can use the attribute L<Mojo::Message/"max_line_size"> or
the start-line for HTTP requests and responses. This limit is 10KB by default,
you can use the attribute L<Mojo::Message/"max_line_size"> or
C<MOJO_MAX_LINE_SIZE> environment variable to change this value.

=head2 What does the error "Maximum header size exceeded" mean?

Almost the same as the previous answer, but this protection mechanism limits
the maximum length of the HTTP request and response headers. This limit is
around 10KB by default, you can use the attribute
L<Mojo::Headers/"max_line_size"> or C<MOJO_MAX_LINE_SIZE> environment variable
to change this value.
the number and maximum length of HTTP request and response headers. The limits
are 128 headers with 10KB each by default, you can use the attributes
L<Mojo::Headers/"max_lines"> and L<Mojo::Headers/"max_line_size"> or the
C<MOJO_MAX_LINES> and C<MOJO_MAX_LINE_SIZE> environment variables to change
these values.

=head2 What does the error "Maximum buffer size exceeded" mean?

Expand Down
37 changes: 19 additions & 18 deletions t/mojo/request.t
Expand Up @@ -72,23 +72,6 @@ is $req->version, '1.1', 'right version';
is $req->url, '/', 'right URL';
is $req->body, 'a=b; ' x 131072, 'right content';

# Parse HTTP 1.1 message with headers combined exceeding line limit
$req = Mojo::Message::Request->new;
is $req->headers->max_line_size, 10240, 'right size';
$req->parse("GET / HTTP/1.1\x0d\x0a");
$req->parse("Foo: @{['a' x 3413]}\x0d\x0a");
ok !$req->is_limit_exceeded, 'limit is not exceeded';
$req->parse("Bar: @{['b' x 3413]}\x0d\x0a");
ok !$req->is_limit_exceeded, 'limit is not exceeded';
$req->parse("Baz: @{['c' x 3413]}\x0d\x0a\x0d\x0a");
ok $req->is_finished, 'request is finished';
is $req->error->{message}, 'Maximum header size exceeded', 'right error';
is $req->error->{advice}, 431, 'right advice';
ok $req->is_limit_exceeded, 'limit is exceeded';
is $req->method, 'GET', 'right method';
is $req->version, '1.1', 'right version';
is $req->url, '/', 'right URL';

# Parse broken start-line
$req = Mojo::Message::Request->new;
$req->parse("12345\x0d\x0a");
Expand All @@ -102,7 +85,7 @@ $req = Mojo::Message::Request->new;
$req->parse("GET / HTTP/1.1\x0d\x0a");
$req->parse("Content-Length: 0\x0d\x0a");
ok !$req->is_limit_exceeded, 'limit is not exceeded';
$req->parse("Foo: @{['a' x 10220]}");
$req->parse("Foo: @{['a' x 10240]}");
ok $req->is_finished, 'request is finished';
is $req->error->{message}, 'Maximum header size exceeded', 'right error';
is $req->error->{advice}, 431, 'right advice';
Expand Down Expand Up @@ -430,6 +413,24 @@ is $req->headers->content_length, undef, 'no "Content-Length" value';
ok $req->is_limit_exceeded, 'limit is exceeded';
}

# Parse HTTP 1.1 message with headers exceeding line limit
{
local $ENV{MOJO_MAX_LINES} = 5;
$req = Mojo::Message::Request->new;
is $req->headers->max_lines, 5, 'right number';
$req->parse("GET / HTTP/1.1\x0d\x0a");
$req->parse("A: a\x0d\x0aB: b\x0d\x0aC: c\x0d\x0aD: d\x0d\x0a");
ok !$req->is_limit_exceeded, 'limit is not exceeded';
$req->parse("D: d\x0d\x0a\x0d\x0a");
ok $req->is_finished, 'request is finished';
is $req->error->{message}, 'Maximum header size exceeded', 'right error';
is $req->error->{advice}, 431, 'right advice';
ok $req->is_limit_exceeded, 'limit is exceeded';
is $req->method, 'GET', 'right method';
is $req->version, '1.1', 'right version';
is $req->url, '/', 'right URL';
}

# Parse full HTTP 1.0 request (solitary LF)
$req = Mojo::Message::Request->new;
my $body = '';
Expand Down

0 comments on commit 8e91547

Please sign in to comment.