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 <stdint.h>
8
9 #include <cstring>
10 #include <utility>
11
12 #include "base/bind.h"
13 #include "base/compiler_specific.h"
14 #include "base/location.h"
15 #include "base/macros.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/run_loop.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/threading/thread_task_runner_handle.h"
20 #include "net/base/completion_once_callback.h"
21 #include "net/base/io_buffer.h"
22 #include "net/base/net_errors.h"
23 #include "net/base/test_completion_callback.h"
24 #include "net/cert/ct_policy_enforcer.h"
25 #include "net/cert/mock_cert_verifier.h"
26 #include "net/cert/multi_log_ct_verifier.h"
27 #include "net/http/http_network_session.h"
28 #include "net/http/http_server_properties.h"
29 #include "net/http/http_stream.h"
30 #include "net/http/transport_security_state.h"
31 #include "net/proxy_resolution/configured_proxy_resolution_service.h"
32 #include "net/quic/quic_context.h"
33 #include "net/ssl/ssl_config_service_defaults.h"
34 #include "net/test/test_with_task_environment.h"
35 #include "testing/gtest/include/gtest/gtest.h"
36
37 namespace net {
38
39 namespace {
40
41 const int kMagicChunkSize = 1024;
42 static_assert((HttpResponseBodyDrainer::kDrainBodyBufferSize %
43 kMagicChunkSize) == 0,
44 "chunk size needs to divide evenly into buffer size");
45
46 class CloseResultWaiter {
47 public:
CloseResultWaiter()48 CloseResultWaiter()
49 : result_(false),
50 have_result_(false),
51 waiting_for_result_(false) {}
52
WaitForResult()53 int WaitForResult() {
54 CHECK(!waiting_for_result_);
55 while (!have_result_) {
56 waiting_for_result_ = true;
57 base::RunLoop().Run();
58 waiting_for_result_ = false;
59 }
60 return result_;
61 }
62
set_result(bool result)63 void set_result(bool result) {
64 result_ = result;
65 have_result_ = true;
66 if (waiting_for_result_)
67 base::RunLoop::QuitCurrentWhenIdleDeprecated();
68 }
69
70 private:
71 int result_;
72 bool have_result_;
73 bool waiting_for_result_;
74
75 DISALLOW_COPY_AND_ASSIGN(CloseResultWaiter);
76 };
77
78 class MockHttpStream : public HttpStream {
79 public:
MockHttpStream(CloseResultWaiter * result_waiter)80 MockHttpStream(CloseResultWaiter* result_waiter)
81 : result_waiter_(result_waiter),
82 buf_len_(0),
83 closed_(false),
84 stall_reads_forever_(false),
85 num_chunks_(0),
86 is_sync_(false),
87 is_last_chunk_zero_size_(false),
88 is_complete_(false),
89 can_reuse_connection_(true) {}
90 ~MockHttpStream() override = default;
91
92 // HttpStream implementation.
InitializeStream(const HttpRequestInfo * request_info,bool can_send_early,RequestPriority priority,const NetLogWithSource & net_log,CompletionOnceCallback callback)93 int InitializeStream(const HttpRequestInfo* request_info,
94 bool can_send_early,
95 RequestPriority priority,
96 const NetLogWithSource& net_log,
97 CompletionOnceCallback callback) override {
98 return ERR_UNEXPECTED;
99 }
SendRequest(const HttpRequestHeaders & request_headers,HttpResponseInfo * response,CompletionOnceCallback callback)100 int SendRequest(const HttpRequestHeaders& request_headers,
101 HttpResponseInfo* response,
102 CompletionOnceCallback callback) override {
103 return ERR_UNEXPECTED;
104 }
ReadResponseHeaders(CompletionOnceCallback callback)105 int ReadResponseHeaders(CompletionOnceCallback callback) override {
106 return ERR_UNEXPECTED;
107 }
108
IsConnectionReused() const109 bool IsConnectionReused() const override { return false; }
SetConnectionReused()110 void SetConnectionReused() override {}
CanReuseConnection() const111 bool CanReuseConnection() const override { return can_reuse_connection_; }
GetTotalReceivedBytes() const112 int64_t GetTotalReceivedBytes() const override { return 0; }
GetTotalSentBytes() const113 int64_t GetTotalSentBytes() const override { return 0; }
GetAlternativeService(AlternativeService * alternative_service) const114 bool GetAlternativeService(
115 AlternativeService* alternative_service) const override {
116 return false;
117 }
GetSSLInfo(SSLInfo * ssl_info)118 void GetSSLInfo(SSLInfo* ssl_info) override {}
GetSSLCertRequestInfo(SSLCertRequestInfo * cert_request_info)119 void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) override {}
GetRemoteEndpoint(IPEndPoint * endpoint)120 bool GetRemoteEndpoint(IPEndPoint* endpoint) override { return false; }
121
122 // Mocked API
123 int ReadResponseBody(IOBuffer* buf,
124 int buf_len,
125 CompletionOnceCallback callback) override;
Close(bool not_reusable)126 void Close(bool not_reusable) override {
127 CHECK(!closed_);
128 closed_ = true;
129 result_waiter_->set_result(not_reusable);
130 }
131
RenewStreamForAuth()132 HttpStream* RenewStreamForAuth() override { return nullptr; }
133
IsResponseBodyComplete() const134 bool IsResponseBodyComplete() const override { return is_complete_; }
135
GetLoadTimingInfo(LoadTimingInfo * load_timing_info) const136 bool GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const override {
137 return false;
138 }
139
Drain(HttpNetworkSession *)140 void Drain(HttpNetworkSession*) override {}
141
PopulateNetErrorDetails(NetErrorDetails * details)142 void PopulateNetErrorDetails(NetErrorDetails* details) override { return; }
143
SetPriority(RequestPriority priority)144 void SetPriority(RequestPriority priority) override {}
145
146 // Methods to tweak/observer mock behavior:
set_stall_reads_forever()147 void set_stall_reads_forever() { stall_reads_forever_ = true; }
148
set_num_chunks(int num_chunks)149 void set_num_chunks(int num_chunks) { num_chunks_ = num_chunks; }
150
set_sync()151 void set_sync() { is_sync_ = true; }
152
set_is_last_chunk_zero_size()153 void set_is_last_chunk_zero_size() { is_last_chunk_zero_size_ = true; }
154
155 // Sets result value of CanReuseConnection. Defaults to true.
set_can_reuse_connection(bool can_reuse_connection)156 void set_can_reuse_connection(bool can_reuse_connection) {
157 can_reuse_connection_ = can_reuse_connection;
158 }
159
SetRequestHeadersCallback(RequestHeadersCallback callback)160 void SetRequestHeadersCallback(RequestHeadersCallback callback) override {}
161
162 private:
163 int ReadResponseBodyImpl(IOBuffer* buf, int buf_len);
164 void CompleteRead();
165
closed() const166 bool closed() const { return closed_; }
167
168 CloseResultWaiter* const result_waiter_;
169 scoped_refptr<IOBuffer> user_buf_;
170 CompletionOnceCallback callback_;
171 int buf_len_;
172 bool closed_;
173 bool stall_reads_forever_;
174 int num_chunks_;
175 bool is_sync_;
176 bool is_last_chunk_zero_size_;
177 bool is_complete_;
178 bool can_reuse_connection_;
179
180 base::WeakPtrFactory<MockHttpStream> weak_factory_{this};
181
182 DISALLOW_COPY_AND_ASSIGN(MockHttpStream);
183 };
184
ReadResponseBody(IOBuffer * buf,int buf_len,CompletionOnceCallback callback)185 int MockHttpStream::ReadResponseBody(IOBuffer* buf,
186 int buf_len,
187 CompletionOnceCallback callback) {
188 CHECK(!callback.is_null());
189 CHECK(callback_.is_null());
190 CHECK(buf);
191
192 if (stall_reads_forever_)
193 return ERR_IO_PENDING;
194
195 if (is_complete_)
196 return ERR_UNEXPECTED;
197
198 if (!is_sync_) {
199 user_buf_ = buf;
200 buf_len_ = buf_len;
201 callback_ = std::move(callback);
202 base::ThreadTaskRunnerHandle::Get()->PostTask(
203 FROM_HERE, base::BindOnce(&MockHttpStream::CompleteRead,
204 weak_factory_.GetWeakPtr()));
205 return ERR_IO_PENDING;
206 } else {
207 return ReadResponseBodyImpl(buf, buf_len);
208 }
209 }
210
ReadResponseBodyImpl(IOBuffer * buf,int buf_len)211 int MockHttpStream::ReadResponseBodyImpl(IOBuffer* buf, int buf_len) {
212 if (is_last_chunk_zero_size_ && num_chunks_ == 1) {
213 buf_len = 0;
214 } else {
215 if (buf_len > kMagicChunkSize)
216 buf_len = kMagicChunkSize;
217 std::memset(buf->data(), 1, buf_len);
218 }
219 num_chunks_--;
220 if (!num_chunks_)
221 is_complete_ = true;
222
223 return buf_len;
224 }
225
CompleteRead()226 void MockHttpStream::CompleteRead() {
227 int result = ReadResponseBodyImpl(user_buf_.get(), buf_len_);
228 user_buf_ = nullptr;
229 std::move(callback_).Run(result);
230 }
231
232 class HttpResponseBodyDrainerTest : public TestWithTaskEnvironment {
233 protected:
HttpResponseBodyDrainerTest()234 HttpResponseBodyDrainerTest()
235 : proxy_resolution_service_(
236 ConfiguredProxyResolutionService::CreateDirect()),
237 ssl_config_service_(new SSLConfigServiceDefaults),
238 http_server_properties_(new HttpServerProperties()),
239 session_(CreateNetworkSession()),
240 mock_stream_(new MockHttpStream(&result_waiter_)),
241 drainer_(new HttpResponseBodyDrainer(mock_stream_)) {}
242
243 ~HttpResponseBodyDrainerTest() override = default;
244
CreateNetworkSession()245 HttpNetworkSession* CreateNetworkSession() {
246 HttpNetworkSession::Context context;
247 context.proxy_resolution_service = proxy_resolution_service_.get();
248 context.ssl_config_service = ssl_config_service_.get();
249 context.http_server_properties = http_server_properties_.get();
250 context.cert_verifier = &cert_verifier_;
251 context.transport_security_state = &transport_security_state_;
252 context.cert_transparency_verifier = &ct_verifier_;
253 context.ct_policy_enforcer = &ct_policy_enforcer_;
254 context.quic_context = &quic_context_;
255 return new HttpNetworkSession(HttpNetworkSession::Params(), context);
256 }
257
258 std::unique_ptr<ProxyResolutionService> proxy_resolution_service_;
259 std::unique_ptr<SSLConfigService> ssl_config_service_;
260 std::unique_ptr<HttpServerProperties> http_server_properties_;
261 MockCertVerifier cert_verifier_;
262 TransportSecurityState transport_security_state_;
263 MultiLogCTVerifier ct_verifier_;
264 DefaultCTPolicyEnforcer ct_policy_enforcer_;
265 QuicContext quic_context_;
266 const std::unique_ptr<HttpNetworkSession> session_;
267 CloseResultWaiter result_waiter_;
268 MockHttpStream* const mock_stream_; // Owned by |drainer_|.
269 HttpResponseBodyDrainer* const drainer_; // Deletes itself.
270 };
271
TEST_F(HttpResponseBodyDrainerTest,DrainBodySyncSingleOK)272 TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncSingleOK) {
273 mock_stream_->set_num_chunks(1);
274 mock_stream_->set_sync();
275 drainer_->Start(session_.get());
276 EXPECT_FALSE(result_waiter_.WaitForResult());
277 }
278
TEST_F(HttpResponseBodyDrainerTest,DrainBodySyncOK)279 TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncOK) {
280 mock_stream_->set_num_chunks(3);
281 mock_stream_->set_sync();
282 drainer_->Start(session_.get());
283 EXPECT_FALSE(result_waiter_.WaitForResult());
284 }
285
TEST_F(HttpResponseBodyDrainerTest,DrainBodyAsyncOK)286 TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncOK) {
287 mock_stream_->set_num_chunks(3);
288 drainer_->Start(session_.get());
289 EXPECT_FALSE(result_waiter_.WaitForResult());
290 }
291
292 // Test the case when the final chunk is 0 bytes. This can happen when
293 // the final 0-byte chunk of a chunk-encoded http response is read in a last
294 // call to ReadResponseBody, after all data were returned from HttpStream.
TEST_F(HttpResponseBodyDrainerTest,DrainBodyAsyncEmptyChunk)295 TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncEmptyChunk) {
296 mock_stream_->set_num_chunks(4);
297 mock_stream_->set_is_last_chunk_zero_size();
298 drainer_->Start(session_.get());
299 EXPECT_FALSE(result_waiter_.WaitForResult());
300 }
301
TEST_F(HttpResponseBodyDrainerTest,DrainBodySyncEmptyChunk)302 TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncEmptyChunk) {
303 mock_stream_->set_num_chunks(4);
304 mock_stream_->set_sync();
305 mock_stream_->set_is_last_chunk_zero_size();
306 drainer_->Start(session_.get());
307 EXPECT_FALSE(result_waiter_.WaitForResult());
308 }
309
TEST_F(HttpResponseBodyDrainerTest,DrainBodySizeEqualsDrainBuffer)310 TEST_F(HttpResponseBodyDrainerTest, DrainBodySizeEqualsDrainBuffer) {
311 mock_stream_->set_num_chunks(
312 HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize);
313 drainer_->Start(session_.get());
314 EXPECT_FALSE(result_waiter_.WaitForResult());
315 }
316
TEST_F(HttpResponseBodyDrainerTest,DrainBodyTimeOut)317 TEST_F(HttpResponseBodyDrainerTest, DrainBodyTimeOut) {
318 mock_stream_->set_num_chunks(2);
319 mock_stream_->set_stall_reads_forever();
320 drainer_->Start(session_.get());
321 EXPECT_TRUE(result_waiter_.WaitForResult());
322 }
323
TEST_F(HttpResponseBodyDrainerTest,CancelledBySession)324 TEST_F(HttpResponseBodyDrainerTest, CancelledBySession) {
325 mock_stream_->set_num_chunks(2);
326 mock_stream_->set_stall_reads_forever();
327 drainer_->Start(session_.get());
328 // HttpNetworkSession should delete |drainer_|.
329 }
330
TEST_F(HttpResponseBodyDrainerTest,DrainBodyTooLarge)331 TEST_F(HttpResponseBodyDrainerTest, DrainBodyTooLarge) {
332 int too_many_chunks =
333 HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize;
334 too_many_chunks += 1; // Now it's too large.
335
336 mock_stream_->set_num_chunks(too_many_chunks);
337 drainer_->Start(session_.get());
338 EXPECT_TRUE(result_waiter_.WaitForResult());
339 }
340
TEST_F(HttpResponseBodyDrainerTest,DrainBodyCantReuse)341 TEST_F(HttpResponseBodyDrainerTest, DrainBodyCantReuse) {
342 mock_stream_->set_num_chunks(1);
343 mock_stream_->set_can_reuse_connection(false);
344 drainer_->Start(session_.get());
345 EXPECT_TRUE(result_waiter_.WaitForResult());
346 }
347
348 } // namespace
349
350 } // namespace net
351