Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(292)

Unified Diff: webrtc/base/httpbase_unittest.cc

Issue 2766063005: Revert of Removing HTTPS and SOCKS proxy server code. (Closed)
Patch Set: Merge with master Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « webrtc/base/httpbase.cc ('k') | webrtc/base/httpcommon.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: webrtc/base/httpbase_unittest.cc
diff --git a/webrtc/base/httpbase_unittest.cc b/webrtc/base/httpbase_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..52c1c53fb101682e01e8bc332698a25b5edf2677
--- /dev/null
+++ b/webrtc/base/httpbase_unittest.cc
@@ -0,0 +1,526 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <algorithm>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/httpbase.h"
+#include "webrtc/base/testutils.h"
+
+namespace rtc {
+
+const char* const kHttpResponse =
+ "HTTP/1.1 200\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Content-Type: text/plain\r\n"
+ "Proxy-Authorization: 42\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "\r\n"
+ "00000008\r\n"
+ "Goodbye!\r\n"
+ "0\r\n\r\n";
+
+const char* const kHttpEmptyResponse =
+ "HTTP/1.1 200\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Content-Length: 0\r\n"
+ "Proxy-Authorization: 42\r\n"
+ "\r\n";
+
+const char* const kHttpResponsePrefix =
+ "HTTP/1.1 200\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Content-Type: text/plain\r\n"
+ "Proxy-Authorization: 42\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "\r\n"
+ "8\r\n"
+ "Goodbye!\r\n";
+
+class HttpBaseTest : public testing::Test, public IHttpNotify {
+public:
+ enum EventType { E_HEADER_COMPLETE, E_COMPLETE, E_CLOSED };
+ struct Event {
+ EventType event;
+ bool chunked;
+ size_t data_size;
+ HttpMode mode;
+ HttpError err;
+ };
+ HttpBaseTest() : mem(nullptr), obtain_stream(false), http_stream(nullptr) {}
+
+ virtual void SetUp() { }
+ virtual void TearDown() {
+ delete http_stream;
+ // Avoid an ASSERT, in case a test doesn't clean up properly
+ base.abort(HE_NONE);
+ }
+
+ virtual HttpError onHttpHeaderComplete(bool chunked, size_t& data_size) {
+ LOG_F(LS_VERBOSE) << "chunked: " << chunked << " size: " << data_size;
+ Event e = { E_HEADER_COMPLETE, chunked, data_size, HM_NONE, HE_NONE};
+ events.push_back(e);
+ if (obtain_stream) {
+ ObtainDocumentStream();
+ }
+ return HE_NONE;
+ }
+ virtual void onHttpComplete(HttpMode mode, HttpError err) {
+ LOG_F(LS_VERBOSE) << "mode: " << mode << " err: " << err;
+ Event e = { E_COMPLETE, false, 0, mode, err };
+ events.push_back(e);
+ }
+ virtual void onHttpClosed(HttpError err) {
+ LOG_F(LS_VERBOSE) << "err: " << err;
+ Event e = { E_CLOSED, false, 0, HM_NONE, err };
+ events.push_back(e);
+ }
+
+ void SetupSource(const char* response);
+
+ void VerifyHeaderComplete(size_t event_count, bool empty_doc);
+ void VerifyDocumentContents(const char* expected_data,
+ size_t expected_length = SIZE_UNKNOWN);
+
+ void ObtainDocumentStream();
+ void VerifyDocumentStreamIsOpening();
+ void VerifyDocumentStreamOpenEvent();
+ void ReadDocumentStreamData(const char* expected_data);
+ void VerifyDocumentStreamIsEOS();
+
+ void SetupDocument(const char* response);
+ void VerifySourceContents(const char* expected_data,
+ size_t expected_length = SIZE_UNKNOWN);
+
+ void VerifyTransferComplete(HttpMode mode, HttpError error);
+
+ HttpBase base;
+ MemoryStream* mem;
+ HttpResponseData data;
+
+ // The source of http data, and source events
+ testing::StreamSource src;
+ std::vector<Event> events;
+
+ // Document stream, and stream events
+ bool obtain_stream;
+ StreamInterface* http_stream;
+ testing::StreamSink sink;
+};
+
+void HttpBaseTest::SetupSource(const char* http_data) {
+ LOG_F(LS_VERBOSE) << "Enter";
+
+ src.SetState(SS_OPENING);
+ src.QueueString(http_data);
+
+ base.notify(this);
+ base.attach(&src);
+ EXPECT_TRUE(events.empty());
+
+ src.SetState(SS_OPEN);
+ ASSERT_EQ(1U, events.size());
+ EXPECT_EQ(E_COMPLETE, events[0].event);
+ EXPECT_EQ(HM_CONNECT, events[0].mode);
+ EXPECT_EQ(HE_NONE, events[0].err);
+ events.clear();
+
+ mem = new MemoryStream;
+ data.document.reset(mem);
+ LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::VerifyHeaderComplete(size_t event_count, bool empty_doc) {
+ LOG_F(LS_VERBOSE) << "Enter";
+
+ ASSERT_EQ(event_count, events.size());
+ EXPECT_EQ(E_HEADER_COMPLETE, events[0].event);
+
+ std::string header;
+ EXPECT_EQ(HVER_1_1, data.version);
+ EXPECT_EQ(static_cast<uint32_t>(HC_OK), data.scode);
+ EXPECT_TRUE(data.hasHeader(HH_PROXY_AUTHORIZATION, &header));
+ EXPECT_EQ("42", header);
+ EXPECT_TRUE(data.hasHeader(HH_CONNECTION, &header));
+ EXPECT_EQ("Keep-Alive", header);
+
+ if (empty_doc) {
+ EXPECT_FALSE(events[0].chunked);
+ EXPECT_EQ(0U, events[0].data_size);
+
+ EXPECT_TRUE(data.hasHeader(HH_CONTENT_LENGTH, &header));
+ EXPECT_EQ("0", header);
+ } else {
+ EXPECT_TRUE(events[0].chunked);
+ EXPECT_EQ(SIZE_UNKNOWN, events[0].data_size);
+
+ EXPECT_TRUE(data.hasHeader(HH_CONTENT_TYPE, &header));
+ EXPECT_EQ("text/plain", header);
+ EXPECT_TRUE(data.hasHeader(HH_TRANSFER_ENCODING, &header));
+ EXPECT_EQ("chunked", header);
+ }
+ LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::VerifyDocumentContents(const char* expected_data,
+ size_t expected_length) {
+ LOG_F(LS_VERBOSE) << "Enter";
+
+ if (SIZE_UNKNOWN == expected_length) {
+ expected_length = strlen(expected_data);
+ }
+ EXPECT_EQ(mem, data.document.get());
+
+ size_t length;
+ mem->GetSize(&length);
+ EXPECT_EQ(expected_length, length);
+ EXPECT_TRUE(0 == memcmp(expected_data, mem->GetBuffer(), length));
+ LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::ObtainDocumentStream() {
+ LOG_F(LS_VERBOSE) << "Enter";
+ EXPECT_FALSE(http_stream);
+ http_stream = base.GetDocumentStream();
+ ASSERT_TRUE(nullptr != http_stream);
+ sink.Monitor(http_stream);
+ LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::VerifyDocumentStreamIsOpening() {
+ LOG_F(LS_VERBOSE) << "Enter";
+ ASSERT_TRUE(nullptr != http_stream);
+ EXPECT_EQ(0, sink.Events(http_stream));
+ EXPECT_EQ(SS_OPENING, http_stream->GetState());
+
+ size_t read = 0;
+ char buffer[5] = { 0 };
+ EXPECT_EQ(SR_BLOCK,
+ http_stream->Read(buffer, sizeof(buffer), &read, nullptr));
+ LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::VerifyDocumentStreamOpenEvent() {
+ LOG_F(LS_VERBOSE) << "Enter";
+
+ ASSERT_TRUE(nullptr != http_stream);
+ EXPECT_EQ(SE_OPEN | SE_READ, sink.Events(http_stream));
+ EXPECT_EQ(SS_OPEN, http_stream->GetState());
+
+ // HTTP headers haven't arrived yet
+ EXPECT_EQ(0U, events.size());
+ EXPECT_EQ(static_cast<uint32_t>(HC_INTERNAL_SERVER_ERROR), data.scode);
+ LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::ReadDocumentStreamData(const char* expected_data) {
+ LOG_F(LS_VERBOSE) << "Enter";
+
+ ASSERT_TRUE(nullptr != http_stream);
+ EXPECT_EQ(SS_OPEN, http_stream->GetState());
+
+ // Pump the HTTP I/O using Read, and verify the results.
+ size_t verified_length = 0;
+ const size_t expected_length = strlen(expected_data);
+ while (verified_length < expected_length) {
+ size_t read = 0;
+ char buffer[5] = { 0 };
+ size_t amt_to_read =
+ std::min(expected_length - verified_length, sizeof(buffer));
+ EXPECT_EQ(SR_SUCCESS,
+ http_stream->Read(buffer, amt_to_read, &read, nullptr));
+ EXPECT_EQ(amt_to_read, read);
+ EXPECT_TRUE(0 == memcmp(expected_data + verified_length, buffer, read));
+ verified_length += read;
+ }
+ LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::VerifyDocumentStreamIsEOS() {
+ LOG_F(LS_VERBOSE) << "Enter";
+
+ ASSERT_TRUE(nullptr != http_stream);
+ size_t read = 0;
+ char buffer[5] = { 0 };
+ EXPECT_EQ(SR_EOS, http_stream->Read(buffer, sizeof(buffer), &read, nullptr));
+ EXPECT_EQ(SS_CLOSED, http_stream->GetState());
+
+ // When EOS is caused by Read, we don't expect SE_CLOSE
+ EXPECT_EQ(0, sink.Events(http_stream));
+ LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::SetupDocument(const char* document_data) {
+ LOG_F(LS_VERBOSE) << "Enter";
+ src.SetState(SS_OPEN);
+
+ base.notify(this);
+ base.attach(&src);
+ EXPECT_TRUE(events.empty());
+
+ if (document_data) {
+ // Note: we could just call data.set_success("text/plain", mem), but that
+ // won't allow us to use the chunked transfer encoding.
+ mem = new MemoryStream(document_data);
+ data.document.reset(mem);
+ data.setHeader(HH_CONTENT_TYPE, "text/plain");
+ data.setHeader(HH_TRANSFER_ENCODING, "chunked");
+ } else {
+ data.setHeader(HH_CONTENT_LENGTH, "0");
+ }
+ data.scode = HC_OK;
+ data.setHeader(HH_PROXY_AUTHORIZATION, "42");
+ data.setHeader(HH_CONNECTION, "Keep-Alive");
+ LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::VerifySourceContents(const char* expected_data,
+ size_t expected_length) {
+ LOG_F(LS_VERBOSE) << "Enter";
+ if (SIZE_UNKNOWN == expected_length) {
+ expected_length = strlen(expected_data);
+ }
+ std::string contents = src.ReadData();
+ EXPECT_EQ(expected_length, contents.length());
+ EXPECT_TRUE(0 == memcmp(expected_data, contents.data(), expected_length));
+ LOG_F(LS_VERBOSE) << "Exit";
+}
+
+void HttpBaseTest::VerifyTransferComplete(HttpMode mode, HttpError error) {
+ LOG_F(LS_VERBOSE) << "Enter";
+ // Verify that http operation has completed
+ ASSERT_TRUE(events.size() > 0);
+ size_t last_event = events.size() - 1;
+ EXPECT_EQ(E_COMPLETE, events[last_event].event);
+ EXPECT_EQ(mode, events[last_event].mode);
+ EXPECT_EQ(error, events[last_event].err);
+ LOG_F(LS_VERBOSE) << "Exit";
+}
+
+//
+// Tests
+//
+
+TEST_F(HttpBaseTest, SupportsSend) {
+ // Queue response document
+ SetupDocument("Goodbye!");
+
+ // Begin send
+ base.send(&data);
+
+ // Send completed successfully
+ VerifyTransferComplete(HM_SEND, HE_NONE);
+ VerifySourceContents(kHttpResponse);
+}
+
+TEST_F(HttpBaseTest, SupportsSendNoDocument) {
+ // Queue response document
+ SetupDocument(nullptr);
+
+ // Begin send
+ base.send(&data);
+
+ // Send completed successfully
+ VerifyTransferComplete(HM_SEND, HE_NONE);
+ VerifySourceContents(kHttpEmptyResponse);
+}
+
+TEST_F(HttpBaseTest, SignalsCompleteOnInterruptedSend) {
+ // This test is attempting to expose a bug that occurs when a particular
+ // base objects is used for receiving, and then used for sending. In
+ // particular, the HttpParser state is different after receiving. Simulate
+ // that here.
+ SetupSource(kHttpResponse);
+ base.recv(&data);
+ VerifyTransferComplete(HM_RECV, HE_NONE);
+
+ src.Clear();
+ data.clear(true);
+ events.clear();
+ base.detach();
+
+ // Queue response document
+ SetupDocument("Goodbye!");
+
+ // Prevent entire response from being sent
+ const size_t kInterruptedLength = strlen(kHttpResponse) - 1;
+ src.SetWriteBlock(kInterruptedLength);
+
+ // Begin send
+ base.send(&data);
+
+ // Document is mostly complete, but no completion signal yet.
+ EXPECT_TRUE(events.empty());
+ VerifySourceContents(kHttpResponse, kInterruptedLength);
+
+ src.SetState(SS_CLOSED);
+
+ // Send completed with disconnect error, and no additional data.
+ VerifyTransferComplete(HM_SEND, HE_DISCONNECTED);
+ EXPECT_TRUE(src.ReadData().empty());
+}
+
+TEST_F(HttpBaseTest, SupportsReceiveViaDocumentPush) {
+ // Queue response document
+ SetupSource(kHttpResponse);
+
+ // Begin receive
+ base.recv(&data);
+
+ // Document completed successfully
+ VerifyHeaderComplete(2, false);
+ VerifyTransferComplete(HM_RECV, HE_NONE);
+ VerifyDocumentContents("Goodbye!");
+}
+
+TEST_F(HttpBaseTest, SupportsReceiveViaStreamPull) {
+ // Switch to pull mode
+ ObtainDocumentStream();
+ VerifyDocumentStreamIsOpening();
+
+ // Queue response document
+ SetupSource(kHttpResponse);
+ VerifyDocumentStreamIsOpening();
+
+ // Begin receive
+ base.recv(&data);
+
+ // Pull document data
+ VerifyDocumentStreamOpenEvent();
+ ReadDocumentStreamData("Goodbye!");
+ VerifyDocumentStreamIsEOS();
+
+ // Document completed successfully
+ VerifyHeaderComplete(2, false);
+ VerifyTransferComplete(HM_RECV, HE_NONE);
+ VerifyDocumentContents("");
+}
+
+TEST_F(HttpBaseTest, DISABLED_AllowsCloseStreamBeforeDocumentIsComplete) {
+
+ // TODO: Remove extra logging once test failure is understood
+ LoggingSeverity old_sev = rtc::LogMessage::GetLogToDebug();
+ rtc::LogMessage::LogToDebug(LS_VERBOSE);
+
+
+ // Switch to pull mode
+ ObtainDocumentStream();
+ VerifyDocumentStreamIsOpening();
+
+ // Queue response document
+ SetupSource(kHttpResponse);
+ VerifyDocumentStreamIsOpening();
+
+ // Begin receive
+ base.recv(&data);
+
+ // Pull some of the data
+ VerifyDocumentStreamOpenEvent();
+ ReadDocumentStreamData("Goodb");
+
+ // We've seen the header by now
+ VerifyHeaderComplete(1, false);
+
+ // Close the pull stream, this will transition back to push I/O.
+ http_stream->Close();
+ Thread::Current()->ProcessMessages(0);
+
+ // Remainder of document completed successfully
+ VerifyTransferComplete(HM_RECV, HE_NONE);
+ VerifyDocumentContents("ye!");
+
+ rtc::LogMessage::LogToDebug(old_sev);
+}
+
+TEST_F(HttpBaseTest, AllowsGetDocumentStreamInResponseToHttpHeader) {
+ // Queue response document
+ SetupSource(kHttpResponse);
+
+ // Switch to pull mode in response to header arrival
+ obtain_stream = true;
+
+ // Begin receive
+ base.recv(&data);
+
+ // We've already seen the header, but not data has arrived
+ VerifyHeaderComplete(1, false);
+ VerifyDocumentContents("");
+
+ // Pull the document data
+ ReadDocumentStreamData("Goodbye!");
+ VerifyDocumentStreamIsEOS();
+
+ // Document completed successfully
+ VerifyTransferComplete(HM_RECV, HE_NONE);
+ VerifyDocumentContents("");
+}
+
+TEST_F(HttpBaseTest, AllowsGetDocumentStreamWithEmptyDocumentBody) {
+ // Queue empty response document
+ SetupSource(kHttpEmptyResponse);
+
+ // Switch to pull mode in response to header arrival
+ obtain_stream = true;
+
+ // Begin receive
+ base.recv(&data);
+
+ // We've already seen the header, but not data has arrived
+ VerifyHeaderComplete(1, true);
+ VerifyDocumentContents("");
+
+ // The document is still open, until we attempt to read
+ ASSERT_TRUE(nullptr != http_stream);
+ EXPECT_EQ(SS_OPEN, http_stream->GetState());
+
+ // Attempt to read data, and discover EOS
+ VerifyDocumentStreamIsEOS();
+
+ // Document completed successfully
+ VerifyTransferComplete(HM_RECV, HE_NONE);
+ VerifyDocumentContents("");
+}
+
+TEST_F(HttpBaseTest, SignalsDocumentStreamCloseOnUnexpectedClose) {
+ // Switch to pull mode
+ ObtainDocumentStream();
+ VerifyDocumentStreamIsOpening();
+
+ // Queue response document
+ SetupSource(kHttpResponsePrefix);
+ VerifyDocumentStreamIsOpening();
+
+ // Begin receive
+ base.recv(&data);
+
+ // Pull document data
+ VerifyDocumentStreamOpenEvent();
+ ReadDocumentStreamData("Goodbye!");
+
+ // Simulate unexpected close
+ src.SetState(SS_CLOSED);
+
+ // Observe error event on document stream
+ EXPECT_EQ(testing::SSE_ERROR, sink.Events(http_stream));
+
+ // Future reads give an error
+ int error = 0;
+ char buffer[5] = { 0 };
+ EXPECT_EQ(SR_ERROR,
+ http_stream->Read(buffer, sizeof(buffer), nullptr, &error));
+ EXPECT_EQ(HE_DISCONNECTED, error);
+
+ // Document completed with error
+ VerifyHeaderComplete(2, false);
+ VerifyTransferComplete(HM_RECV, HE_DISCONNECTED);
+ VerifyDocumentContents("");
+}
+
+} // namespace rtc
« no previous file with comments | « webrtc/base/httpbase.cc ('k') | webrtc/base/httpcommon.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698