// 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_base.h" namespace net { HttpResponseBodyDrainer::HttpResponseBodyDrainer(HttpStreamBase* 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_.get(), 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