1// Copyright (c) 2012 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_base.h"
13
14namespace net {
15
16HttpResponseBodyDrainer::HttpResponseBodyDrainer(HttpStreamBase* stream)
17    : stream_(stream),
18      next_state_(STATE_NONE),
19      total_read_(0),
20      session_(NULL) {}
21
22HttpResponseBodyDrainer::~HttpResponseBodyDrainer() {}
23
24void HttpResponseBodyDrainer::Start(HttpNetworkSession* session) {
25  read_buf_ = new IOBuffer(kDrainBodyBufferSize);
26  next_state_ = STATE_DRAIN_RESPONSE_BODY;
27  int rv = DoLoop(OK);
28
29  if (rv == ERR_IO_PENDING) {
30    timer_.Start(FROM_HERE,
31                 base::TimeDelta::FromSeconds(kTimeoutInSeconds),
32                 this,
33                 &HttpResponseBodyDrainer::OnTimerFired);
34    session_ = session;
35    session->AddResponseDrainer(this);
36    return;
37  }
38
39  Finish(rv);
40}
41
42int 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
67int HttpResponseBodyDrainer::DoDrainResponseBody() {
68  next_state_ = STATE_DRAIN_RESPONSE_BODY_COMPLETE;
69
70  return stream_->ReadResponseBody(
71      read_buf_.get(),
72      kDrainBodyBufferSize - total_read_,
73      base::Bind(&HttpResponseBodyDrainer::OnIOComplete,
74                 base::Unretained(this)));
75}
76
77int HttpResponseBodyDrainer::DoDrainResponseBodyComplete(int result) {
78  DCHECK_NE(ERR_IO_PENDING, result);
79
80  if (result < 0)
81    return result;
82
83  total_read_ += result;
84  if (stream_->IsResponseBodyComplete())
85    return OK;
86
87  DCHECK_LE(total_read_, kDrainBodyBufferSize);
88  if (total_read_ >= kDrainBodyBufferSize)
89    return ERR_RESPONSE_BODY_TOO_BIG_TO_DRAIN;
90
91  if (result == 0)
92    return ERR_CONNECTION_CLOSED;
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