1 // Copyright (c) 2010 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.h"
13
14 namespace net {
15
HttpResponseBodyDrainer(HttpStream * stream)16 HttpResponseBodyDrainer::HttpResponseBodyDrainer(HttpStream* stream)
17 : stream_(stream),
18 next_state_(STATE_NONE),
19 total_read_(0),
20 ALLOW_THIS_IN_INITIALIZER_LIST(
21 io_callback_(this, &HttpResponseBodyDrainer::OnIOComplete)),
22 user_callback_(NULL),
23 session_(NULL) {}
24
~HttpResponseBodyDrainer()25 HttpResponseBodyDrainer::~HttpResponseBodyDrainer() {}
26
Start(HttpNetworkSession * session)27 void HttpResponseBodyDrainer::Start(HttpNetworkSession* session) {
28 read_buf_ = new IOBuffer(kDrainBodyBufferSize);
29 next_state_ = STATE_DRAIN_RESPONSE_BODY;
30 int rv = DoLoop(OK);
31
32 if (rv == ERR_IO_PENDING) {
33 timer_.Start(base::TimeDelta::FromSeconds(kTimeoutInSeconds),
34 this,
35 &HttpResponseBodyDrainer::OnTimerFired);
36 session_ = session;
37 session->AddResponseDrainer(this);
38 return;
39 }
40
41 Finish(rv);
42 }
43
DoLoop(int result)44 int HttpResponseBodyDrainer::DoLoop(int result) {
45 DCHECK_NE(next_state_, STATE_NONE);
46
47 int rv = result;
48 do {
49 State state = next_state_;
50 next_state_ = STATE_NONE;
51 switch (state) {
52 case STATE_DRAIN_RESPONSE_BODY:
53 DCHECK_EQ(OK, rv);
54 rv = DoDrainResponseBody();
55 break;
56 case STATE_DRAIN_RESPONSE_BODY_COMPLETE:
57 rv = DoDrainResponseBodyComplete(rv);
58 break;
59 default:
60 NOTREACHED() << "bad state";
61 rv = ERR_UNEXPECTED;
62 break;
63 }
64 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
65
66 return rv;
67 }
68
DoDrainResponseBody()69 int HttpResponseBodyDrainer::DoDrainResponseBody() {
70 next_state_ = STATE_DRAIN_RESPONSE_BODY_COMPLETE;
71
72 return stream_->ReadResponseBody(
73 read_buf_, kDrainBodyBufferSize - total_read_,
74 &io_callback_);
75 }
76
DoDrainResponseBodyComplete(int result)77 int HttpResponseBodyDrainer::DoDrainResponseBodyComplete(int result) {
78 DCHECK_NE(ERR_IO_PENDING, result);
79
80 if (result < 0)
81 return result;
82
83 if (result == 0)
84 return ERR_CONNECTION_CLOSED;
85
86 total_read_ += result;
87 if (stream_->IsResponseBodyComplete())
88 return OK;
89
90 DCHECK_LE(total_read_, kDrainBodyBufferSize);
91 if (total_read_ >= kDrainBodyBufferSize)
92 return ERR_RESPONSE_BODY_TOO_BIG_TO_DRAIN;
93
94 next_state_ = STATE_DRAIN_RESPONSE_BODY;
95 return OK;
96 }
97
OnIOComplete(int result)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
OnTimerFired()106 void HttpResponseBodyDrainer::OnTimerFired() {
107 Finish(ERR_TIMED_OUT);
108 }
109
Finish(int result)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