created HTTP::Body::Urlencoded
Christian Hansen [Tue, 19 Jul 2005 17:43:38 +0000 (17:43 +0000)]
lib/HTTP/Body.pm
lib/HTTP/Body/Multipart.pm
lib/HTTP/Body/Octetstream.pm
lib/HTTP/Body/Urlencoded.pm
test.pl

index b8d2c92..e89cb20 100644 (file)
@@ -5,7 +5,7 @@ use strict;
 use Carp       qw[ ];
 use List::Util qw[ first ];
 
-our $PARSERS = {
+our $TYPES = {
     'application/octet-stream'          => 'HTTP::Body::Octetstream',
     'application/x-www-form-urlencoded' => 'HTTP::Body::Urlencoded',
     'multipart/form-data'               => 'HTTP::Body::Multipart'
@@ -17,40 +17,43 @@ sub new {
     unless ( @_ == 3 ) {
         Carp::croak( $class, '->new( $content_type, $content_length )' );
     }
-    
-    my $type = first { index( lc($content_type), $_ ) >= 0 } keys %{ $PARSERS };
-    my $body = $PARSERS->{ $type || 'application/octet-stream' };
-    
+
+    my $type = first { index( lc($content_type), $_ ) >= 0 } keys %{$TYPES};
+    my $body = $TYPES->{ $type || 'application/octet-stream' };
+
     eval "require $body";
-    
-    if ( $@ ) {
+
+    if ($@) {
         die $@;
     }
-    
+
     my $self = {
         buffer         => '',
         content_length => $content_length,
         content_type   => $content_type,
         length         => 0,
-        param          => { },
-        upload         => { }
+        param          => {},
+        state          => 'buffering',
+        upload         => {}
     };
 
     bless( $self, $body );
-    
+
     return $self->init;
 }
 
 sub add {
     my $self = shift;
-    
+
     if ( defined $_[0] ) {
         $self->{buffer} .= $_[0];
-        $self->{length} += length($_[0]);
+        $self->{length} += length( $_[0] );
     }
     
-    $self->spin;
-    
+    unless ( $self->state eq 'done' ) {
+        $self->spin;
+    }
+
     return ( $self->length - $self->content_length );
 }
 
@@ -84,6 +87,12 @@ sub spin {
     Carp::croak('Define abstract method spin() in implementation');
 }
 
+sub state {
+    my $self = shift;
+    $self->{state} = shift if @_;
+    return $self->{state};    
+}
+
 sub param {
     my $self = shift;
 
index c042903..63b8d86 100644 (file)
@@ -25,11 +25,7 @@ sub spin {
 
     while (1) {
 
-        if ( $self->{state} eq 'done' ) {
-            return 0;
-        }
-
-        elsif ( $self->{state} =~ /^(preamble|boundary|header|body)$/ ) {
+        if ( $self->{state} =~ /^(preamble|boundary|header|body)$/ ) {
             my $method = "parse_$1";
             return unless $self->$method;
         }
@@ -192,6 +188,7 @@ sub parse_body {
 sub handler {
     my ( $self, $part ) = @_;
 
+    # skip parts without content
     if ( $part->{done} && $part->{size} == 0 ) {
         return 0;
     }
index 8f14f85..f277c62 100644 (file)
@@ -8,7 +8,7 @@ use File::Temp 0.14;
 
 sub spin {
     my $self = shift;
-    
+
     unless ( $self->body ) {
         $self->body( File::Temp->new );
     }
@@ -19,6 +19,7 @@ sub spin {
     
     if ( $self->length == $self->content_length ) {
         seek( $self->body, 0, 0 );
+        $self->state('done');
     }
 }
 
index c01b9b2..db72930 100644 (file)
@@ -4,10 +4,30 @@ use strict;
 use base 'HTTP::Body';
 use bytes;
 
+our $DECODE = qr/%(u?[0-9a-fA-F]{2,4})/;
+
 sub spin {
     my $self = shift;
     
+    return unless $self->length == $self->content_length;
+
+    for my $pair ( split( /[&;]/, $self->{buffer} ) ) {
+        
+        my ( $name, $value ) = split( /=/, $pair );
+        
+        next unless defined $name;
+        next unless defined $value;
+        
+        $name  =~ s/$DECODE/chr(hex($1))/eg;
+        $name  =~ tr/+/ /;
+        $value =~ s/$DECODE/chr(hex($1))/eg;
+        $value =~ tr/+/ /;
+        
+        $self->param( $name, $value );
+    }
     
+    $self->{state}  = 'done';
+    $self->{buffer} = ''
 }
 
 1;
diff --git a/test.pl b/test.pl
index 2142c6a..6f3027b 100644 (file)
--- a/test.pl
+++ b/test.pl
@@ -9,10 +9,12 @@ use HTTP::Body;
 use IO::File;
 use YAML qw[LoadFile];
 
-my $test = shift(@ARGV) || 1;
+my $number = $ARGV[0] || 1;
+my $test   = $ARGV[1] || 'multipart';
 
-my $headers = LoadFile( sprintf( "t/data/multipart/%.3d-headers.yml", $test ) );
-my $content = IO::File->new( sprintf( "t/data/multipart/%.3d-content.dat", $test ), O_RDONLY );
+
+my $headers = LoadFile( sprintf( "t/data/%s/%.3d-headers.yml", $test, $number ) );
+my $content = IO::File->new( sprintf( "t/data/%s/%.3d-content.dat", $test, $number ), O_RDONLY );
 my $body    = HTTP::Body->new( $headers->{'Content-Type'}, $headers->{'Content-Length'} );
 
 binmode $content;
@@ -25,7 +27,8 @@ warn Dumper( $body->param  );
 warn Dumper( $body->upload );
 warn Dumper( $body->body   );
 
+warn "state          : " . $body->state;
 warn "length         : " . $body->length;
 warn "content length : " . $body->content_length;
-warn "state          : " . $body->{state};
-warn "buffer         : " . $body->buffer;
+warn "body length    : " . ( $body->body->stat )[7] if $body->body;
+warn "buffer         : " . $body->buffer if $body->buffer;