- add third_party src.
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / base / httpbase_unittest.cc
1 /*
2  * libjingle
3  * Copyright 2004--2011, Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
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.
15  *
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.
26  */
27
28 #include "talk/base/gunit.h"
29 #include "talk/base/httpbase.h"
30 #include "talk/base/testutils.h"
31
32 namespace talk_base {
33
34 const char* const kHttpResponse =
35   "HTTP/1.1 200\r\n"
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"
40   "\r\n"
41   "00000008\r\n"
42   "Goodbye!\r\n"
43   "0\r\n\r\n";
44
45 const char* const kHttpEmptyResponse =
46   "HTTP/1.1 200\r\n"
47   "Connection: Keep-Alive\r\n"
48   "Content-Length: 0\r\n"
49   "Proxy-Authorization: 42\r\n"
50   "\r\n";
51
52 const char* const kHttpResponsePrefix =
53   "HTTP/1.1 200\r\n"
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"
58   "\r\n"
59   "8\r\n"
60   "Goodbye!\r\n";
61
62 class HttpBaseTest : public testing::Test, public IHttpNotify {
63 public:
64   enum EventType { E_HEADER_COMPLETE, E_COMPLETE, E_CLOSED };
65   struct Event {
66     EventType event;
67     bool chunked;
68     size_t data_size;
69     HttpMode mode;
70     HttpError err;
71   };
72   HttpBaseTest() : mem(NULL), obtain_stream(false), http_stream(NULL) { }
73
74   virtual void SetUp() { }
75   virtual void TearDown() {
76     delete http_stream;
77     // Avoid an ASSERT, in case a test doesn't clean up properly
78     base.abort(HE_NONE);
79   }
80
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};
84     events.push_back(e);
85     if (obtain_stream) {
86       ObtainDocumentStream();
87     }
88     return HE_NONE;
89   }
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 };
93     events.push_back(e);
94   }
95   virtual void onHttpClosed(HttpError err) {
96     LOG_F(LS_VERBOSE) << "err: " << err;
97     Event e = { E_CLOSED, false, 0, HM_NONE, err };
98     events.push_back(e);
99   }
100
101   void SetupSource(const char* response);
102
103   void VerifyHeaderComplete(size_t event_count, bool empty_doc);
104   void VerifyDocumentContents(const char* expected_data,
105                               size_t expected_length = SIZE_UNKNOWN);
106
107   void ObtainDocumentStream();
108   void VerifyDocumentStreamIsOpening();
109   void VerifyDocumentStreamOpenEvent();
110   void ReadDocumentStreamData(const char* expected_data);
111   void VerifyDocumentStreamIsEOS();
112
113   void SetupDocument(const char* response);
114   void VerifySourceContents(const char* expected_data,
115                             size_t expected_length = SIZE_UNKNOWN);
116
117   void VerifyTransferComplete(HttpMode mode, HttpError error);
118
119   HttpBase base;
120   MemoryStream* mem;
121   HttpResponseData data;
122
123   // The source of http data, and source events
124   testing::StreamSource src;
125   std::vector<Event> events;
126
127   // Document stream, and stream events
128   bool obtain_stream;
129   StreamInterface* http_stream;
130   testing::StreamSink sink;
131 };
132
133 void HttpBaseTest::SetupSource(const char* http_data) {
134   LOG_F(LS_VERBOSE) << "Enter";
135
136   src.SetState(SS_OPENING);
137   src.QueueString(http_data);
138
139   base.notify(this);
140   base.attach(&src);
141   EXPECT_TRUE(events.empty());
142
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);
148   events.clear();
149
150   mem = new MemoryStream;
151   data.document.reset(mem);
152   LOG_F(LS_VERBOSE) << "Exit";
153 }
154
155 void HttpBaseTest::VerifyHeaderComplete(size_t event_count, bool empty_doc) {
156   LOG_F(LS_VERBOSE) << "Enter";
157
158   ASSERT_EQ(event_count, events.size());
159   EXPECT_EQ(E_HEADER_COMPLETE, events[0].event);
160
161   std::string header;
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);
168
169   if (empty_doc) {
170     EXPECT_FALSE(events[0].chunked);
171     EXPECT_EQ(0U, events[0].data_size);
172
173     EXPECT_TRUE(data.hasHeader(HH_CONTENT_LENGTH, &header));
174     EXPECT_EQ("0", header);
175   } else {
176     EXPECT_TRUE(events[0].chunked);
177     EXPECT_EQ(SIZE_UNKNOWN, events[0].data_size);
178
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);
183   }
184   LOG_F(LS_VERBOSE) << "Exit";
185 }
186
187 void HttpBaseTest::VerifyDocumentContents(const char* expected_data,
188                                           size_t expected_length) {
189   LOG_F(LS_VERBOSE) << "Enter";
190
191   if (SIZE_UNKNOWN == expected_length) {
192     expected_length = strlen(expected_data);
193   }
194   EXPECT_EQ(mem, data.document.get());
195
196   size_t length;
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";
201 }
202
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";
210 }
211
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());
217
218   size_t read = 0;
219   char buffer[5] = { 0 };
220   EXPECT_EQ(SR_BLOCK, http_stream->Read(buffer, sizeof(buffer), &read, NULL));
221   LOG_F(LS_VERBOSE) << "Exit";
222 }
223
224 void HttpBaseTest::VerifyDocumentStreamOpenEvent() {
225   LOG_F(LS_VERBOSE) << "Enter";
226
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());
230
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";
235 }
236
237 void HttpBaseTest::ReadDocumentStreamData(const char* expected_data) {
238   LOG_F(LS_VERBOSE) << "Enter";
239
240   ASSERT_TRUE(NULL != http_stream);
241   EXPECT_EQ(SS_OPEN, http_stream->GetState());
242
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) {
247     size_t read = 0;
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;
254   }
255   LOG_F(LS_VERBOSE) << "Exit";
256 }
257
258 void HttpBaseTest::VerifyDocumentStreamIsEOS() {
259   LOG_F(LS_VERBOSE) << "Enter";
260
261   ASSERT_TRUE(NULL != http_stream);
262   size_t read = 0;
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());
266
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";
270 }
271
272 void HttpBaseTest::SetupDocument(const char* document_data) {
273   LOG_F(LS_VERBOSE) << "Enter";
274   src.SetState(SS_OPEN);
275
276   base.notify(this);
277   base.attach(&src);
278   EXPECT_TRUE(events.empty());
279
280   if (document_data) {
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");
287   } else {
288     data.setHeader(HH_CONTENT_LENGTH, "0");
289   }
290   data.scode = HC_OK;
291   data.setHeader(HH_PROXY_AUTHORIZATION, "42");
292   data.setHeader(HH_CONNECTION, "Keep-Alive");
293   LOG_F(LS_VERBOSE) << "Exit";
294 }
295
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);
301   }
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";
306 }
307
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";
317 }
318
319 //
320 // Tests
321 //
322
323 TEST_F(HttpBaseTest, SupportsSend) {
324   // Queue response document
325   SetupDocument("Goodbye!");
326
327   // Begin send
328   base.send(&data);
329
330   // Send completed successfully
331   VerifyTransferComplete(HM_SEND, HE_NONE);
332   VerifySourceContents(kHttpResponse);
333 }
334
335 TEST_F(HttpBaseTest, SupportsSendNoDocument) {
336   // Queue response document
337   SetupDocument(NULL);
338
339   // Begin send
340   base.send(&data);
341
342   // Send completed successfully
343   VerifyTransferComplete(HM_SEND, HE_NONE);
344   VerifySourceContents(kHttpEmptyResponse);
345 }
346
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
351   // that here.
352   SetupSource(kHttpResponse);
353   base.recv(&data);
354   VerifyTransferComplete(HM_RECV, HE_NONE);
355
356   src.Clear();
357   data.clear(true);
358   events.clear();
359   base.detach();
360
361   // Queue response document
362   SetupDocument("Goodbye!");
363
364   // Prevent entire response from being sent
365   const size_t kInterruptedLength = strlen(kHttpResponse) - 1;
366   src.SetWriteBlock(kInterruptedLength);
367
368   // Begin send
369   base.send(&data);
370
371   // Document is mostly complete, but no completion signal yet.
372   EXPECT_TRUE(events.empty());
373   VerifySourceContents(kHttpResponse, kInterruptedLength);
374
375   src.SetState(SS_CLOSED);
376
377   // Send completed with disconnect error, and no additional data.
378   VerifyTransferComplete(HM_SEND, HE_DISCONNECTED);
379   EXPECT_TRUE(src.ReadData().empty());
380 }
381
382 TEST_F(HttpBaseTest, SupportsReceiveViaDocumentPush) {
383   // Queue response document
384   SetupSource(kHttpResponse);
385
386   // Begin receive
387   base.recv(&data);
388
389   // Document completed successfully
390   VerifyHeaderComplete(2, false);
391   VerifyTransferComplete(HM_RECV, HE_NONE);
392   VerifyDocumentContents("Goodbye!");
393 }
394
395 TEST_F(HttpBaseTest, SupportsReceiveViaStreamPull) {
396   // Switch to pull mode
397   ObtainDocumentStream();
398   VerifyDocumentStreamIsOpening();
399
400   // Queue response document
401   SetupSource(kHttpResponse);
402   VerifyDocumentStreamIsOpening();
403
404   // Begin receive
405   base.recv(&data);
406
407   // Pull document data
408   VerifyDocumentStreamOpenEvent();
409   ReadDocumentStreamData("Goodbye!");
410   VerifyDocumentStreamIsEOS();
411
412   // Document completed successfully
413   VerifyHeaderComplete(2, false);
414   VerifyTransferComplete(HM_RECV, HE_NONE);
415   VerifyDocumentContents("");
416 }
417
418 TEST_F(HttpBaseTest, DISABLED_AllowsCloseStreamBeforeDocumentIsComplete) {
419
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);
423
424
425   // Switch to pull mode
426   ObtainDocumentStream();
427   VerifyDocumentStreamIsOpening();
428
429   // Queue response document
430   SetupSource(kHttpResponse);
431   VerifyDocumentStreamIsOpening();
432
433   // Begin receive
434   base.recv(&data);
435
436   // Pull some of the data
437   VerifyDocumentStreamOpenEvent();
438   ReadDocumentStreamData("Goodb");
439
440   // We've seen the header by now
441   VerifyHeaderComplete(1, false);
442
443   // Close the pull stream, this will transition back to push I/O.
444   http_stream->Close();
445   Thread::Current()->ProcessMessages(0);
446
447   // Remainder of document completed successfully
448   VerifyTransferComplete(HM_RECV, HE_NONE);
449   VerifyDocumentContents("ye!");
450
451   talk_base::LogMessage::LogToDebug(old_sev);
452 }
453
454 TEST_F(HttpBaseTest, AllowsGetDocumentStreamInResponseToHttpHeader) {
455   // Queue response document
456   SetupSource(kHttpResponse);
457
458   // Switch to pull mode in response to header arrival
459   obtain_stream = true;
460
461   // Begin receive
462   base.recv(&data);
463
464   // We've already seen the header, but not data has arrived
465   VerifyHeaderComplete(1, false);
466   VerifyDocumentContents("");
467
468   // Pull the document data
469   ReadDocumentStreamData("Goodbye!");
470   VerifyDocumentStreamIsEOS();
471
472   // Document completed successfully
473   VerifyTransferComplete(HM_RECV, HE_NONE);
474   VerifyDocumentContents("");
475 }
476
477 TEST_F(HttpBaseTest, AllowsGetDocumentStreamWithEmptyDocumentBody) {
478   // Queue empty response document
479   SetupSource(kHttpEmptyResponse);
480
481   // Switch to pull mode in response to header arrival
482   obtain_stream = true;
483
484   // Begin receive
485   base.recv(&data);
486
487   // We've already seen the header, but not data has arrived
488   VerifyHeaderComplete(1, true);
489   VerifyDocumentContents("");
490
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());
494
495   // Attempt to read data, and discover EOS
496   VerifyDocumentStreamIsEOS();
497
498   // Document completed successfully
499   VerifyTransferComplete(HM_RECV, HE_NONE);
500   VerifyDocumentContents("");
501 }
502
503 TEST_F(HttpBaseTest, SignalsDocumentStreamCloseOnUnexpectedClose) {
504   // Switch to pull mode
505   ObtainDocumentStream();
506   VerifyDocumentStreamIsOpening();
507
508   // Queue response document
509   SetupSource(kHttpResponsePrefix);
510   VerifyDocumentStreamIsOpening();
511
512   // Begin receive
513   base.recv(&data);
514
515   // Pull document data
516   VerifyDocumentStreamOpenEvent();
517   ReadDocumentStreamData("Goodbye!");
518
519   // Simulate unexpected close
520   src.SetState(SS_CLOSED);
521
522   // Observe error event on document stream
523   EXPECT_EQ(testing::SSE_ERROR, sink.Events(http_stream));
524
525   // Future reads give an error
526   int error = 0;
527   char buffer[5] = { 0 };
528   EXPECT_EQ(SR_ERROR, http_stream->Read(buffer, sizeof(buffer), NULL, &error));
529   EXPECT_EQ(HE_DISCONNECTED, error);
530
531   // Document completed with error
532   VerifyHeaderComplete(2, false);
533   VerifyTransferComplete(HM_RECV, HE_DISCONNECTED);
534   VerifyDocumentContents("");
535 }
536
537 } // namespace talk_base