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/location.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_errors.h"
18 #include "net/base/test_completion_callback.h"
19 #include "net/http/http_network_session.h"
20 #include "net/http/http_server_properties_impl.h"
21 #include "net/http/http_stream.h"
22 #include "net/http/transport_security_state.h"
23 #include "net/proxy/proxy_service.h"
24 #include "net/ssl/ssl_config_service_defaults.h"
25 #include "testing/gtest/include/gtest/gtest.h"
31 const int kMagicChunkSize
= 1024;
32 static_assert((HttpResponseBodyDrainer::kDrainBodyBufferSize
%
33 kMagicChunkSize
) == 0,
34 "chunk size needs to divide evenly into buffer size");
36 class CloseResultWaiter
{
41 waiting_for_result_(false) {}
44 CHECK(!waiting_for_result_
);
45 while (!have_result_
) {
46 waiting_for_result_
= true;
47 base::MessageLoop::current()->Run();
48 waiting_for_result_
= false;
53 void set_result(bool result
) {
56 if (waiting_for_result_
)
57 base::MessageLoop::current()->Quit();
63 bool waiting_for_result_
;
65 DISALLOW_COPY_AND_ASSIGN(CloseResultWaiter
);
68 class MockHttpStream
: public HttpStream
{
70 MockHttpStream(CloseResultWaiter
* result_waiter
)
71 : result_waiter_(result_waiter
),
74 stall_reads_forever_(false),
77 is_last_chunk_zero_size_(false),
79 weak_factory_(this) {}
80 ~MockHttpStream() override
{}
82 // HttpStream implementation.
83 int InitializeStream(const HttpRequestInfo
* request_info
,
84 RequestPriority priority
,
85 const BoundNetLog
& net_log
,
86 const CompletionCallback
& callback
) override
{
87 return ERR_UNEXPECTED
;
89 int SendRequest(const HttpRequestHeaders
& request_headers
,
90 HttpResponseInfo
* response
,
91 const CompletionCallback
& callback
) override
{
92 return ERR_UNEXPECTED
;
94 UploadProgress
GetUploadProgress() const override
{ return UploadProgress(); }
95 int ReadResponseHeaders(const CompletionCallback
& callback
) override
{
96 return ERR_UNEXPECTED
;
99 bool CanFindEndOfResponse() const override
{ return true; }
100 bool IsConnectionReused() const override
{ return false; }
101 void SetConnectionReused() override
{}
102 bool IsConnectionReusable() const override
{ return false; }
103 int64
GetTotalReceivedBytes() const override
{ return 0; }
104 void GetSSLInfo(SSLInfo
* ssl_info
) override
{}
105 void GetSSLCertRequestInfo(SSLCertRequestInfo
* cert_request_info
) override
{}
108 int ReadResponseBody(IOBuffer
* buf
,
110 const CompletionCallback
& callback
) override
;
111 void Close(bool not_reusable
) override
{
114 result_waiter_
->set_result(not_reusable
);
117 HttpStream
* RenewStreamForAuth() override
{ return NULL
; }
119 bool IsResponseBodyComplete() const override
{ return is_complete_
; }
121 bool IsSpdyHttpStream() const override
{ return false; }
123 bool GetLoadTimingInfo(LoadTimingInfo
* load_timing_info
) const override
{
127 void Drain(HttpNetworkSession
*) override
{}
129 void SetPriority(RequestPriority priority
) override
{}
131 // Methods to tweak/observer mock behavior:
132 void set_stall_reads_forever() { stall_reads_forever_
= true; }
134 void set_num_chunks(int num_chunks
) { num_chunks_
= num_chunks
; }
136 void set_sync() { is_sync_
= true; }
138 void set_is_last_chunk_zero_size() { is_last_chunk_zero_size_
= true; }
141 int ReadResponseBodyImpl(IOBuffer
* buf
, int buf_len
);
144 bool closed() const { return closed_
; }
146 CloseResultWaiter
* const result_waiter_
;
147 scoped_refptr
<IOBuffer
> user_buf_
;
148 CompletionCallback callback_
;
151 bool stall_reads_forever_
;
154 bool is_last_chunk_zero_size_
;
156 base::WeakPtrFactory
<MockHttpStream
> weak_factory_
;
159 int MockHttpStream::ReadResponseBody(IOBuffer
* buf
,
161 const CompletionCallback
& callback
) {
162 CHECK(!callback
.is_null());
163 CHECK(callback_
.is_null());
166 if (stall_reads_forever_
)
167 return ERR_IO_PENDING
;
170 return ERR_UNEXPECTED
;
175 callback_
= callback
;
176 base::ThreadTaskRunnerHandle::Get()->PostTask(
178 base::Bind(&MockHttpStream::CompleteRead
, weak_factory_
.GetWeakPtr()));
179 return ERR_IO_PENDING
;
181 return ReadResponseBodyImpl(buf
, buf_len
);
185 int MockHttpStream::ReadResponseBodyImpl(IOBuffer
* buf
, int buf_len
) {
186 if (is_last_chunk_zero_size_
&& num_chunks_
== 1) {
189 if (buf_len
> kMagicChunkSize
)
190 buf_len
= kMagicChunkSize
;
191 std::memset(buf
->data(), 1, buf_len
);
200 void MockHttpStream::CompleteRead() {
201 int result
= ReadResponseBodyImpl(user_buf_
.get(), buf_len_
);
203 CompletionCallback callback
= callback_
;
205 callback
.Run(result
);
208 class HttpResponseBodyDrainerTest
: public testing::Test
{
210 HttpResponseBodyDrainerTest()
211 : proxy_service_(ProxyService::CreateDirect()),
212 ssl_config_service_(new SSLConfigServiceDefaults
),
213 http_server_properties_(new HttpServerPropertiesImpl()),
214 transport_security_state_(new TransportSecurityState()),
215 session_(CreateNetworkSession()),
216 mock_stream_(new MockHttpStream(&result_waiter_
)),
217 drainer_(new HttpResponseBodyDrainer(mock_stream_
)) {}
219 ~HttpResponseBodyDrainerTest() override
{}
221 HttpNetworkSession
* CreateNetworkSession() const {
222 HttpNetworkSession::Params params
;
223 params
.proxy_service
= proxy_service_
.get();
224 params
.ssl_config_service
= ssl_config_service_
.get();
225 params
.http_server_properties
= http_server_properties_
->GetWeakPtr();
226 params
.transport_security_state
= transport_security_state_
.get();
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 scoped_ptr
<TransportSecurityState
> transport_security_state_
;
234 const scoped_refptr
<HttpNetworkSession
> session_
;
235 CloseResultWaiter result_waiter_
;
236 MockHttpStream
* const mock_stream_
; // Owned by |drainer_|.
237 HttpResponseBodyDrainer
* const drainer_
; // Deletes itself.
240 TEST_F(HttpResponseBodyDrainerTest
, DrainBodySyncSingleOK
) {
241 mock_stream_
->set_num_chunks(1);
242 mock_stream_
->set_sync();
243 drainer_
->Start(session_
.get());
244 EXPECT_FALSE(result_waiter_
.WaitForResult());
247 TEST_F(HttpResponseBodyDrainerTest
, DrainBodySyncOK
) {
248 mock_stream_
->set_num_chunks(3);
249 mock_stream_
->set_sync();
250 drainer_
->Start(session_
.get());
251 EXPECT_FALSE(result_waiter_
.WaitForResult());
254 TEST_F(HttpResponseBodyDrainerTest
, DrainBodyAsyncOK
) {
255 mock_stream_
->set_num_chunks(3);
256 drainer_
->Start(session_
.get());
257 EXPECT_FALSE(result_waiter_
.WaitForResult());
260 // Test the case when the final chunk is 0 bytes. This can happen when
261 // the final 0-byte chunk of a chunk-encoded http response is read in a last
262 // call to ReadResponseBody, after all data were returned from HttpStream.
263 TEST_F(HttpResponseBodyDrainerTest
, DrainBodyAsyncEmptyChunk
) {
264 mock_stream_
->set_num_chunks(4);
265 mock_stream_
->set_is_last_chunk_zero_size();
266 drainer_
->Start(session_
.get());
267 EXPECT_FALSE(result_waiter_
.WaitForResult());
270 TEST_F(HttpResponseBodyDrainerTest
, DrainBodySyncEmptyChunk
) {
271 mock_stream_
->set_num_chunks(4);
272 mock_stream_
->set_sync();
273 mock_stream_
->set_is_last_chunk_zero_size();
274 drainer_
->Start(session_
.get());
275 EXPECT_FALSE(result_waiter_
.WaitForResult());
278 TEST_F(HttpResponseBodyDrainerTest
, DrainBodySizeEqualsDrainBuffer
) {
279 mock_stream_
->set_num_chunks(
280 HttpResponseBodyDrainer::kDrainBodyBufferSize
/ kMagicChunkSize
);
281 drainer_
->Start(session_
.get());
282 EXPECT_FALSE(result_waiter_
.WaitForResult());
285 TEST_F(HttpResponseBodyDrainerTest
, DrainBodyTimeOut
) {
286 mock_stream_
->set_num_chunks(2);
287 mock_stream_
->set_stall_reads_forever();
288 drainer_
->Start(session_
.get());
289 EXPECT_TRUE(result_waiter_
.WaitForResult());
292 TEST_F(HttpResponseBodyDrainerTest
, CancelledBySession
) {
293 mock_stream_
->set_num_chunks(2);
294 mock_stream_
->set_stall_reads_forever();
295 drainer_
->Start(session_
.get());
296 // HttpNetworkSession should delete |drainer_|.
299 TEST_F(HttpResponseBodyDrainerTest
, DrainBodyTooLarge
) {
300 int too_many_chunks
=
301 HttpResponseBodyDrainer::kDrainBodyBufferSize
/ kMagicChunkSize
;
302 too_many_chunks
+= 1; // Now it's too large.
304 mock_stream_
->set_num_chunks(too_many_chunks
);
305 drainer_
->Start(session_
.get());
306 EXPECT_TRUE(result_waiter_
.WaitForResult());