Home | History | Annotate | Download | only in http
      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