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>(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>(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   int 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