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 32namespace talk_base { 33 34const 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 45const 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 52const 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 62class HttpBaseTest : public testing::Test, public IHttpNotify { 63public: 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 133void 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 155void 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 187void 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 203void 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 212void 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 224void 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 237void 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 258void 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 272void 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 296void 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 308void 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 323TEST_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 335TEST_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 347TEST_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 382TEST_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 395TEST_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 418TEST_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 454TEST_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 477TEST_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 503TEST_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