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.
5 #include "net/http/http_response_body_drainer.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"
27 const int kMagicChunkSize
= 1024;
29 (HttpResponseBodyDrainer::kDrainBodyBufferSize
% kMagicChunkSize
) == 0,
30 chunk_size_needs_to_divide_evenly_into_buffer_size
);
32 class CloseResultWaiter
{
37 waiting_for_result_(false) {}
40 CHECK(!waiting_for_result_
);
41 while (!have_result_
) {
42 waiting_for_result_
= true;
43 base::MessageLoop::current()->Run();
44 waiting_for_result_
= false;
49 void set_result(bool result
) {
52 if (waiting_for_result_
)
53 base::MessageLoop::current()->Quit();
59 bool waiting_for_result_
;
61 DISALLOW_COPY_AND_ASSIGN(CloseResultWaiter
);
64 class MockHttpStream
: public HttpStream
{
66 MockHttpStream(CloseResultWaiter
* result_waiter
)
67 : result_waiter_(result_waiter
),
70 stall_reads_forever_(false),
73 is_last_chunk_zero_size_(false),
75 weak_factory_(this) {}
76 virtual ~MockHttpStream() {}
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
;
85 virtual int SendRequest(const HttpRequestHeaders
& request_headers
,
86 HttpResponseInfo
* response
,
87 const CompletionCallback
& callback
) OVERRIDE
{
88 return ERR_UNEXPECTED
;
90 virtual UploadProgress
GetUploadProgress() const OVERRIDE
{
91 return UploadProgress();
93 virtual int ReadResponseHeaders(const CompletionCallback
& callback
) OVERRIDE
{
94 return ERR_UNEXPECTED
;
96 virtual const HttpResponseInfo
* GetResponseInfo() const OVERRIDE
{
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 int64
GetTotalReceivedBytes() const OVERRIDE
{ return 0; }
105 virtual void GetSSLInfo(SSLInfo
* ssl_info
) OVERRIDE
{}
106 virtual void GetSSLCertRequestInfo(
107 SSLCertRequestInfo
* cert_request_info
) OVERRIDE
{}
110 virtual int ReadResponseBody(IOBuffer
* buf
, int buf_len
,
111 const CompletionCallback
& callback
) OVERRIDE
;
112 virtual void Close(bool not_reusable
) OVERRIDE
{
115 result_waiter_
->set_result(not_reusable
);
118 virtual HttpStream
* RenewStreamForAuth() OVERRIDE
{
122 virtual bool IsResponseBodyComplete() const OVERRIDE
{ return is_complete_
; }
124 virtual bool IsSpdyHttpStream() const OVERRIDE
{ return false; }
126 virtual bool GetLoadTimingInfo(
127 LoadTimingInfo
* load_timing_info
) const OVERRIDE
{ return false; }
129 virtual void Drain(HttpNetworkSession
*) OVERRIDE
{}
131 virtual void SetPriority(RequestPriority priority
) OVERRIDE
{}
133 // Methods to tweak/observer mock behavior:
134 void set_stall_reads_forever() { stall_reads_forever_
= true; }
136 void set_num_chunks(int num_chunks
) { num_chunks_
= num_chunks
; }
138 void set_sync() { is_sync_
= true; }
140 void set_is_last_chunk_zero_size() { is_last_chunk_zero_size_
= true; }
143 int ReadResponseBodyImpl(IOBuffer
* buf
, int buf_len
);
146 bool closed() const { return closed_
; }
148 CloseResultWaiter
* const result_waiter_
;
149 scoped_refptr
<IOBuffer
> user_buf_
;
150 CompletionCallback callback_
;
153 bool stall_reads_forever_
;
156 bool is_last_chunk_zero_size_
;
158 base::WeakPtrFactory
<MockHttpStream
> weak_factory_
;
161 int MockHttpStream::ReadResponseBody(IOBuffer
* buf
,
163 const CompletionCallback
& callback
) {
164 CHECK(!callback
.is_null());
165 CHECK(callback_
.is_null());
168 if (stall_reads_forever_
)
169 return ERR_IO_PENDING
;
172 return ERR_UNEXPECTED
;
177 callback_
= callback
;
178 base::MessageLoop::current()->PostTask(
180 base::Bind(&MockHttpStream::CompleteRead
, weak_factory_
.GetWeakPtr()));
181 return ERR_IO_PENDING
;
183 return ReadResponseBodyImpl(buf
, buf_len
);
187 int MockHttpStream::ReadResponseBodyImpl(IOBuffer
* buf
, int buf_len
) {
188 if (is_last_chunk_zero_size_
&& num_chunks_
== 1) {
191 if (buf_len
> kMagicChunkSize
)
192 buf_len
= kMagicChunkSize
;
193 std::memset(buf
->data(), 1, buf_len
);
202 void MockHttpStream::CompleteRead() {
203 int result
= ReadResponseBodyImpl(user_buf_
.get(), buf_len_
);
205 CompletionCallback callback
= callback_
;
207 callback
.Run(result
);
210 class HttpResponseBodyDrainerTest
: public testing::Test
{
212 HttpResponseBodyDrainerTest()
213 : proxy_service_(ProxyService::CreateDirect()),
214 ssl_config_service_(new SSLConfigServiceDefaults
),
215 http_server_properties_(new HttpServerPropertiesImpl()),
216 session_(CreateNetworkSession()),
217 mock_stream_(new MockHttpStream(&result_waiter_
)),
218 drainer_(new HttpResponseBodyDrainer(mock_stream_
)) {}
220 virtual ~HttpResponseBodyDrainerTest() {}
222 HttpNetworkSession
* CreateNetworkSession() const {
223 HttpNetworkSession::Params params
;
224 params
.proxy_service
= proxy_service_
.get();
225 params
.ssl_config_service
= ssl_config_service_
.get();
226 params
.http_server_properties
= http_server_properties_
->GetWeakPtr();
227 return new HttpNetworkSession(params
);
230 scoped_ptr
<ProxyService
> proxy_service_
;
231 scoped_refptr
<SSLConfigService
> ssl_config_service_
;
232 scoped_ptr
<HttpServerPropertiesImpl
> http_server_properties_
;
233 const scoped_refptr
<HttpNetworkSession
> session_
;
234 CloseResultWaiter result_waiter_
;
235 MockHttpStream
* const mock_stream_
; // Owned by |drainer_|.
236 HttpResponseBodyDrainer
* const drainer_
; // Deletes itself.
239 TEST_F(HttpResponseBodyDrainerTest
, DrainBodySyncSingleOK
) {
240 mock_stream_
->set_num_chunks(1);
241 mock_stream_
->set_sync();
242 drainer_
->Start(session_
.get());
243 EXPECT_FALSE(result_waiter_
.WaitForResult());
246 TEST_F(HttpResponseBodyDrainerTest
, DrainBodySyncOK
) {
247 mock_stream_
->set_num_chunks(3);
248 mock_stream_
->set_sync();
249 drainer_
->Start(session_
.get());
250 EXPECT_FALSE(result_waiter_
.WaitForResult());
253 TEST_F(HttpResponseBodyDrainerTest
, DrainBodyAsyncOK
) {
254 mock_stream_
->set_num_chunks(3);
255 drainer_
->Start(session_
.get());
256 EXPECT_FALSE(result_waiter_
.WaitForResult());
259 // Test the case when the final chunk is 0 bytes. This can happen when
260 // the final 0-byte chunk of a chunk-encoded http response is read in a last
261 // call to ReadResponseBody, after all data were returned from HttpStream.
262 TEST_F(HttpResponseBodyDrainerTest
, DrainBodyAsyncEmptyChunk
) {
263 mock_stream_
->set_num_chunks(4);
264 mock_stream_
->set_is_last_chunk_zero_size();
265 drainer_
->Start(session_
.get());
266 EXPECT_FALSE(result_waiter_
.WaitForResult());
269 TEST_F(HttpResponseBodyDrainerTest
, DrainBodySyncEmptyChunk
) {
270 mock_stream_
->set_num_chunks(4);
271 mock_stream_
->set_sync();
272 mock_stream_
->set_is_last_chunk_zero_size();
273 drainer_
->Start(session_
.get());
274 EXPECT_FALSE(result_waiter_
.WaitForResult());
277 TEST_F(HttpResponseBodyDrainerTest
, DrainBodySizeEqualsDrainBuffer
) {
278 mock_stream_
->set_num_chunks(
279 HttpResponseBodyDrainer::kDrainBodyBufferSize
/ kMagicChunkSize
);
280 drainer_
->Start(session_
.get());
281 EXPECT_FALSE(result_waiter_
.WaitForResult());
284 TEST_F(HttpResponseBodyDrainerTest
, DrainBodyTimeOut
) {
285 mock_stream_
->set_num_chunks(2);
286 mock_stream_
->set_stall_reads_forever();
287 drainer_
->Start(session_
.get());
288 EXPECT_TRUE(result_waiter_
.WaitForResult());
291 TEST_F(HttpResponseBodyDrainerTest
, CancelledBySession
) {
292 mock_stream_
->set_num_chunks(2);
293 mock_stream_
->set_stall_reads_forever();
294 drainer_
->Start(session_
.get());
295 // HttpNetworkSession should delete |drainer_|.
298 TEST_F(HttpResponseBodyDrainerTest
, DrainBodyTooLarge
) {
299 int too_many_chunks
=
300 HttpResponseBodyDrainer::kDrainBodyBufferSize
/ kMagicChunkSize
;
301 too_many_chunks
+= 1; // Now it's too large.
303 mock_stream_
->set_num_chunks(too_many_chunks
);
304 drainer_
->Start(session_
.get());
305 EXPECT_TRUE(result_waiter_
.WaitForResult());
308 TEST_F(HttpResponseBodyDrainerTest
, StartBodyTooLarge
) {
309 int too_many_chunks
=
310 HttpResponseBodyDrainer::kDrainBodyBufferSize
/ kMagicChunkSize
;
311 too_many_chunks
+= 1; // Now it's too large.
313 mock_stream_
->set_num_chunks(0);
314 drainer_
->StartWithSize(session_
.get(), too_many_chunks
* kMagicChunkSize
);
315 EXPECT_TRUE(result_waiter_
.WaitForResult());
318 TEST_F(HttpResponseBodyDrainerTest
, StartWithNothingToDo
) {
319 mock_stream_
->set_num_chunks(0);
320 drainer_
->StartWithSize(session_
.get(), 0);
321 EXPECT_FALSE(result_waiter_
.WaitForResult());