summaryrefslogtreecommitdiff
path: root/chromium/net/http/http_response_body_drainer.cc
blob: 0be2c28d986eb2c5dc7f5cadfe5ee56ef2ea7bb4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/http/http_response_body_drainer.h"

#include "base/bind.h"
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/memory/ptr_util.h"
#include "base/notreached.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/http/http_network_session.h"
#include "net/http/http_stream.h"

namespace net {

const int HttpResponseBodyDrainer::kDrainBodyBufferSize;
const int HttpResponseBodyDrainer::kTimeoutInSeconds;

HttpResponseBodyDrainer::HttpResponseBodyDrainer(HttpStream* stream)
    : stream_(stream) {}

HttpResponseBodyDrainer::~HttpResponseBodyDrainer() = default;

void HttpResponseBodyDrainer::Start(HttpNetworkSession* session) {
  session_ = session;
  read_buf_ = base::MakeRefCounted<IOBuffer>(kDrainBodyBufferSize);
  next_state_ = STATE_DRAIN_RESPONSE_BODY;
  int rv = DoLoop(OK);

  if (rv == ERR_IO_PENDING) {
    timer_.Start(FROM_HERE, base::Seconds(kTimeoutInSeconds), this,
                 &HttpResponseBodyDrainer::OnTimerFired);
    return;
  }

  Finish(rv);
}

int HttpResponseBodyDrainer::DoLoop(int result) {
  DCHECK_NE(next_state_, STATE_NONE);

  int rv = result;
  do {
    State state = next_state_;
    next_state_ = STATE_NONE;
    switch (state) {
      case STATE_DRAIN_RESPONSE_BODY:
        DCHECK_EQ(OK, rv);
        rv = DoDrainResponseBody();
        break;
      case STATE_DRAIN_RESPONSE_BODY_COMPLETE:
        rv = DoDrainResponseBodyComplete(rv);
        break;
      default:
        NOTREACHED() << "bad state";
        rv = ERR_UNEXPECTED;
        break;
    }
  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);

  return rv;
}

int HttpResponseBodyDrainer::DoDrainResponseBody() {
  next_state_ = STATE_DRAIN_RESPONSE_BODY_COMPLETE;

  return stream_->ReadResponseBody(
      read_buf_.get(), kDrainBodyBufferSize - total_read_,
      base::BindOnce(&HttpResponseBodyDrainer::OnIOComplete,
                     base::Unretained(this)));
}

int HttpResponseBodyDrainer::DoDrainResponseBodyComplete(int result) {
  DCHECK_NE(ERR_IO_PENDING, result);

  if (result < 0)
    return result;

  total_read_ += result;
  if (stream_->IsResponseBodyComplete())
    return OK;

  DCHECK_LE(total_read_, kDrainBodyBufferSize);
  if (total_read_ >= kDrainBodyBufferSize)
    return ERR_RESPONSE_BODY_TOO_BIG_TO_DRAIN;

  if (result == 0)
    return ERR_CONNECTION_CLOSED;

  next_state_ = STATE_DRAIN_RESPONSE_BODY;
  return OK;
}

void HttpResponseBodyDrainer::OnIOComplete(int result) {
  int rv = DoLoop(result);
  if (rv != ERR_IO_PENDING) {
    timer_.Stop();
    Finish(rv);
  }
}

void HttpResponseBodyDrainer::OnTimerFired() {
  Finish(ERR_TIMED_OUT);
}

void HttpResponseBodyDrainer::Finish(int result) {
  DCHECK_NE(ERR_IO_PENDING, result);

  if (result < 0 || !stream_->CanReuseConnection()) {
    stream_->Close(true /* no keep-alive */);
  } else {
    DCHECK_EQ(OK, result);
    stream_->Close(false /* keep-alive */);
  }

  session_->RemoveResponseDrainer(this);
}

}  // namespace net