| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/http/http_response_body_drainer.h" | 5 #include "net/http/http_response_body_drainer.h" |
| 6 | 6 |
| 7 #include <cstring> | 7 #include <cstring> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
| (...skipping 20 matching lines...) Expand all Loading... |
| 31 chunk_size_needs_to_divide_evenly_into_buffer_size); | 31 chunk_size_needs_to_divide_evenly_into_buffer_size); |
| 32 | 32 |
| 33 class CloseResultWaiter { | 33 class CloseResultWaiter { |
| 34 public: | 34 public: |
| 35 CloseResultWaiter() | 35 CloseResultWaiter() |
| 36 : result_(false), | 36 : result_(false), |
| 37 have_result_(false), | 37 have_result_(false), |
| 38 waiting_for_result_(false) {} | 38 waiting_for_result_(false) {} |
| 39 | 39 |
| 40 int WaitForResult() { | 40 int WaitForResult() { |
| 41 DCHECK(!waiting_for_result_); | 41 CHECK(!waiting_for_result_); |
| 42 while (!have_result_) { | 42 while (!have_result_) { |
| 43 waiting_for_result_ = true; | 43 waiting_for_result_ = true; |
| 44 MessageLoop::current()->Run(); | 44 MessageLoop::current()->Run(); |
| 45 waiting_for_result_ = false; | 45 waiting_for_result_ = false; |
| 46 } | 46 } |
| 47 return result_; | 47 return result_; |
| 48 } | 48 } |
| 49 | 49 |
| 50 void set_result(bool result) { | 50 void set_result(bool result) { |
| 51 result_ = result; | 51 result_ = result; |
| 52 have_result_ = true; | 52 have_result_ = true; |
| 53 if (waiting_for_result_) | 53 if (waiting_for_result_) |
| 54 MessageLoop::current()->Quit(); | 54 MessageLoop::current()->Quit(); |
| 55 } | 55 } |
| 56 | 56 |
| 57 private: | 57 private: |
| 58 int result_; | 58 int result_; |
| 59 bool have_result_; | 59 bool have_result_; |
| 60 bool waiting_for_result_; | 60 bool waiting_for_result_; |
| 61 | 61 |
| 62 DISALLOW_COPY_AND_ASSIGN(CloseResultWaiter); | 62 DISALLOW_COPY_AND_ASSIGN(CloseResultWaiter); |
| 63 }; | 63 }; |
| 64 | 64 |
| 65 class MockHttpStream : public HttpStream { | 65 class MockHttpStream : public HttpStream { |
| 66 public: | 66 public: |
| 67 MockHttpStream(CloseResultWaiter* result_waiter) | 67 MockHttpStream(CloseResultWaiter* result_waiter) |
| 68 : result_waiter_(result_waiter), | 68 : result_waiter_(result_waiter), |
| 69 buf_len_(0), |
| 69 closed_(false), | 70 closed_(false), |
| 70 stall_reads_forever_(false), | 71 stall_reads_forever_(false), |
| 71 num_chunks_(0), | 72 num_chunks_(0), |
| 73 is_sync_(false), |
| 74 is_last_chunk_zero_size_(false), |
| 72 is_complete_(false), | 75 is_complete_(false), |
| 73 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {} | 76 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {} |
| 74 virtual ~MockHttpStream() {} | 77 virtual ~MockHttpStream() {} |
| 75 | 78 |
| 76 // HttpStream implementation. | 79 // HttpStream implementation. |
| 77 virtual int InitializeStream(const HttpRequestInfo* request_info, | 80 virtual int InitializeStream(const HttpRequestInfo* request_info, |
| 78 const BoundNetLog& net_log, | 81 const BoundNetLog& net_log, |
| 79 const CompletionCallback& callback) OVERRIDE { | 82 const CompletionCallback& callback) OVERRIDE { |
| 80 return ERR_UNEXPECTED; | 83 return ERR_UNEXPECTED; |
| 81 } | 84 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 101 virtual void SetConnectionReused() OVERRIDE {} | 104 virtual void SetConnectionReused() OVERRIDE {} |
| 102 virtual bool IsConnectionReusable() const OVERRIDE { return false; } | 105 virtual bool IsConnectionReusable() const OVERRIDE { return false; } |
| 103 virtual void GetSSLInfo(SSLInfo* ssl_info) OVERRIDE {} | 106 virtual void GetSSLInfo(SSLInfo* ssl_info) OVERRIDE {} |
| 104 virtual void GetSSLCertRequestInfo( | 107 virtual void GetSSLCertRequestInfo( |
| 105 SSLCertRequestInfo* cert_request_info) OVERRIDE {} | 108 SSLCertRequestInfo* cert_request_info) OVERRIDE {} |
| 106 | 109 |
| 107 // Mocked API | 110 // Mocked API |
| 108 virtual int ReadResponseBody(IOBuffer* buf, int buf_len, | 111 virtual int ReadResponseBody(IOBuffer* buf, int buf_len, |
| 109 const CompletionCallback& callback) OVERRIDE; | 112 const CompletionCallback& callback) OVERRIDE; |
| 110 virtual void Close(bool not_reusable) OVERRIDE { | 113 virtual void Close(bool not_reusable) OVERRIDE { |
| 111 DCHECK(!closed_); | 114 CHECK(!closed_); |
| 112 closed_ = true; | 115 closed_ = true; |
| 113 result_waiter_->set_result(not_reusable); | 116 result_waiter_->set_result(not_reusable); |
| 114 } | 117 } |
| 115 | 118 |
| 116 virtual HttpStream* RenewStreamForAuth() OVERRIDE { | 119 virtual HttpStream* RenewStreamForAuth() OVERRIDE { |
| 117 return NULL; | 120 return NULL; |
| 118 } | 121 } |
| 119 | 122 |
| 120 virtual bool IsResponseBodyComplete() const OVERRIDE { return is_complete_; } | 123 virtual bool IsResponseBodyComplete() const OVERRIDE { return is_complete_; } |
| 121 | 124 |
| 122 virtual bool IsSpdyHttpStream() const OVERRIDE { return false; } | 125 virtual bool IsSpdyHttpStream() const OVERRIDE { return false; } |
| 123 | 126 |
| 124 virtual void LogNumRttVsBytesMetrics() const OVERRIDE {} | 127 virtual void LogNumRttVsBytesMetrics() const OVERRIDE {} |
| 125 | 128 |
| 126 virtual void Drain(HttpNetworkSession*) OVERRIDE {} | 129 virtual void Drain(HttpNetworkSession*) OVERRIDE {} |
| 127 | 130 |
| 128 // Methods to tweak/observer mock behavior: | 131 // Methods to tweak/observer mock behavior: |
| 129 void StallReadsForever() { stall_reads_forever_ = true; } | 132 void set_stall_reads_forever() { stall_reads_forever_ = true; } |
| 130 | 133 |
| 131 void set_num_chunks(int num_chunks) { num_chunks_ = num_chunks; } | 134 void set_num_chunks(int num_chunks) { num_chunks_ = num_chunks; } |
| 132 | 135 |
| 136 void set_sync() { is_sync_ = true; } |
| 137 |
| 138 void set_is_last_chunk_zero_size() { is_last_chunk_zero_size_ = true; } |
| 139 |
| 133 private: | 140 private: |
| 141 int ReadResponseBodyImpl(IOBuffer* buf, int buf_len); |
| 134 void CompleteRead(); | 142 void CompleteRead(); |
| 135 | 143 |
| 136 bool closed() const { return closed_; } | 144 bool closed() const { return closed_; } |
| 137 | 145 |
| 138 CloseResultWaiter* const result_waiter_; | 146 CloseResultWaiter* const result_waiter_; |
| 139 scoped_refptr<IOBuffer> user_buf_; | 147 scoped_refptr<IOBuffer> user_buf_; |
| 140 CompletionCallback callback_; | 148 CompletionCallback callback_; |
| 149 int buf_len_; |
| 141 bool closed_; | 150 bool closed_; |
| 142 bool stall_reads_forever_; | 151 bool stall_reads_forever_; |
| 143 int num_chunks_; | 152 int num_chunks_; |
| 153 bool is_sync_; |
| 154 bool is_last_chunk_zero_size_; |
| 144 bool is_complete_; | 155 bool is_complete_; |
| 145 base::WeakPtrFactory<MockHttpStream> weak_factory_; | 156 base::WeakPtrFactory<MockHttpStream> weak_factory_; |
| 146 }; | 157 }; |
| 147 | 158 |
| 148 int MockHttpStream::ReadResponseBody( | 159 int MockHttpStream::ReadResponseBody(IOBuffer* buf, |
| 149 IOBuffer* buf, int buf_len, const CompletionCallback& callback) { | 160 int buf_len, |
| 150 DCHECK(!callback.is_null()); | 161 const CompletionCallback& callback) { |
| 151 DCHECK(callback_.is_null()); | 162 CHECK(!callback.is_null()); |
| 152 DCHECK(buf); | 163 CHECK(callback_.is_null()); |
| 164 CHECK(buf); |
| 153 | 165 |
| 154 if (stall_reads_forever_) | 166 if (stall_reads_forever_) |
| 155 return ERR_IO_PENDING; | 167 return ERR_IO_PENDING; |
| 156 | 168 |
| 157 if (num_chunks_ == 0) | 169 if (is_complete_) |
| 158 return ERR_UNEXPECTED; | 170 return ERR_UNEXPECTED; |
| 159 | 171 |
| 160 if (buf_len > kMagicChunkSize && num_chunks_ > 1) { | 172 if (!is_sync_) { |
| 161 user_buf_ = buf; | 173 user_buf_ = buf; |
| 174 buf_len_ = buf_len; |
| 162 callback_ = callback; | 175 callback_ = callback; |
| 163 MessageLoop::current()->PostTask( | 176 MessageLoop::current()->PostTask( |
| 164 FROM_HERE, | 177 FROM_HERE, |
| 165 base::Bind(&MockHttpStream::CompleteRead, weak_factory_.GetWeakPtr())); | 178 base::Bind(&MockHttpStream::CompleteRead, weak_factory_.GetWeakPtr())); |
| 166 return ERR_IO_PENDING; | 179 return ERR_IO_PENDING; |
| 180 } else { |
| 181 return ReadResponseBodyImpl(buf, buf_len); |
| 167 } | 182 } |
| 183 } |
| 168 | 184 |
| 185 int MockHttpStream::ReadResponseBodyImpl(IOBuffer* buf, int buf_len) { |
| 186 if (is_last_chunk_zero_size_ && num_chunks_ == 1) { |
| 187 buf_len = 0; |
| 188 } else { |
| 189 if (buf_len > kMagicChunkSize) |
| 190 buf_len = kMagicChunkSize; |
| 191 std::memset(buf->data(), 1, buf_len); |
| 192 } |
| 169 num_chunks_--; | 193 num_chunks_--; |
| 170 if (!num_chunks_) | 194 if (!num_chunks_) |
| 171 is_complete_ = true; | 195 is_complete_ = true; |
| 172 | 196 |
| 173 return buf_len; | 197 return buf_len; |
| 174 } | 198 } |
| 175 | 199 |
| 176 void MockHttpStream::CompleteRead() { | 200 void MockHttpStream::CompleteRead() { |
| 201 int result = ReadResponseBodyImpl(user_buf_, buf_len_); |
| 202 user_buf_ = NULL; |
| 177 CompletionCallback callback = callback_; | 203 CompletionCallback callback = callback_; |
| 178 std::memset(user_buf_->data(), 1, kMagicChunkSize); | |
| 179 user_buf_ = NULL; | |
| 180 callback_.Reset(); | 204 callback_.Reset(); |
| 181 num_chunks_--; | 205 callback.Run(result); |
| 182 if (!num_chunks_) | |
| 183 is_complete_ = true; | |
| 184 callback.Run(kMagicChunkSize); | |
| 185 } | 206 } |
| 186 | 207 |
| 187 class HttpResponseBodyDrainerTest : public testing::Test { | 208 class HttpResponseBodyDrainerTest : public testing::Test { |
| 188 protected: | 209 protected: |
| 189 HttpResponseBodyDrainerTest() | 210 HttpResponseBodyDrainerTest() |
| 190 : proxy_service_(ProxyService::CreateDirect()), | 211 : proxy_service_(ProxyService::CreateDirect()), |
| 191 ssl_config_service_(new SSLConfigServiceDefaults), | 212 ssl_config_service_(new SSLConfigServiceDefaults), |
| 192 http_server_properties_(new HttpServerPropertiesImpl), | 213 http_server_properties_(new HttpServerPropertiesImpl), |
| 193 session_(CreateNetworkSession()), | 214 session_(CreateNetworkSession()), |
| 194 mock_stream_(new MockHttpStream(&result_waiter_)), | 215 mock_stream_(new MockHttpStream(&result_waiter_)), |
| (...skipping 11 matching lines...) Expand all Loading... |
| 206 | 227 |
| 207 scoped_ptr<ProxyService> proxy_service_; | 228 scoped_ptr<ProxyService> proxy_service_; |
| 208 scoped_refptr<SSLConfigService> ssl_config_service_; | 229 scoped_refptr<SSLConfigService> ssl_config_service_; |
| 209 scoped_ptr<HttpServerPropertiesImpl> http_server_properties_; | 230 scoped_ptr<HttpServerPropertiesImpl> http_server_properties_; |
| 210 const scoped_refptr<HttpNetworkSession> session_; | 231 const scoped_refptr<HttpNetworkSession> session_; |
| 211 CloseResultWaiter result_waiter_; | 232 CloseResultWaiter result_waiter_; |
| 212 MockHttpStream* const mock_stream_; // Owned by |drainer_|. | 233 MockHttpStream* const mock_stream_; // Owned by |drainer_|. |
| 213 HttpResponseBodyDrainer* const drainer_; // Deletes itself. | 234 HttpResponseBodyDrainer* const drainer_; // Deletes itself. |
| 214 }; | 235 }; |
| 215 | 236 |
| 216 TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncOK) { | 237 TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncSingleOK) { |
| 217 mock_stream_->set_num_chunks(1); | 238 mock_stream_->set_num_chunks(1); |
| 239 mock_stream_->set_sync(); |
| 218 drainer_->Start(session_); | 240 drainer_->Start(session_); |
| 219 EXPECT_FALSE(result_waiter_.WaitForResult()); | 241 EXPECT_FALSE(result_waiter_.WaitForResult()); |
| 220 } | 242 } |
| 243 |
| 244 TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncOK) { |
| 245 mock_stream_->set_num_chunks(3); |
| 246 mock_stream_->set_sync(); |
| 247 drainer_->Start(session_); |
| 248 EXPECT_FALSE(result_waiter_.WaitForResult()); |
| 249 } |
| 221 | 250 |
| 222 TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncOK) { | 251 TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncOK) { |
| 223 mock_stream_->set_num_chunks(3); | 252 mock_stream_->set_num_chunks(3); |
| 224 drainer_->Start(session_); | 253 drainer_->Start(session_); |
| 225 EXPECT_FALSE(result_waiter_.WaitForResult()); | 254 EXPECT_FALSE(result_waiter_.WaitForResult()); |
| 226 } | 255 } |
| 227 | 256 |
| 257 // Test the case when the final chunk is 0 bytes. This can happen when |
| 258 // the final 0-byte chunk of a chunk-encoded http response is read in a last |
| 259 // call to ReadResponseBody, after all data were returned from HttpStream. |
| 260 TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncEmptyChunk) { |
| 261 mock_stream_->set_num_chunks(4); |
| 262 mock_stream_->set_is_last_chunk_zero_size(); |
| 263 drainer_->Start(session_); |
| 264 EXPECT_FALSE(result_waiter_.WaitForResult()); |
| 265 } |
| 266 |
| 267 TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncEmptyChunk) { |
| 268 mock_stream_->set_num_chunks(4); |
| 269 mock_stream_->set_sync(); |
| 270 mock_stream_->set_is_last_chunk_zero_size(); |
| 271 drainer_->Start(session_); |
| 272 EXPECT_FALSE(result_waiter_.WaitForResult()); |
| 273 } |
| 274 |
| 228 TEST_F(HttpResponseBodyDrainerTest, DrainBodySizeEqualsDrainBuffer) { | 275 TEST_F(HttpResponseBodyDrainerTest, DrainBodySizeEqualsDrainBuffer) { |
| 229 mock_stream_->set_num_chunks( | 276 mock_stream_->set_num_chunks( |
| 230 HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize); | 277 HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize); |
| 231 drainer_->Start(session_); | 278 drainer_->Start(session_); |
| 232 EXPECT_FALSE(result_waiter_.WaitForResult()); | 279 EXPECT_FALSE(result_waiter_.WaitForResult()); |
| 233 } | 280 } |
| 234 | 281 |
| 235 TEST_F(HttpResponseBodyDrainerTest, DrainBodyTimeOut) { | 282 TEST_F(HttpResponseBodyDrainerTest, DrainBodyTimeOut) { |
| 236 mock_stream_->set_num_chunks(2); | 283 mock_stream_->set_num_chunks(2); |
| 237 mock_stream_->StallReadsForever(); | 284 mock_stream_->set_stall_reads_forever(); |
| 238 drainer_->Start(session_); | 285 drainer_->Start(session_); |
| 239 EXPECT_TRUE(result_waiter_.WaitForResult()); | 286 EXPECT_TRUE(result_waiter_.WaitForResult()); |
| 240 } | 287 } |
| 241 | 288 |
| 242 TEST_F(HttpResponseBodyDrainerTest, CancelledBySession) { | 289 TEST_F(HttpResponseBodyDrainerTest, CancelledBySession) { |
| 243 mock_stream_->set_num_chunks(2); | 290 mock_stream_->set_num_chunks(2); |
| 244 mock_stream_->StallReadsForever(); | 291 mock_stream_->set_stall_reads_forever(); |
| 245 drainer_->Start(session_); | 292 drainer_->Start(session_); |
| 246 // HttpNetworkSession should delete |drainer_|. | 293 // HttpNetworkSession should delete |drainer_|. |
| 247 } | 294 } |
| 248 | 295 |
| 249 TEST_F(HttpResponseBodyDrainerTest, DrainBodyTooLarge) { | 296 TEST_F(HttpResponseBodyDrainerTest, DrainBodyTooLarge) { |
| 250 int too_many_chunks = | 297 int too_many_chunks = |
| 251 HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize; | 298 HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize; |
| 252 too_many_chunks += 1; // Now it's too large. | 299 too_many_chunks += 1; // Now it's too large. |
| 253 | 300 |
| 254 mock_stream_->set_num_chunks(too_many_chunks); | 301 mock_stream_->set_num_chunks(too_many_chunks); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 268 | 315 |
| 269 TEST_F(HttpResponseBodyDrainerTest, StartWithNothingToDo) { | 316 TEST_F(HttpResponseBodyDrainerTest, StartWithNothingToDo) { |
| 270 mock_stream_->set_num_chunks(0); | 317 mock_stream_->set_num_chunks(0); |
| 271 drainer_->StartWithSize(session_, 0); | 318 drainer_->StartWithSize(session_, 0); |
| 272 EXPECT_FALSE(result_waiter_.WaitForResult()); | 319 EXPECT_FALSE(result_waiter_.WaitForResult()); |
| 273 } | 320 } |
| 274 | 321 |
| 275 } // namespace | 322 } // namespace |
| 276 | 323 |
| 277 } // namespace net | 324 } // namespace net |
| OLD | NEW |