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_base.h" 13 14 namespace net { 15 16 HttpResponseBodyDrainer::HttpResponseBodyDrainer(HttpStreamBase* 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