Home | History | Annotate | Download | only in base
      1 /*
      2  *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include <algorithm>
     12 
     13 #include "webrtc/base/gunit.h"
     14 #include "webrtc/base/httpbase.h"
     15 #include "webrtc/base/testutils.h"
     16 
     17 namespace rtc {
     18 
     19 const char* const kHttpResponse =
     20   "HTTP/1.1 200\r\n"
     21   "Connection: Keep-Alive\r\n"
     22   "Content-Type: text/plain\r\n"
     23   "Proxy-Authorization: 42\r\n"
     24   "Transfer-Encoding: chunked\r\n"
     25   "\r\n"
     26   "00000008\r\n"
     27   "Goodbye!\r\n"
     28   "0\r\n\r\n";
     29 
     30 const char* const kHttpEmptyResponse =
     31   "HTTP/1.1 200\r\n"
     32   "Connection: Keep-Alive\r\n"
     33   "Content-Length: 0\r\n"
     34   "Proxy-Authorization: 42\r\n"
     35   "\r\n";
     36 
     37 const char* const kHttpResponsePrefix =
     38   "HTTP/1.1 200\r\n"
     39   "Connection: Keep-Alive\r\n"
     40   "Content-Type: text/plain\r\n"
     41   "Proxy-Authorization: 42\r\n"
     42   "Transfer-Encoding: chunked\r\n"
     43   "\r\n"
     44   "8\r\n"
     45   "Goodbye!\r\n";
     46 
     47 class HttpBaseTest : public testing::Test, public IHttpNotify {
     48 public:
     49   enum EventType { E_HEADER_COMPLETE, E_COMPLETE, E_CLOSED };
     50   struct Event {
     51     EventType event;
     52     bool chunked;
     53     size_t data_size;
     54     HttpMode mode;
     55     HttpError err;
     56   };
     57   HttpBaseTest() : mem(NULL), obtain_stream(false), http_stream(NULL) { }
     58 
     59   virtual void SetUp() { }
     60   virtual void TearDown() {
     61     delete http_stream;
     62     // Avoid an ASSERT, in case a test doesn't clean up properly
     63     base.abort(HE_NONE);
     64   }
     65 
     66   virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) {
     67     LOG_F(LS_VERBOSE) << "chunked: " << chunked << " size: " << data_size;
     68     Event e = { E_HEADER_COMPLETE, chunked, data_size, HM_NONE, HE_NONE};
     69     events.push_back(e);
     70     if (obtain_stream) {
     71       ObtainDocumentStream();
     72     }
     73     return HE_NONE;
     74   }
     75   virtual void onHttpComplete(HttpMode mode, HttpError err) {
     76     LOG_F(LS_VERBOSE) << "mode: " << mode << " err: " << err;
     77     Event e = { E_COMPLETE, false, 0, mode, err };
     78     events.push_back(e);
     79   }
     80   virtual void onHttpClosed(HttpError err) {
     81     LOG_F(LS_VERBOSE) << "err: " << err;
     82     Event e = { E_CLOSED, false, 0, HM_NONE, err };
     83     events.push_back(e);
     84   }
     85 
     86   void SetupSource(const char* response);
     87 
     88   void VerifyHeaderComplete(size_t event_count, bool empty_doc);
     89   void VerifyDocumentContents(const char* expected_data,
     90                               size_t expected_length = SIZE_UNKNOWN);
     91 
     92   void ObtainDocumentStream();
     93   void VerifyDocumentStreamIsOpening();
     94   void VerifyDocumentStreamOpenEvent();
     95   void ReadDocumentStreamData(const char* expected_data);
     96   void VerifyDocumentStreamIsEOS();
     97 
     98   void SetupDocument(const char* response);
     99   void VerifySourceContents(const char* expected_data,
    100                             size_t expected_length = SIZE_UNKNOWN);
    101 
    102   void VerifyTransferComplete(HttpMode mode, HttpError error);
    103 
    104   HttpBase base;
    105   MemoryStream* mem;
    106   HttpResponseData data;
    107 
    108   // The source of http data, and source events
    109   testing::StreamSource src;
    110   std::vector<Event> events;
    111 
    112   // Document stream, and stream events
    113   bool obtain_stream;
    114   StreamInterface* http_stream;
    115   testing::StreamSink sink;
    116 };
    117 
    118 void HttpBaseTest::SetupSource(const char* http_data) {
    119   LOG_F(LS_VERBOSE) << "Enter";
    120 
    121   src.SetState(SS_OPENING);
    122   src.QueueString(http_data);
    123 
    124   base.notify(this);
    125   base.attach(&src);
    126   EXPECT_TRUE(events.empty());
    127 
    128   src.SetState(SS_OPEN);
    129   ASSERT_EQ(1U, events.size());
    130   EXPECT_EQ(E_COMPLETE, events[0].event);
    131   EXPECT_EQ(HM_CONNECT, events[0].mode);
    132   EXPECT_EQ(HE_NONE, events[0].err);
    133   events.clear();
    134 
    135   mem = new MemoryStream;
    136   data.document.reset(mem);
    137   LOG_F(LS_VERBOSE) << "Exit";
    138 }
    139 
    140 void HttpBaseTest::VerifyHeaderComplete(size_t event_count, bool empty_doc) {
    141   LOG_F(LS_VERBOSE) << "Enter";
    142 
    143   ASSERT_EQ(event_count, events.size());
    144   EXPECT_EQ(E_HEADER_COMPLETE, events[0].event);
    145 
    146   std::string header;
    147   EXPECT_EQ(HVER_1_1, data.version);
    148   EXPECT_EQ(static_cast<uint32_t>(HC_OK), data.scode);
    149   EXPECT_TRUE(data.hasHeader(HH_PROXY_AUTHORIZATION, &header));
    150   EXPECT_EQ("42", header);
    151   EXPECT_TRUE(data.hasHeader(HH_CONNECTION, &header));
    152   EXPECT_EQ("Keep-Alive", header);
    153 
    154   if (empty_doc) {
    155     EXPECT_FALSE(events[0].chunked);
    156     EXPECT_EQ(0U, events[0].data_size);
    157 
    158     EXPECT_TRUE(data.hasHeader(HH_CONTENT_LENGTH, &header));
    159     EXPECT_EQ("0", header);
    160   } else {
    161     EXPECT_TRUE(events[0].chunked);
    162     EXPECT_EQ(SIZE_UNKNOWN, events[0].data_size);
    163 
    164     EXPECT_TRUE(data.hasHeader(HH_CONTENT_TYPE, &header));
    165     EXPECT_EQ("text/plain", header);
    166     EXPECT_TRUE(data.hasHeader(HH_TRANSFER_ENCODING, &header));
    167     EXPECT_EQ("chunked", header);
    168   }
    169   LOG_F(LS_VERBOSE) << "Exit";
    170 }
    171 
    172 void HttpBaseTest::VerifyDocumentContents(const char* expected_data,
    173                                           size_t expected_length) {
    174   LOG_F(LS_VERBOSE) << "Enter";
    175 
    176   if (SIZE_UNKNOWN == expected_length) {
    177     expected_length = strlen(expected_data);
    178   }
    179   EXPECT_EQ(mem, data.document.get());
    180 
    181   size_t length;
    182   mem->GetSize(&length);
    183   EXPECT_EQ(expected_length, length);
    184   EXPECT_TRUE(0 == memcmp(expected_data, mem->GetBuffer(), length));
    185   LOG_F(LS_VERBOSE) << "Exit";
    186 }
    187 
    188 void HttpBaseTest::ObtainDocumentStream() {
    189   LOG_F(LS_VERBOSE) << "Enter";
    190   EXPECT_FALSE(http_stream);
    191   http_stream = base.GetDocumentStream();
    192   ASSERT_TRUE(NULL != http_stream);
    193   sink.Monitor(http_stream);
    194   LOG_F(LS_VERBOSE) << "Exit";
    195 }
    196 
    197 void HttpBaseTest::VerifyDocumentStreamIsOpening() {
    198   LOG_F(LS_VERBOSE) << "Enter";
    199   ASSERT_TRUE(NULL != http_stream);
    200   EXPECT_EQ(0, sink.Events(http_stream));
    201   EXPECT_EQ(SS_OPENING, http_stream->GetState());
    202 
    203   size_t read = 0;
    204   char buffer[5] = { 0 };
    205   EXPECT_EQ(SR_BLOCK, http_stream->Read(buffer, sizeof(buffer), &read, NULL));
    206   LOG_F(LS_VERBOSE) << "Exit";
    207 }
    208 
    209 void HttpBaseTest::VerifyDocumentStreamOpenEvent() {
    210   LOG_F(LS_VERBOSE) << "Enter";
    211 
    212   ASSERT_TRUE(NULL != http_stream);
    213   EXPECT_EQ(SE_OPEN | SE_READ, sink.Events(http_stream));
    214   EXPECT_EQ(SS_OPEN, http_stream->GetState());
    215 
    216   // HTTP headers haven't arrived yet
    217   EXPECT_EQ(0U, events.size());
    218   EXPECT_EQ(static_cast<uint32_t>(HC_INTERNAL_SERVER_ERROR), data.scode);
    219   LOG_F(LS_VERBOSE) << "Exit";
    220 }
    221 
    222 void HttpBaseTest::ReadDocumentStreamData(const char* expected_data) {
    223   LOG_F(LS_VERBOSE) << "Enter";
    224 
    225   ASSERT_TRUE(NULL != http_stream);
    226   EXPECT_EQ(SS_OPEN, http_stream->GetState());
    227 
    228   // Pump the HTTP I/O using Read, and verify the results.
    229   size_t verified_length = 0;
    230   const size_t expected_length = strlen(expected_data);
    231   while (verified_length < expected_length) {
    232     size_t read = 0;
    233     char buffer[5] = { 0 };
    234     size_t amt_to_read =
    235         std::min(expected_length - verified_length, sizeof(buffer));
    236     EXPECT_EQ(SR_SUCCESS, http_stream->Read(buffer, amt_to_read, &read, NULL));
    237     EXPECT_EQ(amt_to_read, read);
    238     EXPECT_TRUE(0 == memcmp(expected_data + verified_length, buffer, read));
    239     verified_length += read;
    240   }
    241   LOG_F(LS_VERBOSE) << "Exit";
    242 }
    243 
    244 void HttpBaseTest::VerifyDocumentStreamIsEOS() {
    245   LOG_F(LS_VERBOSE) << "Enter";
    246 
    247   ASSERT_TRUE(NULL != http_stream);
    248   size_t read = 0;
    249   char buffer[5] = { 0 };
    250   EXPECT_EQ(SR_EOS, http_stream->Read(buffer, sizeof(buffer), &read, NULL));
    251   EXPECT_EQ(SS_CLOSED, http_stream->GetState());
    252 
    253   // When EOS is caused by Read, we don't expect SE_CLOSE
    254   EXPECT_EQ(0, sink.Events(http_stream));
    255   LOG_F(LS_VERBOSE) << "Exit";
    256 }
    257 
    258 void HttpBaseTest::SetupDocument(const char* document_data) {
    259   LOG_F(LS_VERBOSE) << "Enter";
    260   src.SetState(SS_OPEN);
    261 
    262   base.notify(this);
    263   base.attach(&src);
    264   EXPECT_TRUE(events.empty());
    265 
    266   if (document_data) {
    267     // Note: we could just call data.set_success("text/plain", mem), but that
    268     // won't allow us to use the chunked transfer encoding.
    269     mem = new MemoryStream(document_data);
    270     data.document.reset(mem);
    271     data.setHeader(HH_CONTENT_TYPE, "text/plain");
    272     data.setHeader(HH_TRANSFER_ENCODING, "chunked");
    273   } else {
    274     data.setHeader(HH_CONTENT_LENGTH, "0");
    275   }
    276   data.scode = HC_OK;
    277   data.setHeader(HH_PROXY_AUTHORIZATION, "42");
    278   data.setHeader(HH_CONNECTION, "Keep-Alive");
    279   LOG_F(LS_VERBOSE) << "Exit";
    280 }
    281 
    282 void HttpBaseTest::VerifySourceContents(const char* expected_data,
    283                                         size_t expected_length) {
    284   LOG_F(LS_VERBOSE) << "Enter";
    285   if (SIZE_UNKNOWN == expected_length) {
    286     expected_length = strlen(expected_data);
    287   }
    288   std::string contents = src.ReadData();
    289   EXPECT_EQ(expected_length, contents.length());
    290   EXPECT_TRUE(0 == memcmp(expected_data, contents.data(), expected_length));
    291   LOG_F(LS_VERBOSE) << "Exit";
    292 }
    293 
    294 void HttpBaseTest::VerifyTransferComplete(HttpMode mode, HttpError error) {
    295   LOG_F(LS_VERBOSE) << "Enter";
    296   // Verify that http operation has completed
    297   ASSERT_TRUE(events.size() > 0);
    298   size_t last_event = events.size() - 1;
    299   EXPECT_EQ(E_COMPLETE, events[last_event].event);
    300   EXPECT_EQ(mode, events[last_event].mode);
    301   EXPECT_EQ(error, events[last_event].err);
    302   LOG_F(LS_VERBOSE) << "Exit";
    303 }
    304 
    305 //
    306 // Tests
    307 //
    308 
    309 TEST_F(HttpBaseTest, SupportsSend) {
    310   // Queue response document
    311   SetupDocument("Goodbye!");
    312 
    313   // Begin send
    314   base.send(&data);
    315 
    316   // Send completed successfully
    317   VerifyTransferComplete(HM_SEND, HE_NONE);
    318   VerifySourceContents(kHttpResponse);
    319 }
    320 
    321 TEST_F(HttpBaseTest, SupportsSendNoDocument) {
    322   // Queue response document
    323   SetupDocument(NULL);
    324 
    325   // Begin send
    326   base.send(&data);
    327 
    328   // Send completed successfully
    329   VerifyTransferComplete(HM_SEND, HE_NONE);
    330   VerifySourceContents(kHttpEmptyResponse);
    331 }
    332 
    333 TEST_F(HttpBaseTest, SignalsCompleteOnInterruptedSend) {
    334   // This test is attempting to expose a bug that occurs when a particular
    335   // base objects is used for receiving, and then used for sending.  In
    336   // particular, the HttpParser state is different after receiving.  Simulate
    337   // that here.
    338   SetupSource(kHttpResponse);
    339   base.recv(&data);
    340   VerifyTransferComplete(HM_RECV, HE_NONE);
    341 
    342   src.Clear();
    343   data.clear(true);
    344   events.clear();
    345   base.detach();
    346 
    347   // Queue response document
    348   SetupDocument("Goodbye!");
    349 
    350   // Prevent entire response from being sent
    351   const size_t kInterruptedLength = strlen(kHttpResponse) - 1;
    352   src.SetWriteBlock(kInterruptedLength);
    353 
    354   // Begin send
    355   base.send(&data);
    356 
    357   // Document is mostly complete, but no completion signal yet.
    358   EXPECT_TRUE(events.empty());
    359   VerifySourceContents(kHttpResponse, kInterruptedLength);
    360 
    361   src.SetState(SS_CLOSED);
    362 
    363   // Send completed with disconnect error, and no additional data.
    364   VerifyTransferComplete(HM_SEND, HE_DISCONNECTED);
    365   EXPECT_TRUE(src.ReadData().empty());
    366 }
    367 
    368 TEST_F(HttpBaseTest, SupportsReceiveViaDocumentPush) {
    369   // Queue response document
    370   SetupSource(kHttpResponse);
    371 
    372   // Begin receive
    373   base.recv(&data);
    374 
    375   // Document completed successfully
    376   VerifyHeaderComplete(2, false);
    377   VerifyTransferComplete(HM_RECV, HE_NONE);
    378   VerifyDocumentContents("Goodbye!");
    379 }
    380 
    381 TEST_F(HttpBaseTest, SupportsReceiveViaStreamPull) {
    382   // Switch to pull mode
    383   ObtainDocumentStream();
    384   VerifyDocumentStreamIsOpening();
    385 
    386   // Queue response document
    387   SetupSource(kHttpResponse);
    388   VerifyDocumentStreamIsOpening();
    389 
    390   // Begin receive
    391   base.recv(&data);
    392 
    393   // Pull document data
    394   VerifyDocumentStreamOpenEvent();
    395   ReadDocumentStreamData("Goodbye!");
    396   VerifyDocumentStreamIsEOS();
    397 
    398   // Document completed successfully
    399   VerifyHeaderComplete(2, false);
    400   VerifyTransferComplete(HM_RECV, HE_NONE);
    401   VerifyDocumentContents("");
    402 }
    403 
    404 TEST_F(HttpBaseTest, DISABLED_AllowsCloseStreamBeforeDocumentIsComplete) {
    405 
    406   // TODO: Remove extra logging once test failure is understood
    407   LoggingSeverity old_sev = rtc::LogMessage::GetLogToDebug();
    408   rtc::LogMessage::LogToDebug(LS_VERBOSE);
    409 
    410 
    411   // Switch to pull mode
    412   ObtainDocumentStream();
    413   VerifyDocumentStreamIsOpening();
    414 
    415   // Queue response document
    416   SetupSource(kHttpResponse);
    417   VerifyDocumentStreamIsOpening();
    418 
    419   // Begin receive
    420   base.recv(&data);
    421 
    422   // Pull some of the data
    423   VerifyDocumentStreamOpenEvent();
    424   ReadDocumentStreamData("Goodb");
    425 
    426   // We've seen the header by now
    427   VerifyHeaderComplete(1, false);
    428 
    429   // Close the pull stream, this will transition back to push I/O.
    430   http_stream->Close();
    431   Thread::Current()->ProcessMessages(0);
    432 
    433   // Remainder of document completed successfully
    434   VerifyTransferComplete(HM_RECV, HE_NONE);
    435   VerifyDocumentContents("ye!");
    436 
    437   rtc::LogMessage::LogToDebug(old_sev);
    438 }
    439 
    440 TEST_F(HttpBaseTest, AllowsGetDocumentStreamInResponseToHttpHeader) {
    441   // Queue response document
    442   SetupSource(kHttpResponse);
    443 
    444   // Switch to pull mode in response to header arrival
    445   obtain_stream = true;
    446 
    447   // Begin receive
    448   base.recv(&data);
    449 
    450   // We've already seen the header, but not data has arrived
    451   VerifyHeaderComplete(1, false);
    452   VerifyDocumentContents("");
    453 
    454   // Pull the document data
    455   ReadDocumentStreamData("Goodbye!");
    456   VerifyDocumentStreamIsEOS();
    457 
    458   // Document completed successfully
    459   VerifyTransferComplete(HM_RECV, HE_NONE);
    460   VerifyDocumentContents("");
    461 }
    462 
    463 TEST_F(HttpBaseTest, AllowsGetDocumentStreamWithEmptyDocumentBody) {
    464   // Queue empty response document
    465   SetupSource(kHttpEmptyResponse);
    466 
    467   // Switch to pull mode in response to header arrival
    468   obtain_stream = true;
    469 
    470   // Begin receive
    471   base.recv(&data);
    472 
    473   // We've already seen the header, but not data has arrived
    474   VerifyHeaderComplete(1, true);
    475   VerifyDocumentContents("");
    476 
    477   // The document is still open, until we attempt to read
    478   ASSERT_TRUE(NULL != http_stream);
    479   EXPECT_EQ(SS_OPEN, http_stream->GetState());
    480 
    481   // Attempt to read data, and discover EOS
    482   VerifyDocumentStreamIsEOS();
    483 
    484   // Document completed successfully
    485   VerifyTransferComplete(HM_RECV, HE_NONE);
    486   VerifyDocumentContents("");
    487 }
    488 
    489 TEST_F(HttpBaseTest, SignalsDocumentStreamCloseOnUnexpectedClose) {
    490   // Switch to pull mode
    491   ObtainDocumentStream();
    492   VerifyDocumentStreamIsOpening();
    493 
    494   // Queue response document
    495   SetupSource(kHttpResponsePrefix);
    496   VerifyDocumentStreamIsOpening();
    497 
    498   // Begin receive
    499   base.recv(&data);
    500 
    501   // Pull document data
    502   VerifyDocumentStreamOpenEvent();
    503   ReadDocumentStreamData("Goodbye!");
    504 
    505   // Simulate unexpected close
    506   src.SetState(SS_CLOSED);
    507 
    508   // Observe error event on document stream
    509   EXPECT_EQ(testing::SSE_ERROR, sink.Events(http_stream));
    510 
    511   // Future reads give an error
    512   int error = 0;
    513   char buffer[5] = { 0 };
    514   EXPECT_EQ(SR_ERROR, http_stream->Read(buffer, sizeof(buffer), NULL, &error));
    515   EXPECT_EQ(HE_DISCONNECTED, error);
    516 
    517   // Document completed with error
    518   VerifyHeaderComplete(2, false);
    519   VerifyTransferComplete(HM_RECV, HE_DISCONNECTED);
    520   VerifyDocumentContents("");
    521 }
    522 
    523 } // namespace rtc
    524