summaryrefslogtreecommitdiffstats
path: root/media/mtransport/test/proxy_tunnel_socket_unittest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/mtransport/test/proxy_tunnel_socket_unittest.cpp')
-rw-r--r--media/mtransport/test/proxy_tunnel_socket_unittest.cpp344
1 files changed, 344 insertions, 0 deletions
diff --git a/media/mtransport/test/proxy_tunnel_socket_unittest.cpp b/media/mtransport/test/proxy_tunnel_socket_unittest.cpp
new file mode 100644
index 000000000..facd0c04c
--- /dev/null
+++ b/media/mtransport/test/proxy_tunnel_socket_unittest.cpp
@@ -0,0 +1,344 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Original authors: ekr@rtfm.com; ryan@tokbox.com
+
+#include <iostream>
+
+#include "nspr.h"
+#include "nss.h"
+#include "ssl.h"
+
+extern "C" {
+#include "nr_api.h"
+#include "nr_socket.h"
+#include "nr_proxy_tunnel.h"
+#include "transport_addr.h"
+#include "stun.h"
+}
+
+#include "databuffer.h"
+#include "dummysocket.h"
+
+#include "nr_socket_prsock.h"
+#include "nriceresolverfake.h"
+
+#define GTEST_HAS_RTTI 0
+#include "gtest/gtest.h"
+#include "gtest_utils.h"
+
+using namespace mozilla;
+
+const std::string kRemoteAddr = "192.0.2.133";
+const uint16_t kRemotePort = 3333;
+
+const std::string kProxyHost = "example.com";
+const std::string kProxyAddr = "192.0.2.134";
+const uint16_t kProxyPort = 9999;
+
+const std::string kHelloMessage = "HELLO";
+const std::string kGarbageMessage = "xxxxxxxxxx";
+
+std::string connect_message(const std::string &host, uint16_t port, const std::string &alpn, const std::string &tail) {
+ std::stringstream ss;
+ ss << "CONNECT " << host << ":" << port << " HTTP/1.0\r\n";
+ if (!alpn.empty()) {
+ ss << "ALPN: " << alpn << "\r\n";
+ }
+ ss << "\r\n" << tail;
+ return ss.str();
+}
+
+std::string connect_response(int code, const std::string &tail = "") {
+ std::stringstream ss;
+ ss << "HTTP/1.0 " << code << "\r\n\r\n" << tail;
+ return ss.str();
+}
+
+class DummyResolver {
+ public:
+ DummyResolver() {
+ vtbl_ = new nr_resolver_vtbl;
+ vtbl_->destroy = &DummyResolver::destroy;
+ vtbl_->resolve = &DummyResolver::resolve;
+ vtbl_->cancel = &DummyResolver::cancel;
+ nr_resolver_create_int((void *)this, vtbl_, &resolver_);
+ }
+
+ ~DummyResolver() {
+ nr_resolver_destroy(&resolver_);
+ delete vtbl_;
+ }
+
+ static int destroy(void **objp) {
+ return 0;
+ }
+
+ static int resolve(void *obj,
+ nr_resolver_resource *resource,
+ int (*cb)(void *cb_arg, nr_transport_addr *addr),
+ void *cb_arg,
+ void **handle) {
+ nr_transport_addr addr;
+
+ nr_str_port_to_transport_addr(
+ (char *)kProxyAddr.c_str(), kProxyPort, IPPROTO_TCP, &addr);
+
+ cb(cb_arg, &addr);
+ return 0;
+ }
+
+ static int cancel(void *obj, void *handle) {
+ return 0;
+ }
+
+ nr_resolver *get_nr_resolver() {
+ return resolver_;
+ }
+
+ private:
+ nr_resolver_vtbl *vtbl_;
+ nr_resolver *resolver_;
+};
+
+class ProxyTunnelSocketTest : public MtransportTest {
+ public:
+ ProxyTunnelSocketTest()
+ : socket_impl_(nullptr),
+ nr_socket_(nullptr) {}
+
+ ~ProxyTunnelSocketTest() {
+ nr_socket_destroy(&nr_socket_);
+ nr_proxy_tunnel_config_destroy(&config_);
+ }
+
+ void SetUp() override {
+ MtransportTest::SetUp();
+
+ nr_resolver_ = resolver_impl_.get_nr_resolver();
+
+ int r = nr_str_port_to_transport_addr(
+ (char *)kRemoteAddr.c_str(), kRemotePort, IPPROTO_TCP, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ r = nr_str_port_to_transport_addr(
+ (char *)kProxyAddr.c_str(), kProxyPort, IPPROTO_TCP, &proxy_addr_);
+ ASSERT_EQ(0, r);
+
+ nr_proxy_tunnel_config_create(&config_);
+ nr_proxy_tunnel_config_set_resolver(config_, nr_resolver_);
+ nr_proxy_tunnel_config_set_proxy(config_, kProxyAddr.c_str(), kProxyPort);
+
+ Configure();
+ }
+
+ // This reconfigures the socket with the updated information in config_.
+ void Configure() {
+ if (nr_socket_) {
+ EXPECT_EQ(0, nr_socket_destroy(&nr_socket_));
+ EXPECT_EQ(nullptr, nr_socket_);
+ }
+
+ RefPtr<DummySocket> dummy(new DummySocket());
+ int r = nr_socket_proxy_tunnel_create(
+ config_,
+ dummy->get_nr_socket(),
+ &nr_socket_);
+ ASSERT_EQ(0, r);
+
+ socket_impl_ = dummy.forget(); // Now owned by nr_socket_.
+ }
+
+ void Connect(int expectedReturn = 0) {
+ int r = nr_socket_connect(nr_socket_, &remote_addr_);
+ EXPECT_EQ(expectedReturn, r);
+
+ size_t written = 0;
+ r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(kHelloMessage.size(), written);
+ }
+
+ nr_socket *socket() { return nr_socket_; }
+
+ protected:
+ RefPtr<DummySocket> socket_impl_;
+ DummyResolver resolver_impl_;
+ nr_socket *nr_socket_;
+ nr_resolver *nr_resolver_;
+ nr_proxy_tunnel_config *config_;
+ nr_transport_addr proxy_addr_;
+ nr_transport_addr remote_addr_;
+};
+
+
+TEST_F(ProxyTunnelSocketTest, TestCreate) {
+}
+
+TEST_F(ProxyTunnelSocketTest, TestConnectProxyAddress) {
+ int r = nr_socket_connect(nr_socket_, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ ASSERT_EQ(0, nr_transport_addr_cmp(socket_impl_->get_connect_addr(), &proxy_addr_,
+ NR_TRANSPORT_ADDR_CMP_MODE_ALL));
+}
+
+// TODO: Reenable once bug 1251212 is fixed
+TEST_F(ProxyTunnelSocketTest, DISABLED_TestConnectProxyRequest) {
+ Connect();
+
+ std::string msg = connect_message(kRemoteAddr, kRemotePort, "", kHelloMessage);
+ socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
+}
+
+// TODO: Reenable once bug 1251212 is fixed
+TEST_F(ProxyTunnelSocketTest, DISABLED_TestAlpnConnect) {
+ const std::string alpn = "this,is,alpn";
+ int r = nr_proxy_tunnel_config_set_alpn(config_, alpn.c_str());
+ EXPECT_EQ(0, r);
+
+ Configure();
+ Connect();
+
+ std::string msg = connect_message(kRemoteAddr, kRemotePort, alpn, kHelloMessage);
+ socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
+}
+
+// TODO: Reenable once bug 1251212 is fixed
+TEST_F(ProxyTunnelSocketTest, DISABLED_TestNullAlpnConnect) {
+ int r = nr_proxy_tunnel_config_set_alpn(config_, nullptr);
+ EXPECT_EQ(0, r);
+
+ Configure();
+ Connect();
+
+ std::string msg = connect_message(kRemoteAddr, kRemotePort, "", kHelloMessage);
+ socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
+}
+
+// TODO: Reenable once bug 1251212 is fixed
+TEST_F(ProxyTunnelSocketTest, DISABLED_TestConnectProxyHostRequest) {
+ nr_proxy_tunnel_config_set_proxy(config_, kProxyHost.c_str(), kProxyPort);
+ Configure();
+ // Because kProxyHost is a domain name and not an IP address,
+ // nr_socket_connect will need to resolve an IP address before continuing. It
+ // does that, and assumes that resolving the IP will take some time, so it
+ // returns R_WOULDBLOCK.
+ //
+ // However, In this test setup, the resolution happens inline immediately, so
+ // nr_socket_connect is called recursively on the inner socket in
+ // nr_socket_proxy_tunnel_resolved_cb. That also completes. Thus, the socket
+ // is actually successfully connected after this call, even though
+ // nr_socket_connect reports an error.
+ //
+ // Arguably nr_socket_proxy_tunnel_connect() is busted, because it shouldn't
+ // report an error when it doesn't need any further assistance from the
+ // calling code, but that's pretty minor.
+ Connect(R_WOULDBLOCK);
+
+ std::string msg = connect_message(kRemoteAddr, kRemotePort, "", kHelloMessage);
+ socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(msg.c_str()), msg.size());
+}
+
+// TODO: Reenable once bug 1251212 is fixed
+TEST_F(ProxyTunnelSocketTest, DISABLED_TestConnectProxyWrite) {
+ Connect();
+
+ socket_impl_->ClearWriteBuffer();
+
+ size_t written = 0;
+ int r = nr_socket_write(nr_socket_, kHelloMessage.c_str(), kHelloMessage.size(), &written, 0);
+ EXPECT_EQ(0, r);
+ EXPECT_EQ(kHelloMessage.size(), written);
+
+ socket_impl_->CheckWriteBuffer(reinterpret_cast<const uint8_t *>(kHelloMessage.c_str()),
+ kHelloMessage.size());
+}
+
+TEST_F(ProxyTunnelSocketTest, TestConnectProxySuccessResponse) {
+ int r = nr_socket_connect(nr_socket_, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ std::string resp = connect_response(200, kHelloMessage);
+ socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(resp.c_str()), resp.size());
+
+ char buf[4096];
+ size_t read = 0;
+ r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+ ASSERT_EQ(0, r);
+
+ ASSERT_EQ(kHelloMessage.size(), read);
+ ASSERT_EQ(0, memcmp(buf, kHelloMessage.c_str(), kHelloMessage.size()));
+}
+
+TEST_F(ProxyTunnelSocketTest, TestConnectProxyRead) {
+ int r = nr_socket_connect(nr_socket_, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ std::string resp = connect_response(200, kHelloMessage);
+ socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(resp.c_str()), resp.size());
+
+ char buf[4096];
+ size_t read = 0;
+ r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+ ASSERT_EQ(0, r);
+
+ socket_impl_->ClearReadBuffer();
+ socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(kHelloMessage.c_str()),
+ kHelloMessage.size());
+
+ r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+ ASSERT_EQ(0, r);
+
+ ASSERT_EQ(kHelloMessage.size(), read);
+ ASSERT_EQ(0, memcmp(buf, kHelloMessage.c_str(), kHelloMessage.size()));
+}
+
+TEST_F(ProxyTunnelSocketTest, TestConnectProxyReadNone) {
+ int r = nr_socket_connect(nr_socket_, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ std::string resp = connect_response(200);
+ socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(resp.c_str()), resp.size());
+
+ char buf[4096];
+ size_t read = 0;
+ r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+ ASSERT_EQ(R_WOULDBLOCK, r);
+
+ socket_impl_->ClearReadBuffer();
+ socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(kHelloMessage.c_str()),
+ kHelloMessage.size());
+
+ r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+ ASSERT_EQ(0, r);
+}
+
+TEST_F(ProxyTunnelSocketTest, TestConnectProxyFailResponse) {
+ int r = nr_socket_connect(nr_socket_, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ std::string resp = connect_response(500, kHelloMessage);
+ socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(resp.c_str()), resp.size());
+
+ char buf[4096];
+ size_t read = 0;
+ r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+ ASSERT_NE(0, r);
+}
+
+TEST_F(ProxyTunnelSocketTest, TestConnectProxyGarbageResponse) {
+ int r = nr_socket_connect(nr_socket_, &remote_addr_);
+ ASSERT_EQ(0, r);
+
+ socket_impl_->SetReadBuffer(reinterpret_cast<const uint8_t *>(kGarbageMessage.c_str()),
+ kGarbageMessage.size());
+
+ char buf[4096];
+ size_t read = 0;
+ r = nr_socket_read(nr_socket_, buf, sizeof(buf), &read, 0);
+ ASSERT_EQ(0ul, read);
+}