3 * Copyright 2004--2011, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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.
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.
28 #include "talk/base/gunit.h"
29 #include "talk/base/httpbase.h"
30 #include "talk/base/testutils.h"
34 const char* const kHttpResponse =
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"
45 const char* const kHttpEmptyResponse =
47 "Connection: Keep-Alive\r\n"
48 "Content-Length: 0\r\n"
49 "Proxy-Authorization: 42\r\n"
52 const char* const kHttpResponsePrefix =
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"
62 class HttpBaseTest : public testing::Test, public IHttpNotify {
64 enum EventType { E_HEADER_COMPLETE, E_COMPLETE, E_CLOSED };
72 HttpBaseTest() : mem(NULL), obtain_stream(false), http_stream(NULL) { }
74 virtual void SetUp() { }
75 virtual void TearDown() {
77 // Avoid an ASSERT, in case a test doesn't clean up properly
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};
86 ObtainDocumentStream();
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 };
95 virtual void onHttpClosed(HttpError err) {
96 LOG_F(LS_VERBOSE) << "err: " << err;
97 Event e = { E_CLOSED, false, 0, HM_NONE, err };
101 void SetupSource(const char* response);
103 void VerifyHeaderComplete(size_t event_count, bool empty_doc);
104 void VerifyDocumentContents(const char* expected_data,
105 size_t expected_length = SIZE_UNKNOWN);
107 void ObtainDocumentStream();
108 void VerifyDocumentStreamIsOpening();
109 void VerifyDocumentStreamOpenEvent();
110 void ReadDocumentStreamData(const char* expected_data);
111 void VerifyDocumentStreamIsEOS();
113 void SetupDocument(const char* response);
114 void VerifySourceContents(const char* expected_data,
115 size_t expected_length = SIZE_UNKNOWN);
117 void VerifyTransferComplete(HttpMode mode, HttpError error);
121 HttpResponseData data;
123 // The source of http data, and source events
124 testing::StreamSource src;
125 std::vector<Event> events;
127 // Document stream, and stream events
129 StreamInterface* http_stream;
130 testing::StreamSink sink;
133 void HttpBaseTest::SetupSource(const char* http_data) {
134 LOG_F(LS_VERBOSE) << "Enter";
136 src.SetState(SS_OPENING);
137 src.QueueString(http_data);
141 EXPECT_TRUE(events.empty());
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);
150 mem = new MemoryStream;
151 data.document.reset(mem);
152 LOG_F(LS_VERBOSE) << "Exit";
155 void HttpBaseTest::VerifyHeaderComplete(size_t event_count, bool empty_doc) {
156 LOG_F(LS_VERBOSE) << "Enter";
158 ASSERT_EQ(event_count, events.size());
159 EXPECT_EQ(E_HEADER_COMPLETE, events[0].event);
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);
170 EXPECT_FALSE(events[0].chunked);
171 EXPECT_EQ(0U, events[0].data_size);
173 EXPECT_TRUE(data.hasHeader(HH_CONTENT_LENGTH, &header));
174 EXPECT_EQ("0", header);
176 EXPECT_TRUE(events[0].chunked);
177 EXPECT_EQ(SIZE_UNKNOWN, events[0].data_size);
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);
184 LOG_F(LS_VERBOSE) << "Exit";
187 void HttpBaseTest::VerifyDocumentContents(const char* expected_data,
188 size_t expected_length) {
189 LOG_F(LS_VERBOSE) << "Enter";
191 if (SIZE_UNKNOWN == expected_length) {
192 expected_length = strlen(expected_data);
194 EXPECT_EQ(mem, data.document.get());
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";
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";
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());
219 char buffer[5] = { 0 };
220 EXPECT_EQ(SR_BLOCK, http_stream->Read(buffer, sizeof(buffer), &read, NULL));
221 LOG_F(LS_VERBOSE) << "Exit";
224 void HttpBaseTest::VerifyDocumentStreamOpenEvent() {
225 LOG_F(LS_VERBOSE) << "Enter";
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());
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";
237 void HttpBaseTest::ReadDocumentStreamData(const char* expected_data) {
238 LOG_F(LS_VERBOSE) << "Enter";
240 ASSERT_TRUE(NULL != http_stream);
241 EXPECT_EQ(SS_OPEN, http_stream->GetState());
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) {
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;
255 LOG_F(LS_VERBOSE) << "Exit";
258 void HttpBaseTest::VerifyDocumentStreamIsEOS() {
259 LOG_F(LS_VERBOSE) << "Enter";
261 ASSERT_TRUE(NULL != http_stream);
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());
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";
272 void HttpBaseTest::SetupDocument(const char* document_data) {
273 LOG_F(LS_VERBOSE) << "Enter";
274 src.SetState(SS_OPEN);
278 EXPECT_TRUE(events.empty());
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");
288 data.setHeader(HH_CONTENT_LENGTH, "0");
291 data.setHeader(HH_PROXY_AUTHORIZATION, "42");
292 data.setHeader(HH_CONNECTION, "Keep-Alive");
293 LOG_F(LS_VERBOSE) << "Exit";
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);
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";
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";
323 TEST_F(HttpBaseTest, SupportsSend) {
324 // Queue response document
325 SetupDocument("Goodbye!");
330 // Send completed successfully
331 VerifyTransferComplete(HM_SEND, HE_NONE);
332 VerifySourceContents(kHttpResponse);
335 TEST_F(HttpBaseTest, SupportsSendNoDocument) {
336 // Queue response document
342 // Send completed successfully
343 VerifyTransferComplete(HM_SEND, HE_NONE);
344 VerifySourceContents(kHttpEmptyResponse);
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
352 SetupSource(kHttpResponse);
354 VerifyTransferComplete(HM_RECV, HE_NONE);
361 // Queue response document
362 SetupDocument("Goodbye!");
364 // Prevent entire response from being sent
365 const size_t kInterruptedLength = strlen(kHttpResponse) - 1;
366 src.SetWriteBlock(kInterruptedLength);
371 // Document is mostly complete, but no completion signal yet.
372 EXPECT_TRUE(events.empty());
373 VerifySourceContents(kHttpResponse, kInterruptedLength);
375 src.SetState(SS_CLOSED);
377 // Send completed with disconnect error, and no additional data.
378 VerifyTransferComplete(HM_SEND, HE_DISCONNECTED);
379 EXPECT_TRUE(src.ReadData().empty());
382 TEST_F(HttpBaseTest, SupportsReceiveViaDocumentPush) {
383 // Queue response document
384 SetupSource(kHttpResponse);
389 // Document completed successfully
390 VerifyHeaderComplete(2, false);
391 VerifyTransferComplete(HM_RECV, HE_NONE);
392 VerifyDocumentContents("Goodbye!");
395 TEST_F(HttpBaseTest, SupportsReceiveViaStreamPull) {
396 // Switch to pull mode
397 ObtainDocumentStream();
398 VerifyDocumentStreamIsOpening();
400 // Queue response document
401 SetupSource(kHttpResponse);
402 VerifyDocumentStreamIsOpening();
407 // Pull document data
408 VerifyDocumentStreamOpenEvent();
409 ReadDocumentStreamData("Goodbye!");
410 VerifyDocumentStreamIsEOS();
412 // Document completed successfully
413 VerifyHeaderComplete(2, false);
414 VerifyTransferComplete(HM_RECV, HE_NONE);
415 VerifyDocumentContents("");
418 TEST_F(HttpBaseTest, DISABLED_AllowsCloseStreamBeforeDocumentIsComplete) {
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);
425 // Switch to pull mode
426 ObtainDocumentStream();
427 VerifyDocumentStreamIsOpening();
429 // Queue response document
430 SetupSource(kHttpResponse);
431 VerifyDocumentStreamIsOpening();
436 // Pull some of the data
437 VerifyDocumentStreamOpenEvent();
438 ReadDocumentStreamData("Goodb");
440 // We've seen the header by now
441 VerifyHeaderComplete(1, false);
443 // Close the pull stream, this will transition back to push I/O.
444 http_stream->Close();
445 Thread::Current()->ProcessMessages(0);
447 // Remainder of document completed successfully
448 VerifyTransferComplete(HM_RECV, HE_NONE);
449 VerifyDocumentContents("ye!");
451 talk_base::LogMessage::LogToDebug(old_sev);
454 TEST_F(HttpBaseTest, AllowsGetDocumentStreamInResponseToHttpHeader) {
455 // Queue response document
456 SetupSource(kHttpResponse);
458 // Switch to pull mode in response to header arrival
459 obtain_stream = true;
464 // We've already seen the header, but not data has arrived
465 VerifyHeaderComplete(1, false);
466 VerifyDocumentContents("");
468 // Pull the document data
469 ReadDocumentStreamData("Goodbye!");
470 VerifyDocumentStreamIsEOS();
472 // Document completed successfully
473 VerifyTransferComplete(HM_RECV, HE_NONE);
474 VerifyDocumentContents("");
477 TEST_F(HttpBaseTest, AllowsGetDocumentStreamWithEmptyDocumentBody) {
478 // Queue empty response document
479 SetupSource(kHttpEmptyResponse);
481 // Switch to pull mode in response to header arrival
482 obtain_stream = true;
487 // We've already seen the header, but not data has arrived
488 VerifyHeaderComplete(1, true);
489 VerifyDocumentContents("");
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());
495 // Attempt to read data, and discover EOS
496 VerifyDocumentStreamIsEOS();
498 // Document completed successfully
499 VerifyTransferComplete(HM_RECV, HE_NONE);
500 VerifyDocumentContents("");
503 TEST_F(HttpBaseTest, SignalsDocumentStreamCloseOnUnexpectedClose) {
504 // Switch to pull mode
505 ObtainDocumentStream();
506 VerifyDocumentStreamIsOpening();
508 // Queue response document
509 SetupSource(kHttpResponsePrefix);
510 VerifyDocumentStreamIsOpening();
515 // Pull document data
516 VerifyDocumentStreamOpenEvent();
517 ReadDocumentStreamData("Goodbye!");
519 // Simulate unexpected close
520 src.SetState(SS_CLOSED);
522 // Observe error event on document stream
523 EXPECT_EQ(testing::SSE_ERROR, sink.Events(http_stream));
525 // Future reads give an error
527 char buffer[5] = { 0 };
528 EXPECT_EQ(SR_ERROR, http_stream->Read(buffer, sizeof(buffer), NULL, &error));
529 EXPECT_EQ(HE_DISCONNECTED, error);
531 // Document completed with error
532 VerifyHeaderComplete(2, false);
533 VerifyTransferComplete(HM_RECV, HE_DISCONNECTED);
534 VerifyDocumentContents("");
537 } // namespace talk_base