1 // Copyright (c) 2010 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/compiler_specific.h" 10 #include "base/message_loop.h" 11 #include "base/task.h" 12 #include "net/base/io_buffer.h" 13 #include "net/base/net_errors.h" 14 #include "net/base/ssl_config_service_defaults.h" 15 #include "net/base/test_completion_callback.h" 16 #include "net/http/http_network_session.h" 17 #include "net/http/http_stream.h" 18 #include "net/proxy/proxy_service.h" 19 #include "testing/gtest/include/gtest/gtest.h" 20 21 namespace net { 22 23 namespace { 24 25 const int kMagicChunkSize = 1024; 26 COMPILE_ASSERT( 27 (HttpResponseBodyDrainer::kDrainBodyBufferSize % kMagicChunkSize) == 0, 28 chunk_size_needs_to_divide_evenly_into_buffer_size); 29 30 class CloseResultWaiter { 31 public: 32 CloseResultWaiter() 33 : result_(false), 34 have_result_(false), 35 waiting_for_result_(false) {} 36 37 int WaitForResult() { 38 DCHECK(!waiting_for_result_); 39 while (!have_result_) { 40 waiting_for_result_ = true; 41 MessageLoop::current()->Run(); 42 waiting_for_result_ = false; 43 } 44 return result_; 45 } 46 47 void set_result(bool result) { 48 result_ = result; 49 have_result_ = true; 50 if (waiting_for_result_) 51 MessageLoop::current()->Quit(); 52 } 53 54 private: 55 int result_; 56 bool have_result_; 57 bool waiting_for_result_; 58 59 DISALLOW_COPY_AND_ASSIGN(CloseResultWaiter); 60 }; 61 62 class MockHttpStream : public HttpStream { 63 public: 64 MockHttpStream(CloseResultWaiter* result_waiter) 65 : result_waiter_(result_waiter), 66 user_callback_(NULL), 67 closed_(false), 68 stall_reads_forever_(false), 69 num_chunks_(0), 70 is_complete_(false), 71 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {} 72 virtual ~MockHttpStream() {} 73 74 // HttpStream implementation: 75 virtual int InitializeStream(const HttpRequestInfo* request_info, 76 const BoundNetLog& net_log, 77 CompletionCallback* callback) OVERRIDE { 78 return ERR_UNEXPECTED; 79 } 80 virtual int SendRequest(const HttpRequestHeaders& request_headers, 81 UploadDataStream* request_body, 82 HttpResponseInfo* response, 83 CompletionCallback* callback) OVERRIDE { 84 return ERR_UNEXPECTED; 85 } 86 virtual uint64 GetUploadProgress() const OVERRIDE { return 0; } 87 virtual int ReadResponseHeaders(CompletionCallback* callback) OVERRIDE { 88 return ERR_UNEXPECTED; 89 } 90 virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE { 91 return NULL; 92 } 93 94 virtual bool CanFindEndOfResponse() const OVERRIDE { return true; } 95 virtual bool IsMoreDataBuffered() const OVERRIDE { return false; } 96 virtual bool IsConnectionReused() const OVERRIDE { return false; } 97 virtual void SetConnectionReused() OVERRIDE {} 98 virtual bool IsConnectionReusable() const OVERRIDE { return false; } 99 virtual void GetSSLInfo(SSLInfo* ssl_info) OVERRIDE {} 100 virtual void GetSSLCertRequestInfo( 101 SSLCertRequestInfo* cert_request_info) OVERRIDE {} 102 103 // Mocked API 104 virtual int ReadResponseBody(IOBuffer* buf, int buf_len, 105 CompletionCallback* callback) OVERRIDE; 106 virtual void Close(bool not_reusable) OVERRIDE { 107 DCHECK(!closed_); 108 closed_ = true; 109 result_waiter_->set_result(not_reusable); 110 } 111 112 virtual HttpStream* RenewStreamForAuth() OVERRIDE { 113 return NULL; 114 } 115 116 virtual bool IsResponseBodyComplete() const OVERRIDE { return is_complete_; } 117 118 virtual bool IsSpdyHttpStream() const OVERRIDE { return false; } 119 120 // Methods to tweak/observer mock behavior: 121 void StallReadsForever() { stall_reads_forever_ = true; } 122 123 void set_num_chunks(int num_chunks) { num_chunks_ = num_chunks; } 124 125 private: 126 void CompleteRead(); 127 128 bool closed() const { return closed_; } 129 130 CloseResultWaiter* const result_waiter_; 131 scoped_refptr<IOBuffer> user_buf_; 132 CompletionCallback* user_callback_; 133 bool closed_; 134 bool stall_reads_forever_; 135 int num_chunks_; 136 bool is_complete_; 137 ScopedRunnableMethodFactory<MockHttpStream> method_factory_; 138 }; 139 140 int MockHttpStream::ReadResponseBody( 141 IOBuffer* buf, int buf_len, CompletionCallback* callback) { 142 DCHECK(callback); 143 DCHECK(!user_callback_); 144 DCHECK(buf); 145 146 if (stall_reads_forever_) 147 return ERR_IO_PENDING; 148 149 if (num_chunks_ == 0) 150 return ERR_UNEXPECTED; 151 152 if (buf_len > kMagicChunkSize && num_chunks_ > 1) { 153 user_buf_ = buf; 154 user_callback_ = callback; 155 MessageLoop::current()->PostTask( 156 FROM_HERE, 157 method_factory_.NewRunnableMethod(&MockHttpStream::CompleteRead)); 158 return ERR_IO_PENDING; 159 } 160 161 num_chunks_--; 162 if (!num_chunks_) 163 is_complete_ = true; 164 165 return buf_len; 166 } 167 168 void MockHttpStream::CompleteRead() { 169 CompletionCallback* callback = user_callback_; 170 std::memset(user_buf_->data(), 1, kMagicChunkSize); 171 user_buf_ = NULL; 172 user_callback_ = NULL; 173 num_chunks_--; 174 if (!num_chunks_) 175 is_complete_ = true; 176 callback->Run(kMagicChunkSize); 177 } 178 179 class HttpResponseBodyDrainerTest : public testing::Test { 180 protected: 181 HttpResponseBodyDrainerTest() 182 : proxy_service_(ProxyService::CreateDirect()), 183 ssl_config_service_(new SSLConfigServiceDefaults), 184 session_(CreateNetworkSession()), 185 mock_stream_(new MockHttpStream(&result_waiter_)), 186 drainer_(new HttpResponseBodyDrainer(mock_stream_)) {} 187 188 ~HttpResponseBodyDrainerTest() {} 189 190 HttpNetworkSession* CreateNetworkSession() const { 191 HttpNetworkSession::Params params; 192 params.proxy_service = proxy_service_; 193 params.ssl_config_service = ssl_config_service_; 194 return new HttpNetworkSession(params); 195 } 196 197 scoped_refptr<ProxyService> proxy_service_; 198 scoped_refptr<SSLConfigService> ssl_config_service_; 199 const scoped_refptr<HttpNetworkSession> session_; 200 CloseResultWaiter result_waiter_; 201 MockHttpStream* const mock_stream_; // Owned by |drainer_|. 202 HttpResponseBodyDrainer* const drainer_; // Deletes itself. 203 }; 204 205 TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncOK) { 206 mock_stream_->set_num_chunks(1); 207 drainer_->Start(session_); 208 EXPECT_FALSE(result_waiter_.WaitForResult()); 209 } 210 211 TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncOK) { 212 mock_stream_->set_num_chunks(3); 213 drainer_->Start(session_); 214 EXPECT_FALSE(result_waiter_.WaitForResult()); 215 } 216 217 TEST_F(HttpResponseBodyDrainerTest, DrainBodySizeEqualsDrainBuffer) { 218 mock_stream_->set_num_chunks( 219 HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize); 220 drainer_->Start(session_); 221 EXPECT_FALSE(result_waiter_.WaitForResult()); 222 } 223 224 TEST_F(HttpResponseBodyDrainerTest, DrainBodyTimeOut) { 225 mock_stream_->set_num_chunks(2); 226 mock_stream_->StallReadsForever(); 227 drainer_->Start(session_); 228 EXPECT_TRUE(result_waiter_.WaitForResult()); 229 } 230 231 TEST_F(HttpResponseBodyDrainerTest, CancelledBySession) { 232 mock_stream_->set_num_chunks(2); 233 mock_stream_->StallReadsForever(); 234 drainer_->Start(session_); 235 // HttpNetworkSession should delete |drainer_|. 236 } 237 238 TEST_F(HttpResponseBodyDrainerTest, DrainBodyTooLarge) { 239 TestCompletionCallback callback; 240 int too_many_chunks = 241 HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize; 242 too_many_chunks += 1; // Now it's too large. 243 244 mock_stream_->set_num_chunks(too_many_chunks); 245 drainer_->Start(session_); 246 EXPECT_TRUE(result_waiter_.WaitForResult()); 247 } 248 249 } // namespace 250 251 } // namespace net 252