| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "net/http/http_response_body_drainer.h" | 
 |  | 
 | #include "base/compiler_specific.h" | 
 | #include "base/logging.h" | 
 | #include "net/base/io_buffer.h" | 
 | #include "net/base/net_errors.h" | 
 | #include "net/http/http_network_session.h" | 
 | #include "net/http/http_stream.h" | 
 |  | 
 | namespace net { | 
 |  | 
 | HttpResponseBodyDrainer::HttpResponseBodyDrainer(HttpStream* stream) | 
 |     : read_size_(0), | 
 |       stream_(stream), | 
 |       next_state_(STATE_NONE), | 
 |       total_read_(0), | 
 |       session_(NULL) {} | 
 |  | 
 | HttpResponseBodyDrainer::~HttpResponseBodyDrainer() {} | 
 |  | 
 | void HttpResponseBodyDrainer::Start(HttpNetworkSession* session) { | 
 |   StartWithSize(session, kDrainBodyBufferSize); | 
 | } | 
 |  | 
 | void HttpResponseBodyDrainer::StartWithSize(HttpNetworkSession* session, | 
 |                                             int num_bytes_to_drain) { | 
 |   DCHECK_LE(0, num_bytes_to_drain); | 
 |   // TODO(simonjam): Consider raising this limit if we're pipelining. If we have | 
 |   // a bunch of responses in the pipeline, we should be less willing to give up | 
 |   // while draining. | 
 |   if (num_bytes_to_drain > kDrainBodyBufferSize) { | 
 |     Finish(ERR_RESPONSE_BODY_TOO_BIG_TO_DRAIN); | 
 |     return; | 
 |   } else if (num_bytes_to_drain == 0) { | 
 |     Finish(OK); | 
 |     return; | 
 |   } | 
 |  | 
 |   read_size_ = num_bytes_to_drain; | 
 |   read_buf_ = new IOBuffer(read_size_); | 
 |   next_state_ = STATE_DRAIN_RESPONSE_BODY; | 
 |   int rv = DoLoop(OK); | 
 |  | 
 |   if (rv == ERR_IO_PENDING) { | 
 |     timer_.Start(FROM_HERE, | 
 |                  base::TimeDelta::FromSeconds(kTimeoutInSeconds), | 
 |                  this, | 
 |                  &HttpResponseBodyDrainer::OnTimerFired); | 
 |     session_ = session; | 
 |     session->AddResponseDrainer(this); | 
 |     return; | 
 |   } | 
 |  | 
 |   Finish(rv); | 
 | } | 
 |  | 
 | int HttpResponseBodyDrainer::DoLoop(int result) { | 
 |   DCHECK_NE(next_state_, STATE_NONE); | 
 |  | 
 |   int rv = result; | 
 |   do { | 
 |     State state = next_state_; | 
 |     next_state_ = STATE_NONE; | 
 |     switch (state) { | 
 |       case STATE_DRAIN_RESPONSE_BODY: | 
 |         DCHECK_EQ(OK, rv); | 
 |         rv = DoDrainResponseBody(); | 
 |         break; | 
 |       case STATE_DRAIN_RESPONSE_BODY_COMPLETE: | 
 |         rv = DoDrainResponseBodyComplete(rv); | 
 |         break; | 
 |       default: | 
 |         NOTREACHED() << "bad state"; | 
 |         rv = ERR_UNEXPECTED; | 
 |         break; | 
 |     } | 
 |   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | 
 |  | 
 |   return rv; | 
 | } | 
 |  | 
 | int HttpResponseBodyDrainer::DoDrainResponseBody() { | 
 |   next_state_ = STATE_DRAIN_RESPONSE_BODY_COMPLETE; | 
 |  | 
 |   return stream_->ReadResponseBody( | 
 |       read_buf_, read_size_ - total_read_, | 
 |       base::Bind(&HttpResponseBodyDrainer::OnIOComplete, | 
 |                  base::Unretained(this))); | 
 | } | 
 |  | 
 | int HttpResponseBodyDrainer::DoDrainResponseBodyComplete(int result) { | 
 |   DCHECK_NE(ERR_IO_PENDING, result); | 
 |  | 
 |   if (result < 0) | 
 |     return result; | 
 |  | 
 |   total_read_ += result; | 
 |   if (stream_->IsResponseBodyComplete()) | 
 |     return OK; | 
 |  | 
 |   DCHECK_LE(total_read_, kDrainBodyBufferSize); | 
 |   if (total_read_ >= kDrainBodyBufferSize) | 
 |     return ERR_RESPONSE_BODY_TOO_BIG_TO_DRAIN; | 
 |  | 
 |   if (result == 0) | 
 |     return ERR_CONNECTION_CLOSED; | 
 |  | 
 |   next_state_ = STATE_DRAIN_RESPONSE_BODY; | 
 |   return OK; | 
 | } | 
 |  | 
 | void HttpResponseBodyDrainer::OnIOComplete(int result) { | 
 |   int rv = DoLoop(result); | 
 |   if (rv != ERR_IO_PENDING) { | 
 |     timer_.Stop(); | 
 |     Finish(rv); | 
 |   } | 
 | } | 
 |  | 
 | void HttpResponseBodyDrainer::OnTimerFired() { | 
 |   Finish(ERR_TIMED_OUT); | 
 | } | 
 |  | 
 | void HttpResponseBodyDrainer::Finish(int result) { | 
 |   DCHECK_NE(ERR_IO_PENDING, result); | 
 |  | 
 |   if (session_) | 
 |     session_->RemoveResponseDrainer(this); | 
 |  | 
 |   if (result < 0) { | 
 |     stream_->Close(true /* no keep-alive */); | 
 |   } else { | 
 |     DCHECK_EQ(OK, result); | 
 |     stream_->Close(false /* keep-alive */); | 
 |   } | 
 |  | 
 |   delete this; | 
 | } | 
 |  | 
 | }  // namespace net |