• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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