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() {
76
// Avoid an ASSERT, in case a test doesn't clean up properly
80
virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) {
81
LOG_F(LS_VERBOSE) << "chunked: " << chunked << " size: " << data_size;
82
Event e = { E_HEADER_COMPLETE, chunked, data_size, HM_NONE, HE_NONE};
85
ObtainDocumentStream();
89
virtual void onHttpComplete(HttpMode mode, HttpError err) {
90
LOG_F(LS_VERBOSE) << "mode: " << mode << " err: " << err;
91
Event e = { E_COMPLETE, false, 0, mode, err };
94
virtual void onHttpClosed(HttpError err) {
95
LOG_F(LS_VERBOSE) << "err: " << err;
96
Event e = { E_CLOSED, false, 0, HM_NONE, err };
100
void SetupSource(const char* response);
102
void VerifyHeaderComplete(size_t event_count, bool empty_doc);
103
void VerifyDocumentContents(const char* expected_data,
104
size_t expected_length = SIZE_UNKNOWN);
106
void ObtainDocumentStream();
107
void VerifyDocumentStreamIsOpening();
108
void VerifyDocumentStreamOpenEvent();
109
void ReadDocumentStreamData(const char* expected_data);
110
void VerifyDocumentStreamIsEOS();
112
void SetupDocument(const char* response);
113
void VerifySourceContents(const char* expected_data,
114
size_t expected_length = SIZE_UNKNOWN);
116
void VerifyTransferComplete(HttpMode mode, HttpError error);
120
HttpResponseData data;
122
// The source of http data, and source events
123
testing::StreamSource src;
124
std::vector<Event> events;
126
// Document stream, and stream events
128
StreamInterface* http_stream;
129
testing::StreamSink sink;
132
void HttpBaseTest::SetupSource(const char* http_data) {
133
LOG_F(LS_VERBOSE) << "Enter";
135
src.SetState(SS_OPENING);
136
src.QueueString(http_data);
140
EXPECT_TRUE(events.empty());
142
src.SetState(SS_OPEN);
143
ASSERT_EQ(1U, events.size());
144
EXPECT_EQ(E_COMPLETE, events[0].event);
145
EXPECT_EQ(HM_CONNECT, events[0].mode);
146
EXPECT_EQ(HE_NONE, events[0].err);
149
mem = new MemoryStream;
150
data.document.reset(mem);
151
LOG_F(LS_VERBOSE) << "Exit";
154
void HttpBaseTest::VerifyHeaderComplete(size_t event_count, bool empty_doc) {
155
LOG_F(LS_VERBOSE) << "Enter";
157
ASSERT_EQ(event_count, events.size());
158
EXPECT_EQ(E_HEADER_COMPLETE, events[0].event);
161
EXPECT_EQ(HVER_1_1, data.version);
162
EXPECT_EQ(static_cast<uint32>(HC_OK), data.scode);
163
EXPECT_TRUE(data.hasHeader(HH_PROXY_AUTHORIZATION, &header));
164
EXPECT_EQ("42", header);
165
EXPECT_TRUE(data.hasHeader(HH_CONNECTION, &header));
166
EXPECT_EQ("Keep-Alive", header);
169
EXPECT_FALSE(events[0].chunked);
170
EXPECT_EQ(0U, events[0].data_size);
172
EXPECT_TRUE(data.hasHeader(HH_CONTENT_LENGTH, &header));
173
EXPECT_EQ("0", header);
175
EXPECT_TRUE(events[0].chunked);
176
EXPECT_EQ(SIZE_UNKNOWN, events[0].data_size);
178
EXPECT_TRUE(data.hasHeader(HH_CONTENT_TYPE, &header));
179
EXPECT_EQ("text/plain", header);
180
EXPECT_TRUE(data.hasHeader(HH_TRANSFER_ENCODING, &header));
181
EXPECT_EQ("chunked", header);
183
LOG_F(LS_VERBOSE) << "Exit";
186
void HttpBaseTest::VerifyDocumentContents(const char* expected_data,
187
size_t expected_length) {
188
LOG_F(LS_VERBOSE) << "Enter";
190
if (SIZE_UNKNOWN == expected_length) {
191
expected_length = strlen(expected_data);
193
EXPECT_EQ(mem, data.document.get());
196
mem->GetSize(&length);
197
EXPECT_EQ(expected_length, length);
198
EXPECT_TRUE(0 == memcmp(expected_data, mem->GetBuffer(), length));
199
LOG_F(LS_VERBOSE) << "Exit";
202
void HttpBaseTest::ObtainDocumentStream() {
203
LOG_F(LS_VERBOSE) << "Enter";
204
EXPECT_FALSE(http_stream);
205
http_stream = base.GetDocumentStream();
206
ASSERT_TRUE(NULL != http_stream);
207
sink.Monitor(http_stream);
208
LOG_F(LS_VERBOSE) << "Exit";
211
void HttpBaseTest::VerifyDocumentStreamIsOpening() {
212
LOG_F(LS_VERBOSE) << "Enter";
213
ASSERT_TRUE(NULL != http_stream);
214
EXPECT_EQ(0, sink.Events(http_stream));
215
EXPECT_EQ(SS_OPENING, http_stream->GetState());
218
char buffer[5] = { 0 };
219
EXPECT_EQ(SR_BLOCK, http_stream->Read(buffer, sizeof(buffer), &read, NULL));
220
LOG_F(LS_VERBOSE) << "Exit";
223
void HttpBaseTest::VerifyDocumentStreamOpenEvent() {
224
LOG_F(LS_VERBOSE) << "Enter";
226
ASSERT_TRUE(NULL != http_stream);
227
EXPECT_EQ(SE_OPEN | SE_READ, sink.Events(http_stream));
228
EXPECT_EQ(SS_OPEN, http_stream->GetState());
230
// HTTP headers haven't arrived yet
231
EXPECT_EQ(0U, events.size());
232
EXPECT_EQ(static_cast<uint32>(HC_INTERNAL_SERVER_ERROR), data.scode);
233
LOG_F(LS_VERBOSE) << "Exit";
236
void HttpBaseTest::ReadDocumentStreamData(const char* expected_data) {
237
LOG_F(LS_VERBOSE) << "Enter";
239
ASSERT_TRUE(NULL != http_stream);
240
EXPECT_EQ(SS_OPEN, http_stream->GetState());
242
// Pump the HTTP I/O using Read, and verify the results.
243
size_t verified_length = 0;
244
const size_t expected_length = strlen(expected_data);
245
while (verified_length < expected_length) {
247
char buffer[5] = { 0 };
248
size_t amt_to_read = _min(expected_length - verified_length, sizeof(buffer));
249
EXPECT_EQ(SR_SUCCESS, http_stream->Read(buffer, amt_to_read, &read, NULL));
250
EXPECT_EQ(amt_to_read, read);
251
EXPECT_TRUE(0 == memcmp(expected_data + verified_length, buffer, read));
252
verified_length += read;
254
LOG_F(LS_VERBOSE) << "Exit";
257
void HttpBaseTest::VerifyDocumentStreamIsEOS() {
258
LOG_F(LS_VERBOSE) << "Enter";
260
ASSERT_TRUE(NULL != http_stream);
262
char buffer[5] = { 0 };
263
EXPECT_EQ(SR_EOS, http_stream->Read(buffer, sizeof(buffer), &read, NULL));
264
EXPECT_EQ(SS_CLOSED, http_stream->GetState());
266
// When EOS is caused by Read, we don't expect SE_CLOSE
267
EXPECT_EQ(0, sink.Events(http_stream));
268
LOG_F(LS_VERBOSE) << "Exit";
271
void HttpBaseTest::SetupDocument(const char* document_data) {
272
LOG_F(LS_VERBOSE) << "Enter";
273
src.SetState(SS_OPEN);
277
EXPECT_TRUE(events.empty());
280
// Note: we could just call data.set_success("text/plain", mem), but that
281
// won't allow us to use the chunked transfer encoding.
282
mem = new MemoryStream(document_data);
283
data.document.reset(mem);
284
data.setHeader(HH_CONTENT_TYPE, "text/plain");
285
data.setHeader(HH_TRANSFER_ENCODING, "chunked");
287
data.setHeader(HH_CONTENT_LENGTH, "0");
290
data.setHeader(HH_PROXY_AUTHORIZATION, "42");
291
data.setHeader(HH_CONNECTION, "Keep-Alive");
292
LOG_F(LS_VERBOSE) << "Exit";
295
void HttpBaseTest::VerifySourceContents(const char* expected_data,
296
size_t expected_length) {
297
LOG_F(LS_VERBOSE) << "Enter";
298
if (SIZE_UNKNOWN == expected_length) {
299
expected_length = strlen(expected_data);
301
std::string contents = src.ReadData();
302
EXPECT_EQ(expected_length, contents.length());
303
EXPECT_TRUE(0 == memcmp(expected_data, contents.data(), expected_length));
304
LOG_F(LS_VERBOSE) << "Exit";
307
void HttpBaseTest::VerifyTransferComplete(HttpMode mode, HttpError error) {
308
LOG_F(LS_VERBOSE) << "Enter";
309
// Verify that http operation has completed
310
ASSERT_TRUE(events.size() > 0);
311
size_t last_event = events.size() - 1;
312
EXPECT_EQ(E_COMPLETE, events[last_event].event);
313
EXPECT_EQ(mode, events[last_event].mode);
314
EXPECT_EQ(error, events[last_event].err);
315
LOG_F(LS_VERBOSE) << "Exit";
322
TEST_F(HttpBaseTest, SupportsSend) {
323
// Queue response document
324
SetupDocument("Goodbye!");
329
// Send completed successfully
330
VerifyTransferComplete(HM_SEND, HE_NONE);
331
VerifySourceContents(kHttpResponse);
334
TEST_F(HttpBaseTest, SupportsSendNoDocument) {
335
// Queue response document
341
// Send completed successfully
342
VerifyTransferComplete(HM_SEND, HE_NONE);
343
VerifySourceContents(kHttpEmptyResponse);
346
TEST_F(HttpBaseTest, SignalsCompleteOnInterruptedSend) {
347
// This test is attempting to expose a bug that occurs when a particular
348
// base objects is used for receiving, and then used for sending. In
349
// particular, the HttpParser state is different after receiving. Simulate
351
SetupSource(kHttpResponse);
353
VerifyTransferComplete(HM_RECV, HE_NONE);
360
// Queue response document
361
SetupDocument("Goodbye!");
363
// Prevent entire response from being sent
364
const size_t kInterruptedLength = strlen(kHttpResponse) - 1;
365
src.SetWriteBlock(kInterruptedLength);
370
// Document is mostly complete, but no completion signal yet.
371
EXPECT_TRUE(events.empty());
372
VerifySourceContents(kHttpResponse, kInterruptedLength);
374
src.SetState(SS_CLOSED);
376
// Send completed with disconnect error, and no additional data.
377
VerifyTransferComplete(HM_SEND, HE_DISCONNECTED);
378
EXPECT_TRUE(src.ReadData().empty());
381
TEST_F(HttpBaseTest, SupportsReceiveViaDocumentPush) {
382
// Queue response document
383
SetupSource(kHttpResponse);
388
// Document completed successfully
389
VerifyHeaderComplete(2, false);
390
VerifyTransferComplete(HM_RECV, HE_NONE);
391
VerifyDocumentContents("Goodbye!");
394
TEST_F(HttpBaseTest, SupportsReceiveViaStreamPull) {
395
// Switch to pull mode
396
ObtainDocumentStream();
397
VerifyDocumentStreamIsOpening();
399
// Queue response document
400
SetupSource(kHttpResponse);
401
VerifyDocumentStreamIsOpening();
406
// Pull document data
407
VerifyDocumentStreamOpenEvent();
408
ReadDocumentStreamData("Goodbye!");
409
VerifyDocumentStreamIsEOS();
411
// Document completed successfully
412
VerifyHeaderComplete(2, false);
413
VerifyTransferComplete(HM_RECV, HE_NONE);
414
VerifyDocumentContents("");
417
TEST_F(HttpBaseTest, DISABLED_AllowsCloseStreamBeforeDocumentIsComplete) {
419
// TODO: Remove extra logging once test failure is understood
420
int old_sev = talk_base::LogMessage::GetLogToDebug();
421
talk_base::LogMessage::LogToDebug(LS_VERBOSE);
424
// Switch to pull mode
425
ObtainDocumentStream();
426
VerifyDocumentStreamIsOpening();
428
// Queue response document
429
SetupSource(kHttpResponse);
430
VerifyDocumentStreamIsOpening();
435
// Pull some of the data
436
VerifyDocumentStreamOpenEvent();
437
ReadDocumentStreamData("Goodb");
439
// We've seen the header by now
440
VerifyHeaderComplete(1, false);
442
// Close the pull stream, this will transition back to push I/O.
443
http_stream->Close();
444
Thread::Current()->ProcessMessages(0);
446
// Remainder of document completed successfully
447
VerifyTransferComplete(HM_RECV, HE_NONE);
448
VerifyDocumentContents("ye!");
450
talk_base::LogMessage::LogToDebug(old_sev);
453
TEST_F(HttpBaseTest, AllowsGetDocumentStreamInResponseToHttpHeader) {
454
// Queue response document
455
SetupSource(kHttpResponse);
457
// Switch to pull mode in response to header arrival
458
obtain_stream = true;
463
// We've already seen the header, but not data has arrived
464
VerifyHeaderComplete(1, false);
465
VerifyDocumentContents("");
467
// Pull the document data
468
ReadDocumentStreamData("Goodbye!");
469
VerifyDocumentStreamIsEOS();
471
// Document completed successfully
472
VerifyTransferComplete(HM_RECV, HE_NONE);
473
VerifyDocumentContents("");
476
TEST_F(HttpBaseTest, AllowsGetDocumentStreamWithEmptyDocumentBody) {
477
// Queue empty response document
478
SetupSource(kHttpEmptyResponse);
480
// Switch to pull mode in response to header arrival
481
obtain_stream = true;
486
// We've already seen the header, but not data has arrived
487
VerifyHeaderComplete(1, true);
488
VerifyDocumentContents("");
490
// The document is still open, until we attempt to read
491
ASSERT_TRUE(NULL != http_stream);
492
EXPECT_EQ(SS_OPEN, http_stream->GetState());
494
// Attempt to read data, and discover EOS
495
VerifyDocumentStreamIsEOS();
497
// Document completed successfully
498
VerifyTransferComplete(HM_RECV, HE_NONE);
499
VerifyDocumentContents("");
502
TEST_F(HttpBaseTest, SignalsDocumentStreamCloseOnUnexpectedClose) {
503
// Switch to pull mode
504
ObtainDocumentStream();
505
VerifyDocumentStreamIsOpening();
507
// Queue response document
508
SetupSource(kHttpResponsePrefix);
509
VerifyDocumentStreamIsOpening();
514
// Pull document data
515
VerifyDocumentStreamOpenEvent();
516
ReadDocumentStreamData("Goodbye!");
518
// Simulate unexpected close
519
src.SetState(SS_CLOSED);
521
// Observe error event on document stream
522
EXPECT_EQ(testing::SSE_ERROR, sink.Events(http_stream));
524
// Future reads give an error
526
char buffer[5] = { 0 };
527
EXPECT_EQ(SR_ERROR, http_stream->Read(buffer, sizeof(buffer), NULL, &error));
528
EXPECT_EQ(HE_DISCONNECTED, error);
530
// Document completed with error
531
VerifyHeaderComplete(2, false);
532
VerifyTransferComplete(HM_RECV, HE_DISCONNECTED);
533
VerifyDocumentContents("");
536
} // namespace talk_base