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