| 1 | package Perlbal::Plugin::MaxContentLength; |
|---|
| 2 | |
|---|
| 3 | =head1 NAME |
|---|
| 4 | |
|---|
| 5 | Perlbal::Plugin::MaxContentLength - Reject large requests |
|---|
| 6 | |
|---|
| 7 | =head1 SYNOPSIS |
|---|
| 8 | |
|---|
| 9 | LOAD MaxContentLength |
|---|
| 10 | CREATE SERVICE cgilike |
|---|
| 11 | # define a service... |
|---|
| 12 | SET max_content_length = 100000 |
|---|
| 13 | SET plugins = MaxContentLength |
|---|
| 14 | ENABLE cgilike |
|---|
| 15 | |
|---|
| 16 | =head1 DESCRIPTION |
|---|
| 17 | |
|---|
| 18 | This module rejects requests that are larger than a configured limit. If a |
|---|
| 19 | request bears a Content-Length header whose value exceeds the |
|---|
| 20 | max_content_length value, the request will be rejected with a 413 "Request |
|---|
| 21 | Entity Too Large" error. |
|---|
| 22 | |
|---|
| 23 | =head1 AUTHOR |
|---|
| 24 | |
|---|
| 25 | Adam Thomason, E<lt>athomason@sixapart.comE<gt> |
|---|
| 26 | |
|---|
| 27 | =head1 COPYRIGHT AND LICENSE |
|---|
| 28 | |
|---|
| 29 | Copyright 2008 Six Apart Ltd. |
|---|
| 30 | |
|---|
| 31 | This module is part of the Perlbal distribution, and as such can be distributed |
|---|
| 32 | under the same licence terms as the rest of Perlbal. |
|---|
| 33 | |
|---|
| 34 | =cut |
|---|
| 35 | |
|---|
| 36 | use strict; |
|---|
| 37 | use warnings; |
|---|
| 38 | |
|---|
| 39 | use Perlbal; |
|---|
| 40 | |
|---|
| 41 | sub load { |
|---|
| 42 | Perlbal::Service::add_tunable( |
|---|
| 43 | max_content_length => { |
|---|
| 44 | check_role => '*', |
|---|
| 45 | check_type => 'int', |
|---|
| 46 | des => "maximum Content-Length allowed, in bytes. 0 for no limit", |
|---|
| 47 | default => 0, |
|---|
| 48 | }, |
|---|
| 49 | ); |
|---|
| 50 | return 1; |
|---|
| 51 | } |
|---|
| 52 | |
|---|
| 53 | use constant HANDLE_REQUEST => 0; |
|---|
| 54 | use constant IGNORE_REQUEST => 1; |
|---|
| 55 | |
|---|
| 56 | sub register { |
|---|
| 57 | my ($class, $svc) = @_; |
|---|
| 58 | |
|---|
| 59 | my $cfg = $svc->{extra_config}; |
|---|
| 60 | return unless $cfg; |
|---|
| 61 | |
|---|
| 62 | $svc->register_hook('MaxContentLength', 'start_http_request' => sub { |
|---|
| 63 | my $client = shift; |
|---|
| 64 | return IGNORE_REQUEST unless $client; |
|---|
| 65 | |
|---|
| 66 | # allow request if max is disabled |
|---|
| 67 | return HANDLE_REQUEST unless $cfg->{max_content_length}; |
|---|
| 68 | |
|---|
| 69 | my $headers = $client->{req_headers}; |
|---|
| 70 | return HANDLE_REQUEST unless $headers; |
|---|
| 71 | |
|---|
| 72 | # allow requests which don't have a Content-Length header |
|---|
| 73 | my $length = $headers->header('content-length'); |
|---|
| 74 | return HANDLE_REQUEST unless $length; |
|---|
| 75 | |
|---|
| 76 | # allow requests under the cap |
|---|
| 77 | return HANDLE_REQUEST if $length <= $cfg->{max_content_length}; |
|---|
| 78 | |
|---|
| 79 | $client->send_response(413, "Content too long.\n"); |
|---|
| 80 | return IGNORE_REQUEST; |
|---|
| 81 | }); |
|---|
| 82 | } |
|---|
| 83 | |
|---|
| 84 | sub unregister { |
|---|
| 85 | my ($class, $svc) = @_; |
|---|
| 86 | |
|---|
| 87 | $svc->unregister_hooks('MaxContentLength'); |
|---|
| 88 | return 1; |
|---|
| 89 | } |
|---|
| 90 | |
|---|
| 91 | 1; |
|---|