1 // Copyright 2012 The Chromium Authors
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/check_op.h"
8 #include "base/compiler_specific.h"
9 #include "base/functional/bind.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/notreached.h"
12 #include "net/base/io_buffer.h"
13 #include "net/base/net_errors.h"
14 #include "net/http/http_network_session.h"
15 #include "net/http/http_stream.h"
16
17 namespace net {
18
19 const int HttpResponseBodyDrainer::kDrainBodyBufferSize;
20 const int HttpResponseBodyDrainer::kTimeoutInSeconds;
21
HttpResponseBodyDrainer(HttpStream * stream)22 HttpResponseBodyDrainer::HttpResponseBodyDrainer(HttpStream* stream)
23 : stream_(stream) {}
24
25 HttpResponseBodyDrainer::~HttpResponseBodyDrainer() = default;
26
Start(HttpNetworkSession * session)27 void HttpResponseBodyDrainer::Start(HttpNetworkSession* session) {
28 session_ = session;
29 read_buf_ = base::MakeRefCounted<IOBufferWithSize>(kDrainBodyBufferSize);
30 next_state_ = STATE_DRAIN_RESPONSE_BODY;
31 int rv = DoLoop(OK);
32
33 if (rv == ERR_IO_PENDING) {
34 timer_.Start(FROM_HERE, base::Seconds(kTimeoutInSeconds), this,
35 &HttpResponseBodyDrainer::OnTimerFired);
36 return;
37 }
38
39 Finish(rv);
40 }
41
DoLoop(int result)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 }
60 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
61
62 return rv;
63 }
64
DoDrainResponseBody()65 int HttpResponseBodyDrainer::DoDrainResponseBody() {
66 next_state_ = STATE_DRAIN_RESPONSE_BODY_COMPLETE;
67
68 return stream_->ReadResponseBody(
69 read_buf_.get(), kDrainBodyBufferSize - total_read_,
70 base::BindOnce(&HttpResponseBodyDrainer::OnIOComplete,
71 base::Unretained(this)));
72 }
73
DoDrainResponseBodyComplete(int result)74 int HttpResponseBodyDrainer::DoDrainResponseBodyComplete(int result) {
75 DCHECK_NE(ERR_IO_PENDING, result);
76
77 if (result < 0)
78 return result;
79
80 total_read_ += result;
81 if (stream_->IsResponseBodyComplete())
82 return OK;
83
84 DCHECK_LE(total_read_, kDrainBodyBufferSize);
85 if (total_read_ >= kDrainBodyBufferSize)
86 return ERR_RESPONSE_BODY_TOO_BIG_TO_DRAIN;
87
88 if (result == 0)
89 return ERR_CONNECTION_CLOSED;
90
91 next_state_ = STATE_DRAIN_RESPONSE_BODY;
92 return OK;
93 }
94
OnIOComplete(int result)95 void HttpResponseBodyDrainer::OnIOComplete(int result) {
96 int rv = DoLoop(result);
97 if (rv != ERR_IO_PENDING) {
98 timer_.Stop();
99 Finish(rv);
100 }
101 }
102
OnTimerFired()103 void HttpResponseBodyDrainer::OnTimerFired() {
104 Finish(ERR_TIMED_OUT);
105 }
106
Finish(int result)107 void HttpResponseBodyDrainer::Finish(int result) {
108 DCHECK_NE(ERR_IO_PENDING, result);
109
110 if (result < 0 || !stream_->CanReuseConnection()) {
111 stream_->Close(true /* no keep-alive */);
112 } else {
113 DCHECK_EQ(OK, result);
114 stream_->Close(false /* keep-alive */);
115 }
116
117 session_->RemoveResponseDrainer(this);
118 }
119
120 } // namespace net
121