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 : stream_(stream), 18 next_state_(STATE_NONE), 19 total_read_(0), 20 session_(NULL) {} 21 22 HttpResponseBodyDrainer::~HttpResponseBodyDrainer() {} 23 24 void HttpResponseBodyDrainer::Start(HttpNetworkSession* session) { 25 read_buf_ = new IOBuffer(kDrainBodyBufferSize); 26 next_state_ = STATE_DRAIN_RESPONSE_BODY; 27 int rv = DoLoop(OK); 28 29 if (rv == ERR_IO_PENDING) { 30 timer_.Start(FROM_HERE, 31 base::TimeDelta::FromSeconds(kTimeoutInSeconds), 32 this, 33 &HttpResponseBodyDrainer::OnTimerFired); 34 session_ = session; 35 session->AddResponseDrainer(this); 36 return; 37 } 38 39 Finish(rv); 40 } 41 42 int HttpResponseBodyDrainer::DoLoop(int result) { 43 DCHECK_NE(next_state_, STATE_NONE); 44 45 int rv = result; 46 do { 47 State state = next_state_; 48 next_state_ = STATE_NONE; 49 switch (state) { 50 case STATE_DRAIN_RESPONSE_BODY: 51 DCHECK_EQ(OK, rv); 52 rv = DoDrainResponseBody(); 53 break; 54 case STATE_DRAIN_RESPONSE_BODY_COMPLETE: 55 rv = DoDrainResponseBodyComplete(rv); 56 break; 57 default: 58 NOTREACHED() << "bad state"; 59 rv = ERR_UNEXPECTED; 60 break; 61 } 62 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); 63 64 return rv; 65 } 66 67 int HttpResponseBodyDrainer::DoDrainResponseBody() { 68 next_state_ = STATE_DRAIN_RESPONSE_BODY_COMPLETE; 69 70 return stream_->ReadResponseBody( 71 read_buf_.get(), 72 kDrainBodyBufferSize - total_read_, 73 base::Bind(&HttpResponseBodyDrainer::OnIOComplete, 74 base::Unretained(this))); 75 } 76 77 int HttpResponseBodyDrainer::DoDrainResponseBodyComplete(int result) { 78 DCHECK_NE(ERR_IO_PENDING, result); 79 80 if (result < 0) 81 return result; 82 83 total_read_ += result; 84 if (stream_->IsResponseBodyComplete()) 85 return OK; 86 87 DCHECK_LE(total_read_, kDrainBodyBufferSize); 88 if (total_read_ >= kDrainBodyBufferSize) 89 return ERR_RESPONSE_BODY_TOO_BIG_TO_DRAIN; 90 91 if (result == 0) 92 return ERR_CONNECTION_CLOSED; 93 94 next_state_ = STATE_DRAIN_RESPONSE_BODY; 95 return OK; 96 } 97 98 void HttpResponseBodyDrainer::OnIOComplete(int result) { 99 int rv = DoLoop(result); 100 if (rv != ERR_IO_PENDING) { 101 timer_.Stop(); 102 Finish(rv); 103 } 104 } 105 106 void HttpResponseBodyDrainer::OnTimerFired() { 107 Finish(ERR_TIMED_OUT); 108 } 109 110 void HttpResponseBodyDrainer::Finish(int result) { 111 DCHECK_NE(ERR_IO_PENDING, result); 112 113 if (session_) 114 session_->RemoveResponseDrainer(this); 115 116 if (result < 0) { 117 stream_->Close(true /* no keep-alive */); 118 } else { 119 DCHECK_EQ(OK, result); 120 stream_->Close(false /* keep-alive */); 121 } 122 123 delete this; 124 } 125 126 } // namespace net 127