| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 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 |  | 
| Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 13 | #include "rtc_base/gunit.h" | 
|  | 14 | #include "rtc_base/httpbase.h" | 
|  | 15 | #include "rtc_base/testutils.h" | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 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 | }; | 
|  | 57 | HttpBaseTest() : mem(nullptr), obtain_stream(false), http_stream(nullptr) {} | 
|  | 58 |  | 
| Steve Anton | 9de3aac | 2017-10-24 10:08:26 -0700 | [diff] [blame] | 59 | void TearDown() override { | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 60 | delete http_stream; | 
|  | 61 | // Avoid an ASSERT, in case a test doesn't clean up properly | 
|  | 62 | base.abort(HE_NONE); | 
|  | 63 | } | 
|  | 64 |  | 
| Steve Anton | 9de3aac | 2017-10-24 10:08:26 -0700 | [diff] [blame] | 65 | HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) override { | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 66 | RTC_LOG_F(LS_VERBOSE) << "chunked: " << chunked << " size: " << data_size; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 67 | Event e = { E_HEADER_COMPLETE, chunked, data_size, HM_NONE, HE_NONE}; | 
|  | 68 | events.push_back(e); | 
|  | 69 | if (obtain_stream) { | 
|  | 70 | ObtainDocumentStream(); | 
|  | 71 | } | 
|  | 72 | return HE_NONE; | 
|  | 73 | } | 
| Steve Anton | 9de3aac | 2017-10-24 10:08:26 -0700 | [diff] [blame] | 74 | void onHttpComplete(HttpMode mode, HttpError err) override { | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 75 | RTC_LOG_F(LS_VERBOSE) << "mode: " << mode << " err: " << err; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 76 | Event e = { E_COMPLETE, false, 0, mode, err }; | 
|  | 77 | events.push_back(e); | 
|  | 78 | } | 
| Steve Anton | 9de3aac | 2017-10-24 10:08:26 -0700 | [diff] [blame] | 79 | void onHttpClosed(HttpError err) override { | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 80 | RTC_LOG_F(LS_VERBOSE) << "err: " << err; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 81 | Event e = { E_CLOSED, false, 0, HM_NONE, err }; | 
|  | 82 | events.push_back(e); | 
|  | 83 | } | 
|  | 84 |  | 
|  | 85 | void SetupSource(const char* response); | 
|  | 86 |  | 
|  | 87 | void VerifyHeaderComplete(size_t event_count, bool empty_doc); | 
|  | 88 | void VerifyDocumentContents(const char* expected_data, | 
|  | 89 | size_t expected_length = SIZE_UNKNOWN); | 
|  | 90 |  | 
|  | 91 | void ObtainDocumentStream(); | 
|  | 92 | void VerifyDocumentStreamIsOpening(); | 
|  | 93 | void VerifyDocumentStreamOpenEvent(); | 
|  | 94 | void ReadDocumentStreamData(const char* expected_data); | 
|  | 95 | void VerifyDocumentStreamIsEOS(); | 
|  | 96 |  | 
|  | 97 | void SetupDocument(const char* response); | 
|  | 98 | void VerifySourceContents(const char* expected_data, | 
|  | 99 | size_t expected_length = SIZE_UNKNOWN); | 
|  | 100 |  | 
|  | 101 | void VerifyTransferComplete(HttpMode mode, HttpError error); | 
|  | 102 |  | 
|  | 103 | HttpBase base; | 
|  | 104 | MemoryStream* mem; | 
|  | 105 | HttpResponseData data; | 
|  | 106 |  | 
|  | 107 | // The source of http data, and source events | 
| kwiberg | d0d8148 | 2017-04-18 03:18:22 -0700 | [diff] [blame] | 108 | webrtc::testing::StreamSource src; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 109 | std::vector<Event> events; | 
|  | 110 |  | 
|  | 111 | // Document stream, and stream events | 
|  | 112 | bool obtain_stream; | 
|  | 113 | StreamInterface* http_stream; | 
| kwiberg | d0d8148 | 2017-04-18 03:18:22 -0700 | [diff] [blame] | 114 | webrtc::testing::StreamSink sink; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 115 | }; | 
|  | 116 |  | 
|  | 117 | void HttpBaseTest::SetupSource(const char* http_data) { | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 118 | RTC_LOG_F(LS_VERBOSE) << "Enter"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 119 |  | 
|  | 120 | src.SetState(SS_OPENING); | 
|  | 121 | src.QueueString(http_data); | 
|  | 122 |  | 
|  | 123 | base.notify(this); | 
|  | 124 | base.attach(&src); | 
|  | 125 | EXPECT_TRUE(events.empty()); | 
|  | 126 |  | 
|  | 127 | src.SetState(SS_OPEN); | 
|  | 128 | ASSERT_EQ(1U, events.size()); | 
|  | 129 | EXPECT_EQ(E_COMPLETE, events[0].event); | 
|  | 130 | EXPECT_EQ(HM_CONNECT, events[0].mode); | 
|  | 131 | EXPECT_EQ(HE_NONE, events[0].err); | 
|  | 132 | events.clear(); | 
|  | 133 |  | 
|  | 134 | mem = new MemoryStream; | 
|  | 135 | data.document.reset(mem); | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 136 | RTC_LOG_F(LS_VERBOSE) << "Exit"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 137 | } | 
|  | 138 |  | 
|  | 139 | void HttpBaseTest::VerifyHeaderComplete(size_t event_count, bool empty_doc) { | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 140 | RTC_LOG_F(LS_VERBOSE) << "Enter"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 141 |  | 
|  | 142 | ASSERT_EQ(event_count, events.size()); | 
|  | 143 | EXPECT_EQ(E_HEADER_COMPLETE, events[0].event); | 
|  | 144 |  | 
|  | 145 | std::string header; | 
|  | 146 | EXPECT_EQ(HVER_1_1, data.version); | 
|  | 147 | EXPECT_EQ(static_cast<uint32_t>(HC_OK), data.scode); | 
|  | 148 | EXPECT_TRUE(data.hasHeader(HH_PROXY_AUTHORIZATION, &header)); | 
|  | 149 | EXPECT_EQ("42", header); | 
|  | 150 | EXPECT_TRUE(data.hasHeader(HH_CONNECTION, &header)); | 
|  | 151 | EXPECT_EQ("Keep-Alive", header); | 
|  | 152 |  | 
|  | 153 | if (empty_doc) { | 
|  | 154 | EXPECT_FALSE(events[0].chunked); | 
|  | 155 | EXPECT_EQ(0U, events[0].data_size); | 
|  | 156 |  | 
|  | 157 | EXPECT_TRUE(data.hasHeader(HH_CONTENT_LENGTH, &header)); | 
|  | 158 | EXPECT_EQ("0", header); | 
|  | 159 | } else { | 
|  | 160 | EXPECT_TRUE(events[0].chunked); | 
|  | 161 | EXPECT_EQ(SIZE_UNKNOWN, events[0].data_size); | 
|  | 162 |  | 
|  | 163 | EXPECT_TRUE(data.hasHeader(HH_CONTENT_TYPE, &header)); | 
|  | 164 | EXPECT_EQ("text/plain", header); | 
|  | 165 | EXPECT_TRUE(data.hasHeader(HH_TRANSFER_ENCODING, &header)); | 
|  | 166 | EXPECT_EQ("chunked", header); | 
|  | 167 | } | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 168 | RTC_LOG_F(LS_VERBOSE) << "Exit"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 169 | } | 
|  | 170 |  | 
|  | 171 | void HttpBaseTest::VerifyDocumentContents(const char* expected_data, | 
|  | 172 | size_t expected_length) { | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 173 | RTC_LOG_F(LS_VERBOSE) << "Enter"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 174 |  | 
|  | 175 | if (SIZE_UNKNOWN == expected_length) { | 
|  | 176 | expected_length = strlen(expected_data); | 
|  | 177 | } | 
|  | 178 | EXPECT_EQ(mem, data.document.get()); | 
|  | 179 |  | 
|  | 180 | size_t length; | 
|  | 181 | mem->GetSize(&length); | 
|  | 182 | EXPECT_EQ(expected_length, length); | 
|  | 183 | EXPECT_TRUE(0 == memcmp(expected_data, mem->GetBuffer(), length)); | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 184 | RTC_LOG_F(LS_VERBOSE) << "Exit"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 185 | } | 
|  | 186 |  | 
|  | 187 | void HttpBaseTest::ObtainDocumentStream() { | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 188 | RTC_LOG_F(LS_VERBOSE) << "Enter"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 189 | EXPECT_FALSE(http_stream); | 
|  | 190 | http_stream = base.GetDocumentStream(); | 
|  | 191 | ASSERT_TRUE(nullptr != http_stream); | 
|  | 192 | sink.Monitor(http_stream); | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 193 | RTC_LOG_F(LS_VERBOSE) << "Exit"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 194 | } | 
|  | 195 |  | 
|  | 196 | void HttpBaseTest::VerifyDocumentStreamIsOpening() { | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 197 | RTC_LOG_F(LS_VERBOSE) << "Enter"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 198 | ASSERT_TRUE(nullptr != http_stream); | 
|  | 199 | EXPECT_EQ(0, sink.Events(http_stream)); | 
|  | 200 | EXPECT_EQ(SS_OPENING, http_stream->GetState()); | 
|  | 201 |  | 
|  | 202 | size_t read = 0; | 
|  | 203 | char buffer[5] = { 0 }; | 
|  | 204 | EXPECT_EQ(SR_BLOCK, | 
|  | 205 | http_stream->Read(buffer, sizeof(buffer), &read, nullptr)); | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 206 | RTC_LOG_F(LS_VERBOSE) << "Exit"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 207 | } | 
|  | 208 |  | 
|  | 209 | void HttpBaseTest::VerifyDocumentStreamOpenEvent() { | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 210 | RTC_LOG_F(LS_VERBOSE) << "Enter"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 211 |  | 
|  | 212 | ASSERT_TRUE(nullptr != 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); | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 219 | RTC_LOG_F(LS_VERBOSE) << "Exit"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 220 | } | 
|  | 221 |  | 
|  | 222 | void HttpBaseTest::ReadDocumentStreamData(const char* expected_data) { | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 223 | RTC_LOG_F(LS_VERBOSE) << "Enter"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 224 |  | 
|  | 225 | ASSERT_TRUE(nullptr != 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, | 
|  | 237 | http_stream->Read(buffer, amt_to_read, &read, nullptr)); | 
|  | 238 | EXPECT_EQ(amt_to_read, read); | 
|  | 239 | EXPECT_TRUE(0 == memcmp(expected_data + verified_length, buffer, read)); | 
|  | 240 | verified_length += read; | 
|  | 241 | } | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 242 | RTC_LOG_F(LS_VERBOSE) << "Exit"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 243 | } | 
|  | 244 |  | 
|  | 245 | void HttpBaseTest::VerifyDocumentStreamIsEOS() { | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 246 | RTC_LOG_F(LS_VERBOSE) << "Enter"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 247 |  | 
|  | 248 | ASSERT_TRUE(nullptr != http_stream); | 
|  | 249 | size_t read = 0; | 
|  | 250 | char buffer[5] = { 0 }; | 
|  | 251 | EXPECT_EQ(SR_EOS, http_stream->Read(buffer, sizeof(buffer), &read, nullptr)); | 
|  | 252 | EXPECT_EQ(SS_CLOSED, http_stream->GetState()); | 
|  | 253 |  | 
|  | 254 | // When EOS is caused by Read, we don't expect SE_CLOSE | 
|  | 255 | EXPECT_EQ(0, sink.Events(http_stream)); | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 256 | RTC_LOG_F(LS_VERBOSE) << "Exit"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 257 | } | 
|  | 258 |  | 
|  | 259 | void HttpBaseTest::SetupDocument(const char* document_data) { | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 260 | RTC_LOG_F(LS_VERBOSE) << "Enter"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 261 | src.SetState(SS_OPEN); | 
|  | 262 |  | 
|  | 263 | base.notify(this); | 
|  | 264 | base.attach(&src); | 
|  | 265 | EXPECT_TRUE(events.empty()); | 
|  | 266 |  | 
|  | 267 | if (document_data) { | 
|  | 268 | // Note: we could just call data.set_success("text/plain", mem), but that | 
|  | 269 | // won't allow us to use the chunked transfer encoding. | 
|  | 270 | mem = new MemoryStream(document_data); | 
|  | 271 | data.document.reset(mem); | 
|  | 272 | data.setHeader(HH_CONTENT_TYPE, "text/plain"); | 
|  | 273 | data.setHeader(HH_TRANSFER_ENCODING, "chunked"); | 
|  | 274 | } else { | 
|  | 275 | data.setHeader(HH_CONTENT_LENGTH, "0"); | 
|  | 276 | } | 
|  | 277 | data.scode = HC_OK; | 
|  | 278 | data.setHeader(HH_PROXY_AUTHORIZATION, "42"); | 
|  | 279 | data.setHeader(HH_CONNECTION, "Keep-Alive"); | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 280 | RTC_LOG_F(LS_VERBOSE) << "Exit"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 281 | } | 
|  | 282 |  | 
|  | 283 | void HttpBaseTest::VerifySourceContents(const char* expected_data, | 
|  | 284 | size_t expected_length) { | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 285 | RTC_LOG_F(LS_VERBOSE) << "Enter"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 286 | if (SIZE_UNKNOWN == expected_length) { | 
|  | 287 | expected_length = strlen(expected_data); | 
|  | 288 | } | 
|  | 289 | std::string contents = src.ReadData(); | 
|  | 290 | EXPECT_EQ(expected_length, contents.length()); | 
|  | 291 | EXPECT_TRUE(0 == memcmp(expected_data, contents.data(), expected_length)); | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 292 | RTC_LOG_F(LS_VERBOSE) << "Exit"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 293 | } | 
|  | 294 |  | 
|  | 295 | void HttpBaseTest::VerifyTransferComplete(HttpMode mode, HttpError error) { | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 296 | RTC_LOG_F(LS_VERBOSE) << "Enter"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 297 | // Verify that http operation has completed | 
|  | 298 | ASSERT_TRUE(events.size() > 0); | 
|  | 299 | size_t last_event = events.size() - 1; | 
|  | 300 | EXPECT_EQ(E_COMPLETE, events[last_event].event); | 
|  | 301 | EXPECT_EQ(mode, events[last_event].mode); | 
|  | 302 | EXPECT_EQ(error, events[last_event].err); | 
| Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 303 | RTC_LOG_F(LS_VERBOSE) << "Exit"; | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 304 | } | 
|  | 305 |  | 
|  | 306 | // | 
|  | 307 | // Tests | 
|  | 308 | // | 
|  | 309 |  | 
|  | 310 | TEST_F(HttpBaseTest, SupportsSend) { | 
|  | 311 | // Queue response document | 
|  | 312 | SetupDocument("Goodbye!"); | 
|  | 313 |  | 
|  | 314 | // Begin send | 
|  | 315 | base.send(&data); | 
|  | 316 |  | 
|  | 317 | // Send completed successfully | 
|  | 318 | VerifyTransferComplete(HM_SEND, HE_NONE); | 
|  | 319 | VerifySourceContents(kHttpResponse); | 
|  | 320 | } | 
|  | 321 |  | 
|  | 322 | TEST_F(HttpBaseTest, SupportsSendNoDocument) { | 
|  | 323 | // Queue response document | 
|  | 324 | SetupDocument(nullptr); | 
|  | 325 |  | 
|  | 326 | // Begin send | 
|  | 327 | base.send(&data); | 
|  | 328 |  | 
|  | 329 | // Send completed successfully | 
|  | 330 | VerifyTransferComplete(HM_SEND, HE_NONE); | 
|  | 331 | VerifySourceContents(kHttpEmptyResponse); | 
|  | 332 | } | 
|  | 333 |  | 
|  | 334 | TEST_F(HttpBaseTest, SignalsCompleteOnInterruptedSend) { | 
|  | 335 | // This test is attempting to expose a bug that occurs when a particular | 
|  | 336 | // base objects is used for receiving, and then used for sending.  In | 
|  | 337 | // particular, the HttpParser state is different after receiving.  Simulate | 
|  | 338 | // that here. | 
|  | 339 | SetupSource(kHttpResponse); | 
|  | 340 | base.recv(&data); | 
|  | 341 | VerifyTransferComplete(HM_RECV, HE_NONE); | 
|  | 342 |  | 
|  | 343 | src.Clear(); | 
|  | 344 | data.clear(true); | 
|  | 345 | events.clear(); | 
|  | 346 | base.detach(); | 
|  | 347 |  | 
|  | 348 | // Queue response document | 
|  | 349 | SetupDocument("Goodbye!"); | 
|  | 350 |  | 
|  | 351 | // Prevent entire response from being sent | 
|  | 352 | const size_t kInterruptedLength = strlen(kHttpResponse) - 1; | 
|  | 353 | src.SetWriteBlock(kInterruptedLength); | 
|  | 354 |  | 
|  | 355 | // Begin send | 
|  | 356 | base.send(&data); | 
|  | 357 |  | 
|  | 358 | // Document is mostly complete, but no completion signal yet. | 
|  | 359 | EXPECT_TRUE(events.empty()); | 
|  | 360 | VerifySourceContents(kHttpResponse, kInterruptedLength); | 
|  | 361 |  | 
|  | 362 | src.SetState(SS_CLOSED); | 
|  | 363 |  | 
|  | 364 | // Send completed with disconnect error, and no additional data. | 
|  | 365 | VerifyTransferComplete(HM_SEND, HE_DISCONNECTED); | 
|  | 366 | EXPECT_TRUE(src.ReadData().empty()); | 
|  | 367 | } | 
|  | 368 |  | 
|  | 369 | TEST_F(HttpBaseTest, SupportsReceiveViaDocumentPush) { | 
|  | 370 | // Queue response document | 
|  | 371 | SetupSource(kHttpResponse); | 
|  | 372 |  | 
|  | 373 | // Begin receive | 
|  | 374 | base.recv(&data); | 
|  | 375 |  | 
|  | 376 | // Document completed successfully | 
|  | 377 | VerifyHeaderComplete(2, false); | 
|  | 378 | VerifyTransferComplete(HM_RECV, HE_NONE); | 
|  | 379 | VerifyDocumentContents("Goodbye!"); | 
|  | 380 | } | 
|  | 381 |  | 
|  | 382 | TEST_F(HttpBaseTest, SupportsReceiveViaStreamPull) { | 
|  | 383 | // Switch to pull mode | 
|  | 384 | ObtainDocumentStream(); | 
|  | 385 | VerifyDocumentStreamIsOpening(); | 
|  | 386 |  | 
|  | 387 | // Queue response document | 
|  | 388 | SetupSource(kHttpResponse); | 
|  | 389 | VerifyDocumentStreamIsOpening(); | 
|  | 390 |  | 
|  | 391 | // Begin receive | 
|  | 392 | base.recv(&data); | 
|  | 393 |  | 
|  | 394 | // Pull document data | 
|  | 395 | VerifyDocumentStreamOpenEvent(); | 
|  | 396 | ReadDocumentStreamData("Goodbye!"); | 
|  | 397 | VerifyDocumentStreamIsEOS(); | 
|  | 398 |  | 
|  | 399 | // Document completed successfully | 
|  | 400 | VerifyHeaderComplete(2, false); | 
|  | 401 | VerifyTransferComplete(HM_RECV, HE_NONE); | 
|  | 402 | VerifyDocumentContents(""); | 
|  | 403 | } | 
|  | 404 |  | 
|  | 405 | TEST_F(HttpBaseTest, DISABLED_AllowsCloseStreamBeforeDocumentIsComplete) { | 
|  | 406 |  | 
|  | 407 | // TODO: Remove extra logging once test failure is understood | 
|  | 408 | LoggingSeverity old_sev = rtc::LogMessage::GetLogToDebug(); | 
|  | 409 | rtc::LogMessage::LogToDebug(LS_VERBOSE); | 
|  | 410 |  | 
|  | 411 |  | 
|  | 412 | // Switch to pull mode | 
|  | 413 | ObtainDocumentStream(); | 
|  | 414 | VerifyDocumentStreamIsOpening(); | 
|  | 415 |  | 
|  | 416 | // Queue response document | 
|  | 417 | SetupSource(kHttpResponse); | 
|  | 418 | VerifyDocumentStreamIsOpening(); | 
|  | 419 |  | 
|  | 420 | // Begin receive | 
|  | 421 | base.recv(&data); | 
|  | 422 |  | 
|  | 423 | // Pull some of the data | 
|  | 424 | VerifyDocumentStreamOpenEvent(); | 
|  | 425 | ReadDocumentStreamData("Goodb"); | 
|  | 426 |  | 
|  | 427 | // We've seen the header by now | 
|  | 428 | VerifyHeaderComplete(1, false); | 
|  | 429 |  | 
|  | 430 | // Close the pull stream, this will transition back to push I/O. | 
|  | 431 | http_stream->Close(); | 
|  | 432 | Thread::Current()->ProcessMessages(0); | 
|  | 433 |  | 
|  | 434 | // Remainder of document completed successfully | 
|  | 435 | VerifyTransferComplete(HM_RECV, HE_NONE); | 
|  | 436 | VerifyDocumentContents("ye!"); | 
|  | 437 |  | 
|  | 438 | rtc::LogMessage::LogToDebug(old_sev); | 
|  | 439 | } | 
|  | 440 |  | 
|  | 441 | TEST_F(HttpBaseTest, AllowsGetDocumentStreamInResponseToHttpHeader) { | 
|  | 442 | // Queue response document | 
|  | 443 | SetupSource(kHttpResponse); | 
|  | 444 |  | 
|  | 445 | // Switch to pull mode in response to header arrival | 
|  | 446 | obtain_stream = true; | 
|  | 447 |  | 
|  | 448 | // Begin receive | 
|  | 449 | base.recv(&data); | 
|  | 450 |  | 
|  | 451 | // We've already seen the header, but not data has arrived | 
|  | 452 | VerifyHeaderComplete(1, false); | 
|  | 453 | VerifyDocumentContents(""); | 
|  | 454 |  | 
|  | 455 | // Pull the document data | 
|  | 456 | ReadDocumentStreamData("Goodbye!"); | 
|  | 457 | VerifyDocumentStreamIsEOS(); | 
|  | 458 |  | 
|  | 459 | // Document completed successfully | 
|  | 460 | VerifyTransferComplete(HM_RECV, HE_NONE); | 
|  | 461 | VerifyDocumentContents(""); | 
|  | 462 | } | 
|  | 463 |  | 
|  | 464 | TEST_F(HttpBaseTest, AllowsGetDocumentStreamWithEmptyDocumentBody) { | 
|  | 465 | // Queue empty response document | 
|  | 466 | SetupSource(kHttpEmptyResponse); | 
|  | 467 |  | 
|  | 468 | // Switch to pull mode in response to header arrival | 
|  | 469 | obtain_stream = true; | 
|  | 470 |  | 
|  | 471 | // Begin receive | 
|  | 472 | base.recv(&data); | 
|  | 473 |  | 
|  | 474 | // We've already seen the header, but not data has arrived | 
|  | 475 | VerifyHeaderComplete(1, true); | 
|  | 476 | VerifyDocumentContents(""); | 
|  | 477 |  | 
|  | 478 | // The document is still open, until we attempt to read | 
|  | 479 | ASSERT_TRUE(nullptr != http_stream); | 
|  | 480 | EXPECT_EQ(SS_OPEN, http_stream->GetState()); | 
|  | 481 |  | 
|  | 482 | // Attempt to read data, and discover EOS | 
|  | 483 | VerifyDocumentStreamIsEOS(); | 
|  | 484 |  | 
|  | 485 | // Document completed successfully | 
|  | 486 | VerifyTransferComplete(HM_RECV, HE_NONE); | 
|  | 487 | VerifyDocumentContents(""); | 
|  | 488 | } | 
|  | 489 |  | 
|  | 490 | TEST_F(HttpBaseTest, SignalsDocumentStreamCloseOnUnexpectedClose) { | 
|  | 491 | // Switch to pull mode | 
|  | 492 | ObtainDocumentStream(); | 
|  | 493 | VerifyDocumentStreamIsOpening(); | 
|  | 494 |  | 
|  | 495 | // Queue response document | 
|  | 496 | SetupSource(kHttpResponsePrefix); | 
|  | 497 | VerifyDocumentStreamIsOpening(); | 
|  | 498 |  | 
|  | 499 | // Begin receive | 
|  | 500 | base.recv(&data); | 
|  | 501 |  | 
|  | 502 | // Pull document data | 
|  | 503 | VerifyDocumentStreamOpenEvent(); | 
|  | 504 | ReadDocumentStreamData("Goodbye!"); | 
|  | 505 |  | 
|  | 506 | // Simulate unexpected close | 
|  | 507 | src.SetState(SS_CLOSED); | 
|  | 508 |  | 
|  | 509 | // Observe error event on document stream | 
| kwiberg | d0d8148 | 2017-04-18 03:18:22 -0700 | [diff] [blame] | 510 | EXPECT_EQ(webrtc::testing::SSE_ERROR, sink.Events(http_stream)); | 
| deadbeef | f137e97 | 2017-03-23 15:45:49 -0700 | [diff] [blame] | 511 |  | 
|  | 512 | // Future reads give an error | 
|  | 513 | int error = 0; | 
|  | 514 | char buffer[5] = { 0 }; | 
|  | 515 | EXPECT_EQ(SR_ERROR, | 
|  | 516 | http_stream->Read(buffer, sizeof(buffer), nullptr, &error)); | 
|  | 517 | EXPECT_EQ(HE_DISCONNECTED, error); | 
|  | 518 |  | 
|  | 519 | // Document completed with error | 
|  | 520 | VerifyHeaderComplete(2, false); | 
|  | 521 | VerifyTransferComplete(HM_RECV, HE_DISCONNECTED); | 
|  | 522 | VerifyDocumentContents(""); | 
|  | 523 | } | 
|  | 524 |  | 
|  | 525 | } // namespace rtc |