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