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