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