system-proxy: Accept CONNECT requests
This CL enables workers to process proxy CONNECT requests.
It extracts the url from the incoming request and sets up a
connection to the remote proxy using curl for
authentication.
BUG=chromium:1042626
TEST=unittests
Change-Id: Iebbed5a8229f17aa0f13fb2c7413e084bf276051
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2106136
Reviewed-by: Pavol Marko <pmarko@chromium.org>
Tested-by: Andreea-Elena Costinas <acostinas@google.com>
Commit-Queue: Andreea-Elena Costinas <acostinas@google.com>
diff --git a/system-proxy/server_proxy_test.cc b/system-proxy/server_proxy_test.cc
index efbdbc0..4f079cb 100644
--- a/system-proxy/server_proxy_test.cc
+++ b/system-proxy/server_proxy_test.cc
@@ -5,31 +5,40 @@
#include "system-proxy/server_proxy.h"
#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utility>
+#include <arc/network/socket.h>
+#include <arc/network/socket_forwarder.h>
#include <base/bind.h>
#include <base/bind_helpers.h>
#include <base/callback_helpers.h>
#include <base/files/file_util.h>
#include <base/files/scoped_file.h>
#include <base/message_loop/message_loop.h>
+#include <base/strings/string_util.h>
#include <brillo/dbus/async_event_sequencer.h>
-#include <brillo/dbus/dbus_object.h>
#include <brillo/message_loops/base_message_loop.h>
#include "bindings/worker_common.pb.h"
#include "system-proxy/protobuf_util.h"
+#include "system-proxy/proxy_connect_job.h"
namespace system_proxy {
namespace {
-constexpr char kUser[] = "proxy_user";
-constexpr char kPassword[] = "proxy_password";
+constexpr char kUsername[] = "proxy:user";
+constexpr char kUsernameEncoded[] = "proxy%3Auser";
+constexpr char kPassword[] = "proxy password";
+constexpr char kPasswordEncoded[] = "proxy%20password";
constexpr int kTestPort = 3128;
+
} // namespace
+using ::testing::_;
using ::testing::Return;
class MockServerProxy : public ServerProxy {
@@ -43,6 +52,23 @@
MOCK_METHOD(int, GetStdinPipe, (), (override));
};
+class MockProxyConnectJob : public ProxyConnectJob {
+ public:
+ MockProxyConnectJob(std::unique_ptr<arc_networkd::Socket> socket,
+ const std::string& credentials,
+ ResolveProxyCallback resolve_proxy_callback,
+ OnConnectionSetupFinishedCallback setup_finished_callback)
+ : ProxyConnectJob(std::move(socket),
+ credentials,
+ std::move(resolve_proxy_callback),
+ std::move(setup_finished_callback)) {}
+ MockProxyConnectJob(const MockProxyConnectJob&) = delete;
+ MockProxyConnectJob& operator=(const MockProxyConnectJob&) = delete;
+ ~MockProxyConnectJob() override = default;
+
+ MOCK_METHOD(bool, Start, (), (override));
+};
+
class ServerProxyTest : public ::testing::Test {
public:
ServerProxyTest() {
@@ -74,7 +100,7 @@
TEST_F(ServerProxyTest, FetchCredentials) {
Credentials credentials;
- credentials.set_username(kUser);
+ credentials.set_username(kUsername);
credentials.set_password(kPassword);
WorkerConfigs configs;
*configs.mutable_credentials() = credentials;
@@ -84,8 +110,9 @@
brillo_loop_.RunOnce(false);
- EXPECT_EQ(server_proxy_->username_, kUser);
- EXPECT_EQ(server_proxy_->password_, kPassword);
+ std::string expected_credentials =
+ base::JoinString({kUsernameEncoded, kPasswordEncoded}, ":");
+ EXPECT_EQ(server_proxy_->credentials_, expected_credentials);
}
TEST_F(ServerProxyTest, FetchListeningAddress) {
@@ -104,4 +131,76 @@
EXPECT_EQ(server_proxy_->listening_port_, kTestPort);
}
+TEST_F(ServerProxyTest, HandleConnectRequest) {
+ server_proxy_->listening_addr_ = htonl(INADDR_LOOPBACK);
+ server_proxy_->listening_port_ = kTestPort;
+ // Redirect the worker stdin and stdout pipes.
+ RedirectStdPipes();
+ server_proxy_->CreateListeningSocket();
+
+ CHECK_NE(-1, server_proxy_->listening_fd_->fd());
+ brillo_loop_.RunOnce(false);
+
+ struct sockaddr_in ipv4addr;
+ ipv4addr.sin_family = AF_INET;
+ ipv4addr.sin_port = htons(kTestPort);
+ ipv4addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ auto client_socket =
+ std::make_unique<arc_networkd::Socket>(AF_INET, SOCK_STREAM);
+ EXPECT_TRUE(client_socket->Connect((const struct sockaddr*)&ipv4addr,
+ sizeof(ipv4addr)));
+ brillo_loop_.RunOnce(false);
+
+ EXPECT_EQ(1, server_proxy_->pending_connect_jobs_.size());
+}
+
+// Tests the |OnConnectionSetupFinished| callback is handled correctly in case
+// of success or error.
+TEST_F(ServerProxyTest, HandlePendingJobs) {
+ int connection_count = 100;
+ int success_count = 51;
+ int failure_count = 49;
+ // Create |connection_count| connections.
+ for (int i = 0; i < connection_count; ++i) {
+ auto client_socket =
+ std::make_unique<arc_networkd::Socket>(AF_INET, SOCK_STREAM);
+ auto mock_connect_job = std::make_unique<MockProxyConnectJob>(
+ std::move(client_socket), "" /* credentials */,
+ base::BindOnce([](const std::string& target_url,
+ OnProxyResolvedCallback callback) {}),
+ base::BindOnce(&ServerProxy::OnConnectionSetupFinished,
+ base::Unretained(server_proxy_.get())));
+ server_proxy_->pending_connect_jobs_[mock_connect_job.get()] =
+ std::move(mock_connect_job);
+ }
+ // Resolve |failure_count| pending connections with error.
+ for (int i = 0; i < failure_count; ++i) {
+ auto job_iter = server_proxy_->pending_connect_jobs_.begin();
+ std::move(job_iter->second->setup_finished_callback_)
+ .Run(nullptr, job_iter->first);
+ }
+ // Expect failed requests have been cleared from the pending list and no
+ // forwarder.
+ EXPECT_EQ(success_count, server_proxy_->pending_connect_jobs_.size());
+ EXPECT_EQ(0, server_proxy_->forwarders_.size());
+
+ // Resolve |success_count| successful connections.
+ for (int i = 0; i < success_count; ++i) {
+ auto fwd = std::make_unique<arc_networkd::SocketForwarder>(
+ "" /* thread name */,
+ std::make_unique<arc_networkd::Socket>(AF_INET, SOCK_STREAM),
+ std::make_unique<arc_networkd::Socket>(AF_INET, SOCK_STREAM));
+ fwd->Start();
+ auto job_iter = server_proxy_->pending_connect_jobs_.begin();
+ std::move(job_iter->second->setup_finished_callback_)
+ .Run(std::move(fwd), job_iter->first);
+ }
+
+ // Expect the successful requests to have been cleared and |success_count|
+ // active forwarders.
+ EXPECT_EQ(0, server_proxy_->pending_connect_jobs_.size());
+ EXPECT_EQ(success_count, server_proxy_->forwarders_.size());
+}
+
} // namespace system_proxy