1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
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/compiler_specific.h"
8#include "base/logging.h"
9#include "net/base/io_buffer.h"
10#include "net/base/net_errors.h"
11#include "net/http/http_network_session.h"
12#include "net/http/http_stream.h"
13
14namespace net {
15
16HttpResponseBodyDrainer::HttpResponseBodyDrainer(HttpStream* stream)
17    : stream_(stream),
18      next_state_(STATE_NONE),
19      total_read_(0),
20      ALLOW_THIS_IN_INITIALIZER_LIST(
21          io_callback_(this, &HttpResponseBodyDrainer::OnIOComplete)),
22      user_callback_(NULL),
23      session_(NULL) {}
24
25HttpResponseBodyDrainer::~HttpResponseBodyDrainer() {}
26
27void HttpResponseBodyDrainer::Start(HttpNetworkSession* session) {
28  read_buf_ = new IOBuffer(kDrainBodyBufferSize);
29  next_state_ = STATE_DRAIN_RESPONSE_BODY;
30  int rv = DoLoop(OK);
31
32  if (rv == ERR_IO_PENDING) {
33    timer_.Start(base::TimeDelta::FromSeconds(kTimeoutInSeconds),
34                 this,
35                 &HttpResponseBodyDrainer::OnTimerFired);
36    session_ = session;
37    session->AddResponseDrainer(this);
38    return;
39  }
40
41  Finish(rv);
42}
43
44int HttpResponseBodyDrainer::DoLoop(int result) {
45  DCHECK_NE(next_state_, STATE_NONE);
46
47  int rv = result;
48  do {
49    State state = next_state_;
50    next_state_ = STATE_NONE;
51    switch (state) {
52      case STATE_DRAIN_RESPONSE_BODY:
53        DCHECK_EQ(OK, rv);
54        rv = DoDrainResponseBody();
55        break;
56      case STATE_DRAIN_RESPONSE_BODY_COMPLETE:
57        rv = DoDrainResponseBodyComplete(rv);
58        break;
59      default:
60        NOTREACHED() << "bad state";
61        rv = ERR_UNEXPECTED;
62        break;
63    }
64  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
65
66  return rv;
67}
68
69int HttpResponseBodyDrainer::DoDrainResponseBody() {
70  next_state_ = STATE_DRAIN_RESPONSE_BODY_COMPLETE;
71
72  return stream_->ReadResponseBody(
73      read_buf_, kDrainBodyBufferSize - total_read_,
74      &io_callback_);
75}
76
77int HttpResponseBodyDrainer::DoDrainResponseBodyComplete(int result) {
78  DCHECK_NE(ERR_IO_PENDING, result);
79
80  if (result < 0)
81    return result;
82
83  if (result == 0)
84    return ERR_CONNECTION_CLOSED;
85
86  total_read_ += result;
87  if (stream_->IsResponseBodyComplete())
88    return OK;
89
90  DCHECK_LE(total_read_, kDrainBodyBufferSize);
91  if (total_read_ >= kDrainBodyBufferSize)
92    return ERR_RESPONSE_BODY_TOO_BIG_TO_DRAIN;
93
94  next_state_ = STATE_DRAIN_RESPONSE_BODY;
95  return OK;
96}
97
98void HttpResponseBodyDrainer::OnIOComplete(int result) {
99  int rv = DoLoop(result);
100  if (rv != ERR_IO_PENDING) {
101    timer_.Stop();
102    Finish(rv);
103  }
104}
105
106void HttpResponseBodyDrainer::OnTimerFired() {
107  Finish(ERR_TIMED_OUT);
108}
109
110void HttpResponseBodyDrainer::Finish(int result) {
111  DCHECK_NE(ERR_IO_PENDING, result);
112
113  if (session_)
114    session_->RemoveResponseDrainer(this);
115
116  if (result < 0) {
117    stream_->Close(true /* no keep-alive */);
118  } else {
119    DCHECK_EQ(OK, result);
120    stream_->Close(false /* keep-alive */);
121  }
122
123  delete this;
124}
125
126}  // namespace net
127