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 rv = ERR_UNEXPECTED;
60 break;
61 }
62 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
63
64 return rv;
65 }
66
DoDrainResponseBody()67 int HttpResponseBodyDrainer::DoDrainResponseBody() {
68 next_state_ = STATE_DRAIN_RESPONSE_BODY_COMPLETE;
69
70 return stream_->ReadResponseBody(
71 read_buf_.get(), kDrainBodyBufferSize - total_read_,
72 base::BindOnce(&HttpResponseBodyDrainer::OnIOComplete,
73 base::Unretained(this)));
74 }
75
DoDrainResponseBodyComplete(int result)76 int HttpResponseBodyDrainer::DoDrainResponseBodyComplete(int result) {
77 DCHECK_NE(ERR_IO_PENDING, result);
78
79 if (result < 0)
80 return result;
81
82 total_read_ += result;
83 if (stream_->IsResponseBodyComplete())
84 return OK;
85
86 DCHECK_LE(total_read_, kDrainBodyBufferSize);
87 if (total_read_ >= kDrainBodyBufferSize)
88 return ERR_RESPONSE_BODY_TOO_BIG_TO_DRAIN;
89
90 if (result == 0)
91 return ERR_CONNECTION_CLOSED;
92
93 next_state_ = STATE_DRAIN_RESPONSE_BODY;
94 return OK;
95 }
96
OnIOComplete(int result)97 void HttpResponseBodyDrainer::OnIOComplete(int result) {
98 int rv = DoLoop(result);
99 if (rv != ERR_IO_PENDING) {
100 timer_.Stop();
101 Finish(rv);
102 }
103 }
104
OnTimerFired()105 void HttpResponseBodyDrainer::OnTimerFired() {
106 Finish(ERR_TIMED_OUT);
107 }
108
Finish(int result)109 void HttpResponseBodyDrainer::Finish(int result) {
110 DCHECK_NE(ERR_IO_PENDING, result);
111
112 if (result < 0 || !stream_->CanReuseConnection()) {
113 stream_->Close(true /* no keep-alive */);
114 } else {
115 DCHECK_EQ(OK, result);
116 stream_->Close(false /* keep-alive */);
117 }
118
119 session_->RemoveResponseDrainer(this);
120 }
121
122 } // namespace net
123