xref: /aosp_15_r20/external/cronet/net/http/http_response_body_drainer.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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