Changeset 617

Show
Ignore:
Timestamp:
01/17/07 07:05:55 (2 years ago)
Author:
bradfitz
Message:

-- supported for "Transfer-Encoding: chunked" requests (HTTP/1.1 feature)

as well as the "Expect: 100-continue", which generally accompany
chunked requests. requires "buffered_uploads" be enabled. see
doc/http-versions.txt for details.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/CHANGES

    r613 r617  
     1    -- supported for "Transfer-Encoding: chunked" requests (HTTP/1.1 feature) 
     2       as well as the "Expect: 100-continue", which generally accompany 
     3       chunked requests.  requires "buffered_uploads" be enabled.  see 
     4       doc/http-versions.txt for details. 
     5 
    161.53: 2006-12-05 
    27 
  • trunk/MANIFEST

    r584 r617  
    77lib/Perlbal/BackendHTTP.pm 
    88lib/Perlbal/Cache.pm 
     9lib/Perlbal/ChunkedUploadState.pm 
    910lib/Perlbal/ClientHTTP.pm 
    1011lib/Perlbal/ClientHTTPBase.pm 
     
    3334META.yml                                 Module meta-data (added by MakeMaker) 
    3435devtools/gendocs.pl 
     36doc/http-versions.txt 
    3537doc/config-guide.txt 
    3638doc/service-parameters.txt 
     
    5961t/45-buffereduploads.t 
    6062t/50-plugins.t 
     63t/52-chunked-upload.t 
    6164 
  • trunk/META.yml

    r607 r617  
    22#XXXXXXX This is a prototype!!!  It will change in the future!!! XXXXX# 
    33name:         Perlbal 
    4 version:      1.52 
     4version:      1.53 
    55version_from: lib/Perlbal.pm 
    66installdirs:  site 
  • trunk/lib/Perlbal/ClientProxy.pm

    r612 r617  
    1010use base "Perlbal::ClientHTTPBase"; 
    1111no  warnings qw(deprecated); 
     12 
     13use Perlbal::ChunkedUploadState; 
    1214 
    1315use fields ( 
     
    3638            'backend_stalled',   # boolean:  if backend has shut off its reads because we're too slow. 
    3739            'unread_data_waiting',  # boolean:  if we shut off reads while we know data is yet to be read from client 
     40            'chunked_upload_state', # bool/obj:  if processing a chunked upload, Perlbal::ChunkedUploadState object, else undef 
     41            'request_body_length',  # integer:  request's body length, either as-declared, 
     42                                    #           or calculated after chunked upload is complete 
    3843 
    3944            # for perlbal sending out UDP packets related to upload status (for xmlhttprequest upload bar) 
     
    99104    $self->{buoutpos} = 0; 
    100105    $self->{bureason} = undef; 
     106    $self->{chunked_upload_state} = undef; 
     107    $self->{request_body_length} = undef; 
    101108 
    102109    $self->{reproxy_uris} = undef; 
     
    467474    $self->{bureason} = undef; 
    468475    $self->{upload_session} = undef; 
     476    $self->{chunked_upload_state} = undef; 
     477    $self->{request_body_length} = undef; 
    469478    return 1; 
    470479} 
     
    569578        print "  disabling reads.\n" if Perlbal::DEBUG >= 3; 
    570579        $self->watch_read(0); 
     580        return; 
     581    } 
     582 
     583    # deal with chunked uploads 
     584    if (my $cus = $self->{chunked_upload_state}) { 
     585        $cus->on_readable($self); 
     586 
     587        # if we got more than 1MB not flushed to disk, 
     588        # stop reading for a bit until disk catches up 
     589        if ($self->{read_ahead} > 1024*1024) { 
     590            $self->watch_read(0); 
     591        } 
    571592        return; 
    572593    } 
     
    713734    return if $svc->run_hook('start_http_request',  $self); 
    714735 
    715     # if defined we're waiting on some amount of data.  also, we have to 
    716     # subtract out read_size, which is the amount of data that was 
    717     # extra in the packet with the header that's part of the body. 
    718     $self->{content_length_remain} = $req_hd->content_length; 
    719     $self->{unread_data_waiting} = 1 if $self->{content_length_remain}; 
     736    if ($self->handle_chunked_upload) { 
     737        # handled in method. 
     738    } else { 
     739        # if defined we're waiting on some amount of data.  also, we have to 
     740        # subtract out read_size, which is the amount of data that was 
     741        # extra in the packet with the header that's part of the body. 
     742        $self->{request_body_length} = 
     743            $self->{content_length_remain} = 
     744            $req_hd->content_length; 
     745        $self->{unread_data_waiting} = 1 if $self->{content_length_remain}; 
     746    } 
    720747 
    721748    # upload-tracking stuff.  both starting a new upload track session, 
     
    729756    # either start buffering some of the request to memory, or 
    730757    # immediately request a backend connection. 
    731     if ($self->{content_length_remain} && $self->{service}->{buffer_backend_connect}) { 
     758    if ($self->{chunked_upload_state}) { 
     759        $self->{request_body_length} = 0; 
     760        $self->{is_buffering} = 1; 
     761        $self->{bureason} = 'chunked'; 
     762        $self->buffered_upload_update; 
     763    } elsif ($self->{content_length_remain} && $self->{service}->{buffer_backend_connect}) { 
    732764        # the deeper path 
    733765        $self->start_buffering_request; 
     
    741773        $self->request_backend; 
    742774    } 
     775} 
     776 
     777sub handle_chunked_upload { 
     778    my Perlbal::ClientProxy $self = shift; 
     779    my $req_hd = $self->{req_headers}; 
     780    my $te = $req_hd->header("Transfer-Encoding"); 
     781    return unless $te && $te eq "chunked"; 
     782    return unless $self->{service}->{buffer_uploads}; 
     783 
     784    $req_hd->header("Transfer-Encoding", undef); # remove it (won't go to backend) 
     785 
     786    # TODO: return false if we don't have buffered upload dir configured 
     787    my $eh = $req_hd->header("Expect"); 
     788    if ($eh && $eh =~ /\b100-continue\b/) { 
     789        $self->write(\ "HTTP/1.1 100 Continue\r\n\r\n"); 
     790        $req_hd->header("Expect", undef); # remove it (won't go to backend) 
     791    } 
     792 
     793    my $args = { 
     794        on_new_chunk => sub { 
     795            my $cref = shift; 
     796            my $len = length($$cref); 
     797            push @{$self->{read_buf}}, $cref; 
     798            $self->{read_ahead}          += $len; 
     799            $self->{request_body_length} += $len; 
     800            # TODO: if too large, disconnect? 
     801            $self->buffered_upload_update; 
     802        }, 
     803        on_disconnect => sub { 
     804            $self->client_disconnected; 
     805        }, 
     806        on_zero_chunk => sub { 
     807            $self->send_buffered_upload; 
     808        }, 
     809    }; 
     810 
     811    $self->{chunked_upload_state} = Perlbal::ChunkedUploadState->new(%$args); 
     812    return 1; 
    743813} 
    744814 
     
    915985 
    916986    # make sure our buoutpos is the same as the content length... 
     987    return if $self->{is_writing}; 
     988 
     989    # set the content-length that goes to the backend... 
     990    if ($self->{chunked_upload_state}) { 
     991        $self->{req_headers}->header("Content-Length", $self->{request_body_length}); 
     992    } 
     993 
    917994    my $clen = $self->{req_headers}->content_length; 
    918995    if ($clen != $self->{buoutpos}) { 
     
    9351012 
    9361013    # now send the data 
    937     my $clen = $self->{req_headers}->content_length; 
     1014    my $clen = $self->{request_body_length}; 
     1015 
    9381016    my $sent = Perlbal::Socket::sendfile($be->{fd}, fileno($self->{bufh}), $clen - $self->{buoutpos}); 
    9391017    if ($sent < 0) { 
     
    10171095        } 
    10181096 
     1097        # if we're processing a chunked upload, ... 
     1098        if ($self->{chunked_upload_state}) { 
     1099            # turn reads back on, if we haven't hit the end yet. 
     1100            if ($self->{unread_data_waiting} && $self->{read_ahead} < 1024*1024) { 
     1101                $self->watch_read(1); 
     1102                $self->{unread_data_waiting} = 0; 
     1103            } 
     1104 
     1105            if ($self->{read_ahead} == 0 && $self->{chunked_upload_state}->hit_zero_chunk) { 
     1106                $self->watch_read(0); 
     1107                $self->send_buffered_upload; 
     1108                return; 
     1109            } 
     1110        } 
     1111 
    10191112        # if we're done (no clr and no read ahead!) then send it 
    1020         if ($self->{read_ahead} <= 0 && $self->{content_length_remain} <= 0) { 
     1113        elsif ($self->{read_ahead} <= 0 && $self->{content_length_remain} <= 0) { 
    10211114            $self->send_buffered_upload; 
    10221115            return;