From 0b0bf036e2c2722b5d31162ad620b450763fa352 Mon Sep 17 00:00:00 2001 From: "willchan@chromium.org" Date: Tue, 21 Sep 2010 18:08:50 +0000 Subject: Reland r59910 - Add HttpResponseBodyDrainer. Use it for unfinished HttpStreams." There's one simple fix. wtc had asked me to increment the buffer so we keep reading into new memory while draining the body. So I added |total_read_| to |read_buf_|. The problem is |read_buf_| is an IOBuffer*, not a char*, so I'm causing us to read into raw heap memory. Crashes ensue. My unit tests didn't catch it because they never actually read data. I've fixed that by doing a memset(). I've fixed the problem by not bothering to increment the read index, since that would require throwing away IOBuffers or some intrusive modifications to IOBuffer. BUG=54277 TEST=HttpResponseBodyDrainerTest.* Also see the manual testing instructions in r59910. Review URL: http://codereview.chromium.org/3449014 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@60075 0039d316-1c4b-4281-b951-d872f2087c98 --- net/http/http_response_body_drainer.cc | 119 +++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 net/http/http_response_body_drainer.cc (limited to 'net/http/http_response_body_drainer.cc') diff --git a/net/http/http_response_body_drainer.cc b/net/http/http_response_body_drainer.cc new file mode 100644 index 0000000..599c534 --- /dev/null +++ b/net/http/http_response_body_drainer.cc @@ -0,0 +1,119 @@ +// Copyright (c) 2010 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_stream.h" + +namespace net { + +HttpResponseBodyDrainer::HttpResponseBodyDrainer(HttpStream* stream) + : stream_(stream), + next_state_(STATE_NONE), + total_read_(0), + ALLOW_THIS_IN_INITIALIZER_LIST( + io_callback_(this, &HttpResponseBodyDrainer::OnIOComplete)), + user_callback_(NULL) {} + +HttpResponseBodyDrainer::~HttpResponseBodyDrainer() {} + +void HttpResponseBodyDrainer::Start() { + read_buf_ = new IOBuffer(kDrainBodyBufferSize); + next_state_ = STATE_DRAIN_RESPONSE_BODY; + int rv = DoLoop(OK); + + if (rv == ERR_IO_PENDING) { + timer_.Start(base::TimeDelta::FromSeconds(kTimeoutInSeconds), + this, + &HttpResponseBodyDrainer::OnTimerFired); + 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_, kDrainBodyBufferSize - total_read_, + &io_callback_); +} + +int HttpResponseBodyDrainer::DoDrainResponseBodyComplete(int result) { + DCHECK_NE(ERR_IO_PENDING, result); + + if (result < 0) + return result; + + if (result == 0) + return ERR_CONNECTION_CLOSED; + + 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; + + 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 (result < 0) { + stream_->Close(true /* no keep-alive */); + } else { + DCHECK_EQ(OK, result); + stream_->Close(false /* keep-alive */); + } + + delete this; +} + +} // namespace net -- cgit v1.1