blob: 35321da243e4bd933f2a82a7aac68a2dfa317244 [file] [log] [blame]
deadbeeff137e972017-03-23 15:45:49 -07001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020013#include "rtc_base/gunit.h"
14#include "rtc_base/httpbase.h"
15#include "rtc_base/testutils.h"
deadbeeff137e972017-03-23 15:45:49 -070016
17namespace rtc {
18
19const char* const kHttpResponse =
Yves Gerey665174f2018-06-19 15:03:05 +020020 "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";
deadbeeff137e972017-03-23 15:45:49 -070029
30const char* const kHttpEmptyResponse =
Yves Gerey665174f2018-06-19 15:03:05 +020031 "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";
deadbeeff137e972017-03-23 15:45:49 -070036
deadbeeff137e972017-03-23 15:45:49 -070037class HttpBaseTest : public testing::Test, public IHttpNotify {
Yves Gerey665174f2018-06-19 15:03:05 +020038 public:
deadbeeff137e972017-03-23 15:45:49 -070039 enum EventType { E_HEADER_COMPLETE, E_COMPLETE, E_CLOSED };
40 struct Event {
41 EventType event;
42 bool chunked;
43 size_t data_size;
44 HttpMode mode;
45 HttpError err;
46 };
Niels Möllerb4731ff2018-06-25 10:41:46 +020047 HttpBaseTest() : mem(nullptr), http_stream(nullptr) {}
deadbeeff137e972017-03-23 15:45:49 -070048
Steve Anton9de3aac2017-10-24 10:08:26 -070049 void TearDown() override {
deadbeeff137e972017-03-23 15:45:49 -070050 delete http_stream;
51 // Avoid an ASSERT, in case a test doesn't clean up properly
52 base.abort(HE_NONE);
53 }
54
Steve Anton9de3aac2017-10-24 10:08:26 -070055 HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) override {
Mirko Bonadei675513b2017-11-09 11:09:25 +010056 RTC_LOG_F(LS_VERBOSE) << "chunked: " << chunked << " size: " << data_size;
Yves Gerey665174f2018-06-19 15:03:05 +020057 Event e = {E_HEADER_COMPLETE, chunked, data_size, HM_NONE, HE_NONE};
deadbeeff137e972017-03-23 15:45:49 -070058 events.push_back(e);
deadbeeff137e972017-03-23 15:45:49 -070059 return HE_NONE;
60 }
Steve Anton9de3aac2017-10-24 10:08:26 -070061 void onHttpComplete(HttpMode mode, HttpError err) override {
Mirko Bonadei675513b2017-11-09 11:09:25 +010062 RTC_LOG_F(LS_VERBOSE) << "mode: " << mode << " err: " << err;
Yves Gerey665174f2018-06-19 15:03:05 +020063 Event e = {E_COMPLETE, false, 0, mode, err};
deadbeeff137e972017-03-23 15:45:49 -070064 events.push_back(e);
65 }
Steve Anton9de3aac2017-10-24 10:08:26 -070066 void onHttpClosed(HttpError err) override {
Mirko Bonadei675513b2017-11-09 11:09:25 +010067 RTC_LOG_F(LS_VERBOSE) << "err: " << err;
Yves Gerey665174f2018-06-19 15:03:05 +020068 Event e = {E_CLOSED, false, 0, HM_NONE, err};
deadbeeff137e972017-03-23 15:45:49 -070069 events.push_back(e);
70 }
71
72 void SetupSource(const char* response);
73
74 void VerifyHeaderComplete(size_t event_count, bool empty_doc);
75 void VerifyDocumentContents(const char* expected_data,
76 size_t expected_length = SIZE_UNKNOWN);
77
deadbeeff137e972017-03-23 15:45:49 -070078 void VerifyDocumentStreamIsOpening();
79 void VerifyDocumentStreamOpenEvent();
80 void ReadDocumentStreamData(const char* expected_data);
81 void VerifyDocumentStreamIsEOS();
82
83 void SetupDocument(const char* response);
84 void VerifySourceContents(const char* expected_data,
85 size_t expected_length = SIZE_UNKNOWN);
86
87 void VerifyTransferComplete(HttpMode mode, HttpError error);
88
89 HttpBase base;
90 MemoryStream* mem;
91 HttpResponseData data;
92
93 // The source of http data, and source events
kwibergd0d81482017-04-18 03:18:22 -070094 webrtc::testing::StreamSource src;
deadbeeff137e972017-03-23 15:45:49 -070095 std::vector<Event> events;
96
Niels Möllerb4731ff2018-06-25 10:41:46 +020097 // Stream events
deadbeeff137e972017-03-23 15:45:49 -070098 StreamInterface* http_stream;
kwibergd0d81482017-04-18 03:18:22 -070099 webrtc::testing::StreamSink sink;
deadbeeff137e972017-03-23 15:45:49 -0700100};
101
102void HttpBaseTest::SetupSource(const char* http_data) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100103 RTC_LOG_F(LS_VERBOSE) << "Enter";
deadbeeff137e972017-03-23 15:45:49 -0700104
105 src.SetState(SS_OPENING);
106 src.QueueString(http_data);
107
108 base.notify(this);
109 base.attach(&src);
110 EXPECT_TRUE(events.empty());
111
112 src.SetState(SS_OPEN);
113 ASSERT_EQ(1U, events.size());
114 EXPECT_EQ(E_COMPLETE, events[0].event);
115 EXPECT_EQ(HM_CONNECT, events[0].mode);
116 EXPECT_EQ(HE_NONE, events[0].err);
117 events.clear();
118
119 mem = new MemoryStream;
120 data.document.reset(mem);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100121 RTC_LOG_F(LS_VERBOSE) << "Exit";
deadbeeff137e972017-03-23 15:45:49 -0700122}
123
124void HttpBaseTest::VerifyHeaderComplete(size_t event_count, bool empty_doc) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100125 RTC_LOG_F(LS_VERBOSE) << "Enter";
deadbeeff137e972017-03-23 15:45:49 -0700126
127 ASSERT_EQ(event_count, events.size());
128 EXPECT_EQ(E_HEADER_COMPLETE, events[0].event);
129
130 std::string header;
131 EXPECT_EQ(HVER_1_1, data.version);
132 EXPECT_EQ(static_cast<uint32_t>(HC_OK), data.scode);
133 EXPECT_TRUE(data.hasHeader(HH_PROXY_AUTHORIZATION, &header));
134 EXPECT_EQ("42", header);
135 EXPECT_TRUE(data.hasHeader(HH_CONNECTION, &header));
136 EXPECT_EQ("Keep-Alive", header);
137
138 if (empty_doc) {
139 EXPECT_FALSE(events[0].chunked);
140 EXPECT_EQ(0U, events[0].data_size);
141
142 EXPECT_TRUE(data.hasHeader(HH_CONTENT_LENGTH, &header));
143 EXPECT_EQ("0", header);
144 } else {
145 EXPECT_TRUE(events[0].chunked);
146 EXPECT_EQ(SIZE_UNKNOWN, events[0].data_size);
147
148 EXPECT_TRUE(data.hasHeader(HH_CONTENT_TYPE, &header));
149 EXPECT_EQ("text/plain", header);
150 EXPECT_TRUE(data.hasHeader(HH_TRANSFER_ENCODING, &header));
151 EXPECT_EQ("chunked", header);
152 }
Mirko Bonadei675513b2017-11-09 11:09:25 +0100153 RTC_LOG_F(LS_VERBOSE) << "Exit";
deadbeeff137e972017-03-23 15:45:49 -0700154}
155
156void HttpBaseTest::VerifyDocumentContents(const char* expected_data,
157 size_t expected_length) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100158 RTC_LOG_F(LS_VERBOSE) << "Enter";
deadbeeff137e972017-03-23 15:45:49 -0700159
160 if (SIZE_UNKNOWN == expected_length) {
161 expected_length = strlen(expected_data);
162 }
163 EXPECT_EQ(mem, data.document.get());
164
165 size_t length;
166 mem->GetSize(&length);
167 EXPECT_EQ(expected_length, length);
168 EXPECT_TRUE(0 == memcmp(expected_data, mem->GetBuffer(), length));
Mirko Bonadei675513b2017-11-09 11:09:25 +0100169 RTC_LOG_F(LS_VERBOSE) << "Exit";
deadbeeff137e972017-03-23 15:45:49 -0700170}
171
deadbeeff137e972017-03-23 15:45:49 -0700172void HttpBaseTest::VerifyDocumentStreamIsOpening() {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100173 RTC_LOG_F(LS_VERBOSE) << "Enter";
deadbeeff137e972017-03-23 15:45:49 -0700174 ASSERT_TRUE(nullptr != http_stream);
175 EXPECT_EQ(0, sink.Events(http_stream));
176 EXPECT_EQ(SS_OPENING, http_stream->GetState());
177
178 size_t read = 0;
Yves Gerey665174f2018-06-19 15:03:05 +0200179 char buffer[5] = {0};
deadbeeff137e972017-03-23 15:45:49 -0700180 EXPECT_EQ(SR_BLOCK,
181 http_stream->Read(buffer, sizeof(buffer), &read, nullptr));
Mirko Bonadei675513b2017-11-09 11:09:25 +0100182 RTC_LOG_F(LS_VERBOSE) << "Exit";
deadbeeff137e972017-03-23 15:45:49 -0700183}
184
185void HttpBaseTest::VerifyDocumentStreamOpenEvent() {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100186 RTC_LOG_F(LS_VERBOSE) << "Enter";
deadbeeff137e972017-03-23 15:45:49 -0700187
188 ASSERT_TRUE(nullptr != http_stream);
189 EXPECT_EQ(SE_OPEN | SE_READ, sink.Events(http_stream));
190 EXPECT_EQ(SS_OPEN, http_stream->GetState());
191
192 // HTTP headers haven't arrived yet
193 EXPECT_EQ(0U, events.size());
194 EXPECT_EQ(static_cast<uint32_t>(HC_INTERNAL_SERVER_ERROR), data.scode);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100195 RTC_LOG_F(LS_VERBOSE) << "Exit";
deadbeeff137e972017-03-23 15:45:49 -0700196}
197
198void HttpBaseTest::ReadDocumentStreamData(const char* expected_data) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100199 RTC_LOG_F(LS_VERBOSE) << "Enter";
deadbeeff137e972017-03-23 15:45:49 -0700200
201 ASSERT_TRUE(nullptr != http_stream);
202 EXPECT_EQ(SS_OPEN, http_stream->GetState());
203
204 // Pump the HTTP I/O using Read, and verify the results.
205 size_t verified_length = 0;
206 const size_t expected_length = strlen(expected_data);
207 while (verified_length < expected_length) {
208 size_t read = 0;
Yves Gerey665174f2018-06-19 15:03:05 +0200209 char buffer[5] = {0};
deadbeeff137e972017-03-23 15:45:49 -0700210 size_t amt_to_read =
211 std::min(expected_length - verified_length, sizeof(buffer));
212 EXPECT_EQ(SR_SUCCESS,
213 http_stream->Read(buffer, amt_to_read, &read, nullptr));
214 EXPECT_EQ(amt_to_read, read);
215 EXPECT_TRUE(0 == memcmp(expected_data + verified_length, buffer, read));
216 verified_length += read;
217 }
Mirko Bonadei675513b2017-11-09 11:09:25 +0100218 RTC_LOG_F(LS_VERBOSE) << "Exit";
deadbeeff137e972017-03-23 15:45:49 -0700219}
220
221void HttpBaseTest::VerifyDocumentStreamIsEOS() {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100222 RTC_LOG_F(LS_VERBOSE) << "Enter";
deadbeeff137e972017-03-23 15:45:49 -0700223
224 ASSERT_TRUE(nullptr != http_stream);
225 size_t read = 0;
Yves Gerey665174f2018-06-19 15:03:05 +0200226 char buffer[5] = {0};
deadbeeff137e972017-03-23 15:45:49 -0700227 EXPECT_EQ(SR_EOS, http_stream->Read(buffer, sizeof(buffer), &read, nullptr));
228 EXPECT_EQ(SS_CLOSED, http_stream->GetState());
229
230 // When EOS is caused by Read, we don't expect SE_CLOSE
231 EXPECT_EQ(0, sink.Events(http_stream));
Mirko Bonadei675513b2017-11-09 11:09:25 +0100232 RTC_LOG_F(LS_VERBOSE) << "Exit";
deadbeeff137e972017-03-23 15:45:49 -0700233}
234
235void HttpBaseTest::SetupDocument(const char* document_data) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100236 RTC_LOG_F(LS_VERBOSE) << "Enter";
deadbeeff137e972017-03-23 15:45:49 -0700237 src.SetState(SS_OPEN);
238
239 base.notify(this);
240 base.attach(&src);
241 EXPECT_TRUE(events.empty());
242
243 if (document_data) {
244 // Note: we could just call data.set_success("text/plain", mem), but that
245 // won't allow us to use the chunked transfer encoding.
246 mem = new MemoryStream(document_data);
247 data.document.reset(mem);
248 data.setHeader(HH_CONTENT_TYPE, "text/plain");
249 data.setHeader(HH_TRANSFER_ENCODING, "chunked");
250 } else {
251 data.setHeader(HH_CONTENT_LENGTH, "0");
252 }
253 data.scode = HC_OK;
254 data.setHeader(HH_PROXY_AUTHORIZATION, "42");
255 data.setHeader(HH_CONNECTION, "Keep-Alive");
Mirko Bonadei675513b2017-11-09 11:09:25 +0100256 RTC_LOG_F(LS_VERBOSE) << "Exit";
deadbeeff137e972017-03-23 15:45:49 -0700257}
258
259void HttpBaseTest::VerifySourceContents(const char* expected_data,
260 size_t expected_length) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100261 RTC_LOG_F(LS_VERBOSE) << "Enter";
deadbeeff137e972017-03-23 15:45:49 -0700262 if (SIZE_UNKNOWN == expected_length) {
263 expected_length = strlen(expected_data);
264 }
265 std::string contents = src.ReadData();
266 EXPECT_EQ(expected_length, contents.length());
267 EXPECT_TRUE(0 == memcmp(expected_data, contents.data(), expected_length));
Mirko Bonadei675513b2017-11-09 11:09:25 +0100268 RTC_LOG_F(LS_VERBOSE) << "Exit";
deadbeeff137e972017-03-23 15:45:49 -0700269}
270
271void HttpBaseTest::VerifyTransferComplete(HttpMode mode, HttpError error) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100272 RTC_LOG_F(LS_VERBOSE) << "Enter";
deadbeeff137e972017-03-23 15:45:49 -0700273 // Verify that http operation has completed
274 ASSERT_TRUE(events.size() > 0);
275 size_t last_event = events.size() - 1;
276 EXPECT_EQ(E_COMPLETE, events[last_event].event);
277 EXPECT_EQ(mode, events[last_event].mode);
278 EXPECT_EQ(error, events[last_event].err);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100279 RTC_LOG_F(LS_VERBOSE) << "Exit";
deadbeeff137e972017-03-23 15:45:49 -0700280}
281
282//
283// Tests
284//
285
286TEST_F(HttpBaseTest, SupportsSend) {
287 // Queue response document
288 SetupDocument("Goodbye!");
289
290 // Begin send
291 base.send(&data);
292
293 // Send completed successfully
294 VerifyTransferComplete(HM_SEND, HE_NONE);
295 VerifySourceContents(kHttpResponse);
296}
297
298TEST_F(HttpBaseTest, SupportsSendNoDocument) {
299 // Queue response document
300 SetupDocument(nullptr);
301
302 // Begin send
303 base.send(&data);
304
305 // Send completed successfully
306 VerifyTransferComplete(HM_SEND, HE_NONE);
307 VerifySourceContents(kHttpEmptyResponse);
308}
309
310TEST_F(HttpBaseTest, SignalsCompleteOnInterruptedSend) {
311 // This test is attempting to expose a bug that occurs when a particular
312 // base objects is used for receiving, and then used for sending. In
313 // particular, the HttpParser state is different after receiving. Simulate
314 // that here.
315 SetupSource(kHttpResponse);
316 base.recv(&data);
317 VerifyTransferComplete(HM_RECV, HE_NONE);
318
319 src.Clear();
320 data.clear(true);
321 events.clear();
322 base.detach();
323
324 // Queue response document
325 SetupDocument("Goodbye!");
326
327 // Prevent entire response from being sent
328 const size_t kInterruptedLength = strlen(kHttpResponse) - 1;
329 src.SetWriteBlock(kInterruptedLength);
330
331 // Begin send
332 base.send(&data);
333
334 // Document is mostly complete, but no completion signal yet.
335 EXPECT_TRUE(events.empty());
336 VerifySourceContents(kHttpResponse, kInterruptedLength);
337
338 src.SetState(SS_CLOSED);
339
340 // Send completed with disconnect error, and no additional data.
341 VerifyTransferComplete(HM_SEND, HE_DISCONNECTED);
342 EXPECT_TRUE(src.ReadData().empty());
343}
344
345TEST_F(HttpBaseTest, SupportsReceiveViaDocumentPush) {
346 // Queue response document
347 SetupSource(kHttpResponse);
348
349 // Begin receive
350 base.recv(&data);
351
352 // Document completed successfully
353 VerifyHeaderComplete(2, false);
354 VerifyTransferComplete(HM_RECV, HE_NONE);
355 VerifyDocumentContents("Goodbye!");
356}
357
Yves Gerey665174f2018-06-19 15:03:05 +0200358} // namespace rtc