| OLD | NEW |
| (Empty) |
| 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/http/transport_security_state.h" | |
| 20 #include "net/proxy/proxy_service.h" | |
| 21 #include "net/ssl/ssl_config_service_defaults.h" | |
| 22 #include "testing/gtest/include/gtest/gtest.h" | |
| 23 | |
| 24 namespace net { | |
| 25 | |
| 26 namespace { | |
| 27 | |
| 28 const int kMagicChunkSize = 1024; | |
| 29 static_assert((HttpResponseBodyDrainer::kDrainBodyBufferSize % | |
| 30 kMagicChunkSize) == 0, | |
| 31 "chunk size needs to divide evenly into buffer size"); | |
| 32 | |
| 33 class CloseResultWaiter { | |
| 34 public: | |
| 35 CloseResultWaiter() | |
| 36 : result_(false), | |
| 37 have_result_(false), | |
| 38 waiting_for_result_(false) {} | |
| 39 | |
| 40 int WaitForResult() { | |
| 41 CHECK(!waiting_for_result_); | |
| 42 while (!have_result_) { | |
| 43 waiting_for_result_ = true; | |
| 44 base::MessageLoop::current()->Run(); | |
| 45 waiting_for_result_ = false; | |
| 46 } | |
| 47 return result_; | |
| 48 } | |
| 49 | |
| 50 void set_result(bool result) { | |
| 51 result_ = result; | |
| 52 have_result_ = true; | |
| 53 if (waiting_for_result_) | |
| 54 base::MessageLoop::current()->Quit(); | |
| 55 } | |
| 56 | |
| 57 private: | |
| 58 int result_; | |
| 59 bool have_result_; | |
| 60 bool waiting_for_result_; | |
| 61 | |
| 62 DISALLOW_COPY_AND_ASSIGN(CloseResultWaiter); | |
| 63 }; | |
| 64 | |
| 65 class MockHttpStream : public HttpStream { | |
| 66 public: | |
| 67 MockHttpStream(CloseResultWaiter* result_waiter) | |
| 68 : result_waiter_(result_waiter), | |
| 69 buf_len_(0), | |
| 70 closed_(false), | |
| 71 stall_reads_forever_(false), | |
| 72 num_chunks_(0), | |
| 73 is_sync_(false), | |
| 74 is_last_chunk_zero_size_(false), | |
| 75 is_complete_(false), | |
| 76 weak_factory_(this) {} | |
| 77 ~MockHttpStream() override {} | |
| 78 | |
| 79 // HttpStream implementation. | |
| 80 int InitializeStream(const HttpRequestInfo* request_info, | |
| 81 RequestPriority priority, | |
| 82 const BoundNetLog& net_log, | |
| 83 const CompletionCallback& callback) override { | |
| 84 return ERR_UNEXPECTED; | |
| 85 } | |
| 86 int SendRequest(const HttpRequestHeaders& request_headers, | |
| 87 HttpResponseInfo* response, | |
| 88 const CompletionCallback& callback) override { | |
| 89 return ERR_UNEXPECTED; | |
| 90 } | |
| 91 UploadProgress GetUploadProgress() const override { return UploadProgress(); } | |
| 92 int ReadResponseHeaders(const CompletionCallback& callback) override { | |
| 93 return ERR_UNEXPECTED; | |
| 94 } | |
| 95 | |
| 96 bool CanFindEndOfResponse() const override { return true; } | |
| 97 bool IsConnectionReused() const override { return false; } | |
| 98 void SetConnectionReused() override {} | |
| 99 bool IsConnectionReusable() const override { return false; } | |
| 100 int64 GetTotalReceivedBytes() const override { return 0; } | |
| 101 void GetSSLInfo(SSLInfo* ssl_info) override {} | |
| 102 void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) override {} | |
| 103 | |
| 104 // Mocked API | |
| 105 int ReadResponseBody(IOBuffer* buf, | |
| 106 int buf_len, | |
| 107 const CompletionCallback& callback) override; | |
| 108 void Close(bool not_reusable) override { | |
| 109 CHECK(!closed_); | |
| 110 closed_ = true; | |
| 111 result_waiter_->set_result(not_reusable); | |
| 112 } | |
| 113 | |
| 114 HttpStream* RenewStreamForAuth() override { return NULL; } | |
| 115 | |
| 116 bool IsResponseBodyComplete() const override { return is_complete_; } | |
| 117 | |
| 118 bool IsSpdyHttpStream() const override { return false; } | |
| 119 | |
| 120 bool GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const override { | |
| 121 return false; | |
| 122 } | |
| 123 | |
| 124 void Drain(HttpNetworkSession*) override {} | |
| 125 | |
| 126 void SetPriority(RequestPriority priority) override {} | |
| 127 | |
| 128 // Methods to tweak/observer mock behavior: | |
| 129 void set_stall_reads_forever() { stall_reads_forever_ = true; } | |
| 130 | |
| 131 void set_num_chunks(int num_chunks) { num_chunks_ = num_chunks; } | |
| 132 | |
| 133 void set_sync() { is_sync_ = true; } | |
| 134 | |
| 135 void set_is_last_chunk_zero_size() { is_last_chunk_zero_size_ = true; } | |
| 136 | |
| 137 private: | |
| 138 int ReadResponseBodyImpl(IOBuffer* buf, int buf_len); | |
| 139 void CompleteRead(); | |
| 140 | |
| 141 bool closed() const { return closed_; } | |
| 142 | |
| 143 CloseResultWaiter* const result_waiter_; | |
| 144 scoped_refptr<IOBuffer> user_buf_; | |
| 145 CompletionCallback callback_; | |
| 146 int buf_len_; | |
| 147 bool closed_; | |
| 148 bool stall_reads_forever_; | |
| 149 int num_chunks_; | |
| 150 bool is_sync_; | |
| 151 bool is_last_chunk_zero_size_; | |
| 152 bool is_complete_; | |
| 153 base::WeakPtrFactory<MockHttpStream> weak_factory_; | |
| 154 }; | |
| 155 | |
| 156 int MockHttpStream::ReadResponseBody(IOBuffer* buf, | |
| 157 int buf_len, | |
| 158 const CompletionCallback& callback) { | |
| 159 CHECK(!callback.is_null()); | |
| 160 CHECK(callback_.is_null()); | |
| 161 CHECK(buf); | |
| 162 | |
| 163 if (stall_reads_forever_) | |
| 164 return ERR_IO_PENDING; | |
| 165 | |
| 166 if (is_complete_) | |
| 167 return ERR_UNEXPECTED; | |
| 168 | |
| 169 if (!is_sync_) { | |
| 170 user_buf_ = buf; | |
| 171 buf_len_ = buf_len; | |
| 172 callback_ = callback; | |
| 173 base::MessageLoop::current()->PostTask( | |
| 174 FROM_HERE, | |
| 175 base::Bind(&MockHttpStream::CompleteRead, weak_factory_.GetWeakPtr())); | |
| 176 return ERR_IO_PENDING; | |
| 177 } else { | |
| 178 return ReadResponseBodyImpl(buf, buf_len); | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 int MockHttpStream::ReadResponseBodyImpl(IOBuffer* buf, int buf_len) { | |
| 183 if (is_last_chunk_zero_size_ && num_chunks_ == 1) { | |
| 184 buf_len = 0; | |
| 185 } else { | |
| 186 if (buf_len > kMagicChunkSize) | |
| 187 buf_len = kMagicChunkSize; | |
| 188 std::memset(buf->data(), 1, buf_len); | |
| 189 } | |
| 190 num_chunks_--; | |
| 191 if (!num_chunks_) | |
| 192 is_complete_ = true; | |
| 193 | |
| 194 return buf_len; | |
| 195 } | |
| 196 | |
| 197 void MockHttpStream::CompleteRead() { | |
| 198 int result = ReadResponseBodyImpl(user_buf_.get(), buf_len_); | |
| 199 user_buf_ = NULL; | |
| 200 CompletionCallback callback = callback_; | |
| 201 callback_.Reset(); | |
| 202 callback.Run(result); | |
| 203 } | |
| 204 | |
| 205 class HttpResponseBodyDrainerTest : public testing::Test { | |
| 206 protected: | |
| 207 HttpResponseBodyDrainerTest() | |
| 208 : proxy_service_(ProxyService::CreateDirect()), | |
| 209 ssl_config_service_(new SSLConfigServiceDefaults), | |
| 210 http_server_properties_(new HttpServerPropertiesImpl()), | |
| 211 transport_security_state_(new TransportSecurityState()), | |
| 212 session_(CreateNetworkSession()), | |
| 213 mock_stream_(new MockHttpStream(&result_waiter_)), | |
| 214 drainer_(new HttpResponseBodyDrainer(mock_stream_)) {} | |
| 215 | |
| 216 ~HttpResponseBodyDrainerTest() override {} | |
| 217 | |
| 218 HttpNetworkSession* CreateNetworkSession() const { | |
| 219 HttpNetworkSession::Params params; | |
| 220 params.proxy_service = proxy_service_.get(); | |
| 221 params.ssl_config_service = ssl_config_service_.get(); | |
| 222 params.http_server_properties = http_server_properties_->GetWeakPtr(); | |
| 223 params.transport_security_state = transport_security_state_.get(); | |
| 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 scoped_ptr<TransportSecurityState> transport_security_state_; | |
| 231 const scoped_refptr<HttpNetworkSession> session_; | |
| 232 CloseResultWaiter result_waiter_; | |
| 233 MockHttpStream* const mock_stream_; // Owned by |drainer_|. | |
| 234 HttpResponseBodyDrainer* const drainer_; // Deletes itself. | |
| 235 }; | |
| 236 | |
| 237 TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncSingleOK) { | |
| 238 mock_stream_->set_num_chunks(1); | |
| 239 mock_stream_->set_sync(); | |
| 240 drainer_->Start(session_.get()); | |
| 241 EXPECT_FALSE(result_waiter_.WaitForResult()); | |
| 242 } | |
| 243 | |
| 244 TEST_F(HttpResponseBodyDrainerTest, DrainBodySyncOK) { | |
| 245 mock_stream_->set_num_chunks(3); | |
| 246 mock_stream_->set_sync(); | |
| 247 drainer_->Start(session_.get()); | |
| 248 EXPECT_FALSE(result_waiter_.WaitForResult()); | |
| 249 } | |
| 250 | |
| 251 TEST_F(HttpResponseBodyDrainerTest, DrainBodyAsyncOK) { | |
| 252 mock_stream_->set_num_chunks(3); | |
| 253 drainer_->Start(session_.get()); | |
| 254 EXPECT_FALSE(result_waiter_.WaitForResult()); | |
| 255 } | |
| 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_.get()); | |
| 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_.get()); | |
| 272 EXPECT_FALSE(result_waiter_.WaitForResult()); | |
| 273 } | |
| 274 | |
| 275 TEST_F(HttpResponseBodyDrainerTest, DrainBodySizeEqualsDrainBuffer) { | |
| 276 mock_stream_->set_num_chunks( | |
| 277 HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize); | |
| 278 drainer_->Start(session_.get()); | |
| 279 EXPECT_FALSE(result_waiter_.WaitForResult()); | |
| 280 } | |
| 281 | |
| 282 TEST_F(HttpResponseBodyDrainerTest, DrainBodyTimeOut) { | |
| 283 mock_stream_->set_num_chunks(2); | |
| 284 mock_stream_->set_stall_reads_forever(); | |
| 285 drainer_->Start(session_.get()); | |
| 286 EXPECT_TRUE(result_waiter_.WaitForResult()); | |
| 287 } | |
| 288 | |
| 289 TEST_F(HttpResponseBodyDrainerTest, CancelledBySession) { | |
| 290 mock_stream_->set_num_chunks(2); | |
| 291 mock_stream_->set_stall_reads_forever(); | |
| 292 drainer_->Start(session_.get()); | |
| 293 // HttpNetworkSession should delete |drainer_|. | |
| 294 } | |
| 295 | |
| 296 TEST_F(HttpResponseBodyDrainerTest, DrainBodyTooLarge) { | |
| 297 int too_many_chunks = | |
| 298 HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize; | |
| 299 too_many_chunks += 1; // Now it's too large. | |
| 300 | |
| 301 mock_stream_->set_num_chunks(too_many_chunks); | |
| 302 drainer_->Start(session_.get()); | |
| 303 EXPECT_TRUE(result_waiter_.WaitForResult()); | |
| 304 } | |
| 305 | |
| 306 } // namespace | |
| 307 | |
| 308 } // namespace net | |
| OLD | NEW |