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