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 };
HttpBaseTest()57 HttpBaseTest() : mem(NULL), obtain_stream(false), http_stream(NULL) { }
58
SetUp()59 virtual void SetUp() { }
TearDown()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
onHttpHeaderComplete(bool chunked,size_t & data_size)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 }
onHttpComplete(HttpMode mode,HttpError err)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 }
onHttpClosed(HttpError err)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
SetupSource(const char * http_data)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
VerifyHeaderComplete(size_t event_count,bool empty_doc)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
VerifyDocumentContents(const char * expected_data,size_t expected_length)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
ObtainDocumentStream()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
VerifyDocumentStreamIsOpening()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
VerifyDocumentStreamOpenEvent()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
ReadDocumentStreamData(const char * expected_data)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
VerifyDocumentStreamIsEOS()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
SetupDocument(const char * document_data)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
VerifySourceContents(const char * expected_data,size_t expected_length)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
VerifyTransferComplete(HttpMode mode,HttpError error)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
TEST_F(HttpBaseTest,SupportsSend)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
TEST_F(HttpBaseTest,SupportsSendNoDocument)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
TEST_F(HttpBaseTest,SignalsCompleteOnInterruptedSend)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
TEST_F(HttpBaseTest,SupportsReceiveViaDocumentPush)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
TEST_F(HttpBaseTest,SupportsReceiveViaStreamPull)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
TEST_F(HttpBaseTest,DISABLED_AllowsCloseStreamBeforeDocumentIsComplete)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
TEST_F(HttpBaseTest,AllowsGetDocumentStreamInResponseToHttpHeader)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
TEST_F(HttpBaseTest,AllowsGetDocumentStreamWithEmptyDocumentBody)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
TEST_F(HttpBaseTest,SignalsDocumentStreamCloseOnUnexpectedClose)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