From 1b2e27ea25e78c09577e544c53e2cc681e884a0d Mon Sep 17 00:00:00 2001 From: Tatsuhiko Miyagawa Date: Fri, 29 Jan 2010 03:06:13 -0800 Subject: [PATCH] Use TempBuffer in Dechunk middleware. TempBuffer now added Auto backend, which begins with PerlIO but switches to File when the size exceeds the max limit, which may have a little overhead but suitable for chunked requests. --- lib/Plack/Middleware/Dechunk.pm | 17 +++++++++-------- lib/Plack/TempBuffer.pm | 25 ++++++++++++++++++++---- lib/Plack/TempBuffer/Auto.pm | 34 +++++++++++++++++++++++++++++++++ lib/Plack/TempBuffer/File.pm | 6 ++++++ lib/Plack/TempBuffer/PerlIO.pm | 5 +++++ t/Plack-TempBuffer/print.t | 16 +++++++++++++++- 6 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 lib/Plack/TempBuffer/Auto.pm diff --git a/lib/Plack/Middleware/Dechunk.pm b/lib/Plack/Middleware/Dechunk.pm index 8085bdf6b..718a0473d 100644 --- a/lib/Plack/Middleware/Dechunk.pm +++ b/lib/Plack/Middleware/Dechunk.pm @@ -1,13 +1,13 @@ package Plack::Middleware::Dechunk; use strict; -no warnings; use parent qw(Plack::Middleware); -use constant CHUNK_SIZE => 1024;# * 32; +use Plack::TempBuffer; +use constant CHUNK_SIZE => 1024 * 32; sub call { my($self, $env) = @_; - + no warnings; if ( $env->{HTTP_TRANSFER_ENCODING} eq 'chunked' && ($env->{REQUEST_METHOD} eq 'POST' || $env->{REQUEST_METHOD} eq 'PUT')) { $self->dechunk_input($env); @@ -19,18 +19,19 @@ sub call { sub dechunk_input { my($self, $env) = @_; - my $chunk_buffer = ''; - my($body, $length); + my $buffer = Plack::TempBuffer->new; + my $chunk_buffer; + my $length; DECHUNK: while (1) { - my $read = $env->{'psgi.input'}->read($chunk_buffer, CHUNK_SIZE, length $chunk_buffer); + my $read = $env->{'psgi.input'}->read(my $chunk_buffer, CHUNK_SIZE, length $chunk_buffer); while ( $chunk_buffer =~ s/^([0-9a-fA-F]+).*\015\012// ) { my $chunk_len = hex $1; last DECHUNK if $chunk_len == 0; - $body .= substr $chunk_buffer, 0, $chunk_len, ''; + $buffer->print(substr $chunk_buffer, 0, $chunk_len, ''); $chunk_buffer =~ s/^\015\012//; $length += $chunk_len; @@ -41,7 +42,7 @@ sub dechunk_input { delete $env->{HTTP_TRANSFER_ENCODING}; $env->{CONTENT_LENGTH} = $length; - $env->{'psgi.input'} = do { open my $input, "<", \$body; $input }; + $env->{'psgi.input'} = $buffer->rewind; } 1; diff --git a/lib/Plack/TempBuffer.pm b/lib/Plack/TempBuffer.pm index f5bd21841..7b6912847 100644 --- a/lib/Plack/TempBuffer.pm +++ b/lib/Plack/TempBuffer.pm @@ -11,15 +11,30 @@ sub new { # $MaxMemoryBufferSize = 0 -> Always temp file # $MaxMemoryBufferSize = -1 -> Always PerlIO - if ($length && $MaxMemoryBufferSize >= 0 && $length > $MaxMemoryBufferSize) { - Plack::Util::load_class('File', $class)->new($length); + my $backend; + if ($MaxMemoryBufferSize < 0) { + $backend = "PerlIO"; + } elsif ($MaxMemoryBufferSize == 0) { + $backend = "File"; + } elsif (!$length) { + $backend = "Auto"; + } elsif ($length > $MaxMemoryBufferSize) { + $backend = "File"; } else { - Plack::Util::load_class('PerlIO', $class)->new; + $backend = "PerlIO"; } + + $class->create($backend, $length, $MaxMemoryBufferSize); +} + +sub create { + my($class, $backend, $length, $max) = @_; + Plack::Util::load_class($backend, $class)->new($length, $max); } sub print; sub rewind; +sub size; 1; @@ -33,7 +48,9 @@ Plack::TempBuffer - temporary buffer to save bytes my $buf = Plack::TempBuffer->new($length); $buf->print($bytes); - my $fh = $buf->rewind; + + my $size = $buf->size; + my $fh = $buf->rewind; =head1 DESCRIPTION diff --git a/lib/Plack/TempBuffer/Auto.pm b/lib/Plack/TempBuffer/Auto.pm new file mode 100644 index 000000000..9ef9653ab --- /dev/null +++ b/lib/Plack/TempBuffer/Auto.pm @@ -0,0 +1,34 @@ +package Plack::TempBuffer::Auto; +use strict; +use parent 'Plack::TempBuffer'; + +sub new { + my($class, undef, $max_memory_size) = @_; + bless { + _buffer => Plack::TempBuffer->create('PerlIO'), + _max => $max_memory_size, + }, $class; +} + +sub print { + my $self = shift; + $self->{_buffer}->print(@_); + + if ($self->{_buffer}->size > $self->{_max}) { + my $buf = $self->{_buffer}->{buffer}; + $self->{_buffer} = Plack::TempBuffer->create('File'), + $self->{_buffer}->print($buf); + } +} + +sub size { + my $self = shift; + $self->{_buffer}->size; +} + +sub rewind { + my $self = shift; + $self->{_buffer}->rewind; +} + +1; diff --git a/lib/Plack/TempBuffer/File.pm b/lib/Plack/TempBuffer/File.pm index 363e53caf..6b5f294a7 100644 --- a/lib/Plack/TempBuffer/File.pm +++ b/lib/Plack/TempBuffer/File.pm @@ -18,6 +18,12 @@ sub print { $self->{fh}->print(@_); } +sub size { + my $self = shift; + $self->{fh}->flush; + -s $self->{fh}; +} + sub rewind { my $self = shift; $self->{fh}->seek(0, 0); diff --git a/lib/Plack/TempBuffer/PerlIO.pm b/lib/Plack/TempBuffer/PerlIO.pm index 4794928bd..864b0c6bc 100644 --- a/lib/Plack/TempBuffer/PerlIO.pm +++ b/lib/Plack/TempBuffer/PerlIO.pm @@ -12,6 +12,11 @@ sub print { $self->{buffer} .= "@_"; } +sub size { + my $self = shift; + length $self->{buffer}; +} + sub rewind { my $self = shift; my $buffer = $self->{buffer}; diff --git a/t/Plack-TempBuffer/print.t b/t/Plack-TempBuffer/print.t index f62af8b82..586739f2f 100644 --- a/t/Plack-TempBuffer/print.t +++ b/t/Plack-TempBuffer/print.t @@ -3,18 +3,32 @@ use Test::More; use Plack::TempBuffer; { - my $b = Plack::TempBuffer->new; + my $b = Plack::TempBuffer->new(-1); $b->print("foo"); + is $b->size, 3; my $fh = $b->rewind; is do { local $/; <$fh> }, 'foo'; $fh->seek(0, 0); } +{ + local $Plack::TempBuffer::MaxMemoryBufferSize = 12; + my $b = Plack::TempBuffer->new; + is $b->size, 0; + $b->print("foo") for 1..5; + is $b->size, 15; + my $fh = $b->rewind; + isa_ok $fh, 'IO::File'; + is do { local $/; <$fh> }, ('foo' x 5); +} + { local $Plack::TempBuffer::MaxMemoryBufferSize = 0; my $b = Plack::TempBuffer->new(3); $b->print("foo\n"); + is $b->size, 4; my $fh = $b->rewind; + isa_ok $fh, 'IO::File'; is do { local $/; <$fh> }, "foo\n"; }