X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FHTTP%2FBody.pm;h=3025a8879f5d75cd4980c39eeebbb7aad763ba76;hb=0a66fd2366e29e961fe3ee608a83f86068517c92;hp=4dd8f9f9a5ea937b7795f58665158cc20d2d018c;hpb=807be76d0cc3edc476c5242d8895044611081f48;p=catagits%2FHTTP-Body.git diff --git a/lib/HTTP/Body.pm b/lib/HTTP/Body.pm index 4dd8f9f..3025a88 100644 --- a/lib/HTTP/Body.pm +++ b/lib/HTTP/Body.pm @@ -4,7 +4,7 @@ use strict; use Carp qw[ ]; -our $VERSION = 0.91; +our $VERSION = 1.00; our $TYPES = { 'application/octet-stream' => 'HTTP::Body::OctetStream', @@ -16,6 +16,9 @@ require HTTP::Body::OctetStream; require HTTP::Body::UrlEncoded; require HTTP::Body::MultiPart; +use HTTP::Headers; +use HTTP::Message; + =head1 NAME HTTP::Body - HTTP Body Parser @@ -53,6 +56,8 @@ HTTP::Body parses chunks of HTTP POST data and supports application/octet-stream, application/x-www-form-urlencoded, and multipart/form-data. +Chunked bodies are supported by not passing a length value to new(). + It is currently used by L to parse POST bodies. =head1 METHODS @@ -69,8 +74,8 @@ returns a L object. sub new { my ( $class, $content_type, $content_length ) = @_; - unless ( @_ == 3 ) { - Carp::croak( $class, '->new( $content_type, $content_length )' ); + unless ( @_ >= 2 ) { + Carp::croak( $class, '->new( $content_type, [ $content_length ] )' ); } my $type; @@ -90,8 +95,10 @@ sub new { my $self = { buffer => '', + chunk_buffer => '', body => undef, - content_length => $content_length, + chunked => !defined $content_length, + content_length => defined $content_length ? $content_length : -1, content_type => $content_type, length => 0, param => {}, @@ -114,6 +121,57 @@ length before adding self. sub add { my $self = shift; + if ( $self->{chunked} ) { + $self->{chunk_buffer} .= $_[0]; + + while ( $self->{chunk_buffer} =~ m/^([\da-fA-F]+).*\x0D\x0A/ ) { + my $chunk_len = hex($1); + + if ( $chunk_len == 0 ) { + # Strip chunk len + $self->{chunk_buffer} =~ s/^([\da-fA-F]+).*\x0D\x0A//; + + # End of data, there may be trailing headers + if ( my ($headers) = $self->{chunk_buffer} =~ m/(.*)\x0D\x0A/s ) { + if ( my $message = HTTP::Message->parse( $headers ) ) { + $self->{trailing_headers} = $message->headers; + } + } + + $self->{chunk_buffer} = ''; + + # Set content_length equal to the amount of data we read, + # so the spin methods can finish up. + $self->{content_length} = $self->{length}; + } + else { + # Make sure we have the whole chunk in the buffer (+CRLF) + if ( length( $self->{chunk_buffer} ) >= $chunk_len ) { + # Strip chunk len + $self->{chunk_buffer} =~ s/^([\da-fA-F]+).*\x0D\x0A//; + + # Pull chunk data out of chunk buffer into real buffer + $self->{buffer} .= substr $self->{chunk_buffer}, 0, $chunk_len, ''; + + # Strip remaining CRLF + $self->{chunk_buffer} =~ s/^\x0D\x0A//; + + $self->{length} += $chunk_len; + } + else { + # Not enough data for this chunk, wait for more calls to add() + return; + } + } + + unless ( $self->{state} eq 'done' ) { + $self->spin; + } + } + + return; + } + my $cl = $self->content_length; if ( defined $_[0] ) { @@ -147,19 +205,20 @@ sub body { return $self->{body}; } -=item buffer +=item chunked -read only accessor for the buffer. +Returns 1 if the request is chunked. =cut -sub buffer { - return shift->{buffer}; +sub chunked { + return shift->{chunked}; } =item content_length -read only accessor for content length +Returns the content-length for the body data if known. +Returns -1 if the request is chunked. =cut @@ -169,7 +228,7 @@ sub content_length { =item content_type -read only accessor for the content type +Returns the content-type of the body data. =cut @@ -189,7 +248,9 @@ sub init { =item length -read only accessor for body length. +Returns the total length of data we expect to read if known. +In the case of a chunked request, returns the amount of data +read so far. =cut @@ -197,6 +258,17 @@ sub length { return shift->{length}; } +=item trailing_headers + +If a chunked request body had trailing headers, trailing_headers will +return an HTTP::Headers object populated with those headers. + +=cut + +sub trailing_headers { + return shift->{trailing_headers}; +} + =item spin Abstract method to spin the io handle. @@ -209,7 +281,7 @@ sub spin { =item state -accessor for body state. +Returns the current state of the parser. =cut @@ -221,7 +293,7 @@ sub state { =item param -accesor for http parameters. +Get/set body parameters. =cut @@ -248,6 +320,8 @@ sub param { =item upload +Get/set file uploads. + =cut sub upload { @@ -273,16 +347,14 @@ sub upload { =back -=head1 BUGS - -Chunked requests are currently not supported. - =head1 AUTHOR Christian Hansen, C Sebastian Riedel, C +Andy Grundman, C + =head1 LICENSE This library is free software. You can redistribute it and/or modify