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 <cstring>
8
9#include "base/bind.h"
10#include "base/compiler_specific.h"
11#include "base/memory/weak_ptr.h"
12#include "base/message_loop/message_loop.h"
13#include "net/base/io_buffer.h"
14#include "net/base/net_errors.h"
15#include "net/base/test_completion_callback.h"
16#include "net/http/http_network_session.h"
17#include "net/http/http_server_properties_impl.h"
18#include "net/http/http_stream.h"
19#include "net/proxy/proxy_service.h"
20#include "net/ssl/ssl_config_service_defaults.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23namespace net {
24
25namespace {
26
27const int kMagicChunkSize = 1024;
28COMPILE_ASSERT(
29    (HttpResponseBodyDrainer::kDrainBodyBufferSize % kMagicChunkSize) == 0,
30    chunk_size_needs_to_divide_evenly_into_buffer_size);
31
32class CloseResultWaiter {
33 public:
34  CloseResultWaiter()
35      : result_(false),
36        have_result_(false),
37        waiting_for_result_(false) {}
38
39  int WaitForResult() {
40    CHECK(!waiting_for_result_);
41    while (!have_result_) {
42      waiting_for_result_ = true;
43      base::MessageLoop::current()->Run();
44      waiting_for_result_ = false;
45    }
46    return result_;
47  }
48
49  void set_result(bool result) {
50    result_ = result;
51    have_result_ = true;
52    if (waiting_for_result_)
53      base::MessageLoop::current()->Quit();
54  }
55
56 private:
57  int result_;
58  bool have_result_;
59  bool waiting_for_result_;
60
61  DISALLOW_COPY_AND_ASSIGN(CloseResultWaiter);
62};
63
64class MockHttpStream : public HttpStream {
65 public:
66  MockHttpStream(CloseResultWaiter* result_waiter)
67      : result_waiter_(result_waiter),
68        buf_len_(0),
69        closed_(false),
70        stall_reads_forever_(false),
71        num_chunks_(0),
72        is_sync_(false),
73        is_last_chunk_zero_size_(false),
74        is_complete_(false),
75        weak_factory_(this) {}
76  virtual ~MockHttpStream() {}
77
78  // HttpStream implementation.
79  virtual int InitializeStream(const HttpRequestInfo* request_info,
80                               RequestPriority priority,
81                               const BoundNetLog& net_log,
82                               const CompletionCallback& callback) OVERRIDE {
83    return ERR_UNEXPECTED;
84  }
85  virtual int SendRequest(const HttpRequestHeaders& request_headers,
86                          HttpResponseInfo* response,
87                          const CompletionCallback& callback) OVERRIDE {
88    return ERR_UNEXPECTED;
89  }
90  virtual UploadProgress GetUploadProgress() const OVERRIDE {
91    return UploadProgress();
92  }
93  virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE {
94    return ERR_UNEXPECTED;
95  }
96  virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE {
97    return NULL;
98  }
99
100  virtual bool CanFindEndOfResponse() const OVERRIDE { return true; }
101  virtual bool IsConnectionReused() const OVERRIDE { return false; }
102  virtual void SetConnectionReused() OVERRIDE {}
103  virtual bool IsConnectionReusable() const OVERRIDE { return false; }
104  virtual void GetSSLInfo(SSLInfo* ssl_info) OVERRIDE {}
105  virtual void GetSSLCertRequestInfo(
106      SSLCertRequestInfo* cert_request_info) OVERRIDE {}
107
108  // Mocked API
109  virtual int ReadResponseBody(IOBuffer* buf, int buf_len,
110                               const CompletionCallback& callback) OVERRIDE;
111  virtual void Close(bool not_reusable) OVERRIDE {
112    CHECK(!closed_);
113    closed_ = true;
114    result_waiter_->set_result(not_reusable);
115  }
116
117  virtual HttpStream* RenewStreamForAuth() OVERRIDE {
118    return NULL;
119  }
120
121  virtual bool IsResponseBodyComplete() const OVERRIDE { return is_complete_; }
122
123  virtual bool IsSpdyHttpStream() const OVERRIDE { return false; }
124
125  virtual bool GetLoadTimingInfo(
126      LoadTimingInfo* load_timing_info) const OVERRIDE { return false; }
127
128  virtual void Drain(HttpNetworkSession*) OVERRIDE {}
129
130  // Methods to tweak/observer mock behavior:
131  void set_stall_reads_forever() { stall_reads_forever_ = true; }
132
133  void set_num_chunks(int num_chunks) { num_chunks_ = num_chunks; }
134
135  void set_sync() { is_sync_ = true; }
136
137  void set_is_last_chunk_zero_size() { is_last_chunk_zero_size_ = true; }
138
139 private:
140  int ReadResponseBodyImpl(IOBuffer* buf, int buf_len);
141  void CompleteRead();
142
143  bool closed() const { return closed_; }
144
145  CloseResultWaiter* const result_waiter_;
146  scoped_refptr<IOBuffer> user_buf_;
147  CompletionCallback callback_;
148  int buf_len_;
149  bool closed_;
150  bool stall_reads_forever_;
151  int num_chunks_;
152  bool is_sync_;
153  bool is_last_chunk_zero_size_;
154  bool is_complete_;
155  base::WeakPtrFactory<MockHttpStream> weak_factory_;
156};
157
158int MockHttpStream::ReadResponseBody(IOBuffer* buf,
159                                     int buf_len,
160                                     const CompletionCallback& callback) {
161  CHECK(!callback.is_null());
162  CHECK(callback_.is_null());
163  CHECK(buf);
164
165  if (stall_reads_forever_)
166    return ERR_IO_PENDING;
167
168  if (is_complete_)
169    return ERR_UNEXPECTED;
170
171  if (!is_sync_) {
172    user_buf_ = buf;
173    buf_len_ = buf_len;
174    callback_ = callback;
175    base::MessageLoop::current()->PostTask(
176        FROM_HERE,
177        base::Bind(&MockHttpStream::CompleteRead, weak_factory_.GetWeakPtr()));
178    return ERR_IO_PENDING;
179  } else {
180    return ReadResponseBodyImpl(buf, buf_len);
181  }
182}
183
184int MockHttpStream::ReadResponseBodyImpl(IOBuffer* buf, int buf_len) {
185  if (is_last_chunk_zero_size_ && num_chunks_ == 1) {
186    buf_len = 0;
187  } else {
188    if (buf_len > kMagicChunkSize)
189      buf_len = kMagicChunkSize;
190    std::memset(buf->data(), 1, buf_len);
191  }
192  num_chunks_--;
193  if (!num_chunks_)
194    is_complete_ = true;
195
196  return buf_len;
197}
198
199void MockHttpStream::CompleteRead() {
200  int result = ReadResponseBodyImpl(user_buf_.get(), buf_len_);
201  user_buf_ = NULL;
202  CompletionCallback callback = callback_;
203  callback_.Reset();
204  callback.Run(result);
205}
206
207class HttpResponseBodyDrainerTest : public testing::Test {
208 protected:
209  HttpResponseBodyDrainerTest()
210      : proxy_service_(ProxyService::CreateDirect()),
211        ssl_config_service_(new SSLConfigServiceDefaults),
212        http_server_properties_(new HttpServerPropertiesImpl()),
213        session_(CreateNetworkSession()),
214        mock_stream_(new MockHttpStream(&result_waiter_)),
215        drainer_(new HttpResponseBodyDrainer(mock_stream_)) {}
216
217  virtual ~HttpResponseBodyDrainerTest() {}
218
219  HttpNetworkSession* CreateNetworkSession() const {
220    HttpNetworkSession::Params params;
221    params.proxy_service = proxy_service_.get();
222    params.ssl_config_service = ssl_config_service_.get();
223    params.http_server_properties = http_server_properties_->GetWeakPtr();
224    return new HttpNetworkSession(params);
225  }
226
227  scoped_ptr<ProxyService> proxy_service_;
228  scoped_refptr<SSLConfigService> ssl_config_service_;
229  scoped_ptr<HttpServerPropertiesImpl> http_server_properties_;
230  const scoped_refptr<HttpNetworkSession> session_;
231  CloseResultWaiter result_waiter_;
232  MockHttpStream* const mock_stream_;  // Owned by |drainer_|.
233  HttpResponseBodyDrainer* const drainer_;  // Deletes itself.
234};
235
236TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncSingleOK) {
237  mock_stream_->set_num_chunks(1);
238  mock_stream_->set_sync();
239  drainer_->Start(session_.get());
240  EXPECT_FALSE(result_waiter_.WaitForResult());
241}
242
243TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncOK) {
244  mock_stream_->set_num_chunks(3);
245  mock_stream_->set_sync();
246  drainer_->Start(session_.get());
247  EXPECT_FALSE(result_waiter_.WaitForResult());
248}
249
250TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncOK) {
251  mock_stream_->set_num_chunks(3);
252  drainer_->Start(session_.get());
253  EXPECT_FALSE(result_waiter_.WaitForResult());
254}
255
256// Test the case when the final chunk is 0 bytes. This can happen when
257// the final 0-byte chunk of a chunk-encoded http response is read in a last
258// call to ReadResponseBody, after all data were returned from HttpStream.
259TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncEmptyChunk) {
260  mock_stream_->set_num_chunks(4);
261  mock_stream_->set_is_last_chunk_zero_size();
262  drainer_->Start(session_.get());
263  EXPECT_FALSE(result_waiter_.WaitForResult());
264}
265
266TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncEmptyChunk) {
267  mock_stream_->set_num_chunks(4);
268  mock_stream_->set_sync();
269  mock_stream_->set_is_last_chunk_zero_size();
270  drainer_->Start(session_.get());
271  EXPECT_FALSE(result_waiter_.WaitForResult());
272}
273
274TEST_F(HttpResponseBodyDrainerTest, DrainBodySizeEqualsDrainBuffer) {
275  mock_stream_->set_num_chunks(
276      HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize);
277  drainer_->Start(session_.get());
278  EXPECT_FALSE(result_waiter_.WaitForResult());
279}
280
281TEST_F(HttpResponseBodyDrainerTest, DrainBodyTimeOut) {
282  mock_stream_->set_num_chunks(2);
283  mock_stream_->set_stall_reads_forever();
284  drainer_->Start(session_.get());
285  EXPECT_TRUE(result_waiter_.WaitForResult());
286}
287
288TEST_F(HttpResponseBodyDrainerTest, CancelledBySession) {
289  mock_stream_->set_num_chunks(2);
290  mock_stream_->set_stall_reads_forever();
291  drainer_->Start(session_.get());
292  // HttpNetworkSession should delete |drainer_|.
293}
294
295TEST_F(HttpResponseBodyDrainerTest, DrainBodyTooLarge) {
296  int too_many_chunks =
297      HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize;
298  too_many_chunks += 1;  // Now it's too large.
299
300  mock_stream_->set_num_chunks(too_many_chunks);
301  drainer_->Start(session_.get());
302  EXPECT_TRUE(result_waiter_.WaitForResult());
303}
304
305TEST_F(HttpResponseBodyDrainerTest, StartBodyTooLarge) {
306  int too_many_chunks =
307      HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize;
308  too_many_chunks += 1;  // Now it's too large.
309
310  mock_stream_->set_num_chunks(0);
311  drainer_->StartWithSize(session_.get(), too_many_chunks * kMagicChunkSize);
312  EXPECT_TRUE(result_waiter_.WaitForResult());
313}
314
315TEST_F(HttpResponseBodyDrainerTest, StartWithNothingToDo) {
316  mock_stream_->set_num_chunks(0);
317  drainer_->StartWithSize(session_.get(), 0);
318  EXPECT_FALSE(result_waiter_.WaitForResult());
319}
320
321}  // namespace
322
323}  // namespace net
324