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