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 23 namespace net { 24 25 namespace { 26 27 const int kMagicChunkSize = 1024; 28 COMPILE_ASSERT( 29 (HttpResponseBodyDrainer::kDrainBodyBufferSize % kMagicChunkSize) == 0, 30 chunk_size_needs_to_divide_evenly_into_buffer_size); 31 32 class 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 64 class 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 int64 GetTotalReceivedBytes() const OVERRIDE { return 0; } 105 virtual void GetSSLInfo(SSLInfo* ssl_info) OVERRIDE {} 106 virtual void GetSSLCertRequestInfo( 107 SSLCertRequestInfo* cert_request_info) OVERRIDE {} 108 109 // Mocked API 110 virtual int ReadResponseBody(IOBuffer* buf, int buf_len, 111 const CompletionCallback& callback) OVERRIDE; 112 virtual void Close(bool not_reusable) OVERRIDE { 113 CHECK(!closed_); 114 closed_ = true; 115 result_waiter_->set_result(not_reusable); 116 } 117 118 virtual HttpStream* RenewStreamForAuth() OVERRIDE { 119 return NULL; 120 } 121 122 virtual bool IsResponseBodyComplete() const OVERRIDE { return is_complete_; } 123 124 virtual bool IsSpdyHttpStream() const OVERRIDE { return false; } 125 126 virtual bool GetLoadTimingInfo( 127 LoadTimingInfo* load_timing_info) const OVERRIDE { return false; } 128 129 virtual void Drain(HttpNetworkSession*) OVERRIDE {} 130 131 virtual void SetPriority(RequestPriority priority) OVERRIDE {} 132 133 // Methods to tweak/observer mock behavior: 134 void set_stall_reads_forever() { stall_reads_forever_ = true; } 135 136 void set_num_chunks(int num_chunks) { num_chunks_ = num_chunks; } 137 138 void set_sync() { is_sync_ = true; } 139 140 void set_is_last_chunk_zero_size() { is_last_chunk_zero_size_ = true; } 141 142 private: 143 int ReadResponseBodyImpl(IOBuffer* buf, int buf_len); 144 void CompleteRead(); 145 146 bool closed() const { return closed_; } 147 148 CloseResultWaiter* const result_waiter_; 149 scoped_refptr<IOBuffer> user_buf_; 150 CompletionCallback callback_; 151 int buf_len_; 152 bool closed_; 153 bool stall_reads_forever_; 154 int num_chunks_; 155 bool is_sync_; 156 bool is_last_chunk_zero_size_; 157 bool is_complete_; 158 base::WeakPtrFactory<MockHttpStream> weak_factory_; 159 }; 160 161 int MockHttpStream::ReadResponseBody(IOBuffer* buf, 162 int buf_len, 163 const CompletionCallback& callback) { 164 CHECK(!callback.is_null()); 165 CHECK(callback_.is_null()); 166 CHECK(buf); 167 168 if (stall_reads_forever_) 169 return ERR_IO_PENDING; 170 171 if (is_complete_) 172 return ERR_UNEXPECTED; 173 174 if (!is_sync_) { 175 user_buf_ = buf; 176 buf_len_ = buf_len; 177 callback_ = callback; 178 base::MessageLoop::current()->PostTask( 179 FROM_HERE, 180 base::Bind(&MockHttpStream::CompleteRead, weak_factory_.GetWeakPtr())); 181 return ERR_IO_PENDING; 182 } else { 183 return ReadResponseBodyImpl(buf, buf_len); 184 } 185 } 186 187 int MockHttpStream::ReadResponseBodyImpl(IOBuffer* buf, int buf_len) { 188 if (is_last_chunk_zero_size_ && num_chunks_ == 1) { 189 buf_len = 0; 190 } else { 191 if (buf_len > kMagicChunkSize) 192 buf_len = kMagicChunkSize; 193 std::memset(buf->data(), 1, buf_len); 194 } 195 num_chunks_--; 196 if (!num_chunks_) 197 is_complete_ = true; 198 199 return buf_len; 200 } 201 202 void MockHttpStream::CompleteRead() { 203 int result = ReadResponseBodyImpl(user_buf_.get(), buf_len_); 204 user_buf_ = NULL; 205 CompletionCallback callback = callback_; 206 callback_.Reset(); 207 callback.Run(result); 208 } 209 210 class HttpResponseBodyDrainerTest : public testing::Test { 211 protected: 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_)) {} 219 220 virtual ~HttpResponseBodyDrainerTest() {} 221 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); 228 } 229 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. 237 }; 238 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()); 244 } 245 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()); 251 } 252 253 TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncOK) { 254 mock_stream_->set_num_chunks(3); 255 drainer_->Start(session_.get()); 256 EXPECT_FALSE(result_waiter_.WaitForResult()); 257 } 258 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()); 267 } 268 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()); 275 } 276 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()); 282 } 283 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()); 289 } 290 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_|. 296 } 297 298 TEST_F(HttpResponseBodyDrainerTest, DrainBodyTooLarge) { 299 int too_many_chunks = 300 HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize; 301 too_many_chunks += 1; // Now it's too large. 302 303 mock_stream_->set_num_chunks(too_many_chunks); 304 drainer_->Start(session_.get()); 305 EXPECT_TRUE(result_waiter_.WaitForResult()); 306 } 307 308 TEST_F(HttpResponseBodyDrainerTest, StartBodyTooLarge) { 309 int too_many_chunks = 310 HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize; 311 too_many_chunks += 1; // Now it's too large. 312 313 mock_stream_->set_num_chunks(0); 314 drainer_->StartWithSize(session_.get(), too_many_chunks * kMagicChunkSize); 315 EXPECT_TRUE(result_waiter_.WaitForResult()); 316 } 317 318 TEST_F(HttpResponseBodyDrainerTest, StartWithNothingToDo) { 319 mock_stream_->set_num_chunks(0); 320 drainer_->StartWithSize(session_.get(), 0); 321 EXPECT_FALSE(result_waiter_.WaitForResult()); 322 } 323 324 } // namespace 325 326 } // namespace net 327