Merge pull request #23 from dsteinbrunner/patch-2
[perlbal.git] / lib / Perlbal / ChunkedUploadState.pm
blobbe1ec8a83f9dceb8668c88ae90ac70ee233983a5
1 package Perlbal::ChunkedUploadState;
2 use strict;
4 sub new {
5 my ($pkg, %args) = @_;
6 my $self = bless {
7 'buf' => '',
8 'bytes_remain' => 0, # remaining in chunk (ignoring final 2 byte CRLF)
9 }, $pkg;
10 foreach my $k (qw(on_new_chunk on_disconnect on_zero_chunk)) {
11 $self->{$k} = (delete $args{$k}) || sub {};
13 die "bogus args" if %args;
14 return $self;
17 sub on_readable {
18 my ($self, $ds) = @_;
19 my $rbuf = $ds->read(131072);
20 unless (defined $rbuf) {
21 $self->{on_disconnect}->();
22 return;
25 $self->{buf} .= $$rbuf;
27 while ($self->drive_machine) {}
30 # returns 1 if progress was made parsing buffer
31 sub drive_machine {
32 my $self = shift;
34 my $buflen = length($self->{buf});
35 return 0 unless $buflen;
37 if (my $br = $self->{bytes_remain}) {
38 my $extract = $buflen > $br ? $br : $buflen;
39 my $ch = substr($self->{buf}, 0, $extract, '');
40 $self->{bytes_remain} -= $extract;
41 die "assert" if $self->{bytes_remain} < 0;
42 $self->{on_new_chunk}->(\$ch);
43 return 1;
46 return 0 unless $self->{buf} =~ s/^(?:\r\n)?([0-9a-fA-F]+)(?:;.*)?\r\n//;
47 $self->{bytes_remain} = hex($1);
49 if ($self->{bytes_remain} == 0) {
50 # FIXME: new state machine state for trailer parsing/discarding.
51 # (before we do on_zero_chunk). for now, though, just assume
52 # no trailers and throw away the extra post-trailer \r\n that
53 # is probably in this packet. hacky.
54 $self->{buf} =~ s/^\r\n//;
55 $self->{hit_zero} = 1;
56 $self->{on_zero_chunk}->();
57 return 0;
59 return 1;
62 sub hit_zero_chunk { $_[0]{hit_zero} }