Home | History | Annotate | Download | only in http
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/http/http_response_body_drainer.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/logging.h"
      9 #include "net/base/io_buffer.h"
     10 #include "net/base/net_errors.h"
     11 #include "net/http/http_network_session.h"
     12 #include "net/http/http_stream.h"
     13 
     14 namespace net {
     15 
     16 HttpResponseBodyDrainer::HttpResponseBodyDrainer(HttpStream* stream)
     17     : read_size_(0),
     18       stream_(stream),
     19       next_state_(STATE_NONE),
     20       total_read_(0),
     21       session_(NULL) {}
     22 
     23 HttpResponseBodyDrainer::~HttpResponseBodyDrainer() {}
     24 
     25 void HttpResponseBodyDrainer::Start(HttpNetworkSession* session) {
     26   StartWithSize(session, kDrainBodyBufferSize);
     27 }
     28 
     29 void HttpResponseBodyDrainer::StartWithSize(HttpNetworkSession* session,
     30                                             int num_bytes_to_drain) {
     31   DCHECK_LE(0, num_bytes_to_drain);
     32   // TODO(simonjam): Consider raising this limit if we're pipelining. If we have
     33   // a bunch of responses in the pipeline, we should be less willing to give up
     34   // while draining.
     35   if (num_bytes_to_drain > kDrainBodyBufferSize) {
     36     Finish(ERR_RESPONSE_BODY_TOO_BIG_TO_DRAIN);
     37     return;
     38   } else if (num_bytes_to_drain == 0) {
     39     Finish(OK);
     40     return;
     41   }
     42 
     43   read_size_ = num_bytes_to_drain;
     44   read_buf_ = new IOBuffer(read_size_);
     45   next_state_ = STATE_DRAIN_RESPONSE_BODY;
     46   int rv = DoLoop(OK);
     47 
     48   if (rv == ERR_IO_PENDING) {
     49     timer_.Start(FROM_HERE,
     50                  base::TimeDelta::FromSeconds(kTimeoutInSeconds),
     51                  this,
     52                  &HttpResponseBodyDrainer::OnTimerFired);
     53     session_ = session;
     54     session->AddResponseDrainer(this);
     55     return;
     56   }
     57 
     58   Finish(rv);
     59 }
     60 
     61 int HttpResponseBodyDrainer::DoLoop(int result) {
     62   DCHECK_NE(next_state_, STATE_NONE);
     63 
     64   int rv = result;
     65   do {
     66     State state = next_state_;
     67     next_state_ = STATE_NONE;
     68     switch (state) {
     69       case STATE_DRAIN_RESPONSE_BODY:
     70         DCHECK_EQ(OK, rv);
     71         rv = DoDrainResponseBody();
     72         break;
     73       case STATE_DRAIN_RESPONSE_BODY_COMPLETE:
     74         rv = DoDrainResponseBodyComplete(rv);
     75         break;
     76       default:
     77         NOTREACHED() << "bad state";
     78         rv = ERR_UNEXPECTED;
     79         break;
     80     }
     81   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
     82 
     83   return rv;
     84 }
     85 
     86 int HttpResponseBodyDrainer::DoDrainResponseBody() {
     87   next_state_ = STATE_DRAIN_RESPONSE_BODY_COMPLETE;
     88 
     89   return stream_->ReadResponseBody(
     90       read_buf_.get(),
     91       read_size_ - total_read_,
     92       base::Bind(&HttpResponseBodyDrainer::OnIOComplete,
     93                  base::Unretained(this)));
     94 }
     95 
     96 int HttpResponseBodyDrainer::DoDrainResponseBodyComplete(int result) {
     97   DCHECK_NE(ERR_IO_PENDING, result);
     98 
     99   if (result < 0)
    100     return result;
    101 
    102   total_read_ += result;
    103   if (stream_->IsResponseBodyComplete())
    104     return OK;
    105 
    106   DCHECK_LE(total_read_, kDrainBodyBufferSize);
    107   if (total_read_ >= kDrainBodyBufferSize)
    108     return ERR_RESPONSE_BODY_TOO_BIG_TO_DRAIN;
    109 
    110   if (result == 0)
    111     return ERR_CONNECTION_CLOSED;
    112 
    113   next_state_ = STATE_DRAIN_RESPONSE_BODY;
    114   return OK;
    115 }
    116 
    117 void HttpResponseBodyDrainer::OnIOComplete(int result) {
    118   int rv = DoLoop(result);
    119   if (rv != ERR_IO_PENDING) {
    120     timer_.Stop();
    121     Finish(rv);
    122   }
    123 }
    124 
    125 void HttpResponseBodyDrainer::OnTimerFired() {
    126   Finish(ERR_TIMED_OUT);
    127 }
    128 
    129 void HttpResponseBodyDrainer::Finish(int result) {
    130   DCHECK_NE(ERR_IO_PENDING, result);
    131 
    132   if (session_)
    133     session_->RemoveResponseDrainer(this);
    134 
    135   if (result < 0) {
    136     stream_->Close(true /* no keep-alive */);
    137   } else {
    138     DCHECK_EQ(OK, result);
    139     stream_->Close(false /* keep-alive */);
    140   }
    141 
    142   delete this;
    143 }
    144 
    145 }  // namespace net
    146