blob: 4f079cb6ea688829c7ec868a39418c8ea346ae42 [file] [log] [blame]
Andreea Costinas44cefa22020-03-09 09:07:39 +01001// Copyright 2020 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "system-proxy/server_proxy.h"
6
7#include <netinet/in.h>
Andreea Costinase45d54b2020-03-10 09:21:14 +01008#include <sys/socket.h>
9#include <sys/types.h>
Andreea Costinas44cefa22020-03-09 09:07:39 +010010
11#include <gmock/gmock.h>
12#include <gtest/gtest.h>
13#include <utility>
14
Andreea Costinase45d54b2020-03-10 09:21:14 +010015#include <arc/network/socket.h>
16#include <arc/network/socket_forwarder.h>
Andreea Costinas44cefa22020-03-09 09:07:39 +010017#include <base/bind.h>
18#include <base/bind_helpers.h>
19#include <base/callback_helpers.h>
20#include <base/files/file_util.h>
21#include <base/files/scoped_file.h>
22#include <base/message_loop/message_loop.h>
Andreea Costinase45d54b2020-03-10 09:21:14 +010023#include <base/strings/string_util.h>
Andreea Costinas44cefa22020-03-09 09:07:39 +010024#include <brillo/dbus/async_event_sequencer.h>
Andreea Costinas44cefa22020-03-09 09:07:39 +010025#include <brillo/message_loops/base_message_loop.h>
26
27#include "bindings/worker_common.pb.h"
28#include "system-proxy/protobuf_util.h"
Andreea Costinase45d54b2020-03-10 09:21:14 +010029#include "system-proxy/proxy_connect_job.h"
Andreea Costinas44cefa22020-03-09 09:07:39 +010030
31namespace system_proxy {
32namespace {
Andreea Costinase45d54b2020-03-10 09:21:14 +010033constexpr char kUsername[] = "proxy:user";
34constexpr char kUsernameEncoded[] = "proxy%3Auser";
35constexpr char kPassword[] = "proxy password";
36constexpr char kPasswordEncoded[] = "proxy%20password";
Andreea Costinas44cefa22020-03-09 09:07:39 +010037constexpr int kTestPort = 3128;
Andreea Costinase45d54b2020-03-10 09:21:14 +010038
Andreea Costinas44cefa22020-03-09 09:07:39 +010039} // namespace
40
Andreea Costinase45d54b2020-03-10 09:21:14 +010041using ::testing::_;
Andreea Costinas44cefa22020-03-09 09:07:39 +010042using ::testing::Return;
43
44class MockServerProxy : public ServerProxy {
45 public:
46 explicit MockServerProxy(base::OnceClosure quit_closure)
47 : ServerProxy(std::move(quit_closure)) {}
48 MockServerProxy(const MockServerProxy&) = delete;
49 MockServerProxy& operator=(const MockServerProxy&) = delete;
50 ~MockServerProxy() override = default;
51
52 MOCK_METHOD(int, GetStdinPipe, (), (override));
53};
54
Andreea Costinase45d54b2020-03-10 09:21:14 +010055class MockProxyConnectJob : public ProxyConnectJob {
56 public:
57 MockProxyConnectJob(std::unique_ptr<arc_networkd::Socket> socket,
58 const std::string& credentials,
59 ResolveProxyCallback resolve_proxy_callback,
60 OnConnectionSetupFinishedCallback setup_finished_callback)
61 : ProxyConnectJob(std::move(socket),
62 credentials,
63 std::move(resolve_proxy_callback),
64 std::move(setup_finished_callback)) {}
65 MockProxyConnectJob(const MockProxyConnectJob&) = delete;
66 MockProxyConnectJob& operator=(const MockProxyConnectJob&) = delete;
67 ~MockProxyConnectJob() override = default;
68
69 MOCK_METHOD(bool, Start, (), (override));
70};
71
Andreea Costinas44cefa22020-03-09 09:07:39 +010072class ServerProxyTest : public ::testing::Test {
73 public:
74 ServerProxyTest() {
75 server_proxy_ =
76 std::make_unique<MockServerProxy>(brillo_loop_.QuitClosure());
77 }
78
79 ServerProxyTest(const ServerProxyTest&) = delete;
80 ServerProxyTest& operator=(const ServerProxyTest&) = delete;
81 ~ServerProxyTest() override {}
82
83 protected:
84 void RedirectStdPipes() {
85 int fds[2];
86 CHECK(base::CreateLocalNonBlockingPipe(fds));
87 read_scoped_fd_.reset(fds[0]);
88 write_scoped_fd_.reset(fds[1]);
89 EXPECT_CALL(*server_proxy_, GetStdinPipe())
90 .WillRepeatedly(Return(read_scoped_fd_.get()));
91
92 server_proxy_->Init();
93 }
94 // SystemProxyAdaptor instance that creates fake worker processes.
95 std::unique_ptr<MockServerProxy> server_proxy_;
96 base::MessageLoopForIO loop_;
97 brillo::BaseMessageLoop brillo_loop_{&loop_};
98 base::ScopedFD read_scoped_fd_, write_scoped_fd_;
99};
100
101TEST_F(ServerProxyTest, FetchCredentials) {
102 Credentials credentials;
Andreea Costinase45d54b2020-03-10 09:21:14 +0100103 credentials.set_username(kUsername);
Andreea Costinas44cefa22020-03-09 09:07:39 +0100104 credentials.set_password(kPassword);
105 WorkerConfigs configs;
106 *configs.mutable_credentials() = credentials;
107 RedirectStdPipes();
108
109 EXPECT_TRUE(WriteProtobuf(write_scoped_fd_.get(), configs));
110
111 brillo_loop_.RunOnce(false);
112
Andreea Costinase45d54b2020-03-10 09:21:14 +0100113 std::string expected_credentials =
114 base::JoinString({kUsernameEncoded, kPasswordEncoded}, ":");
115 EXPECT_EQ(server_proxy_->credentials_, expected_credentials);
Andreea Costinas44cefa22020-03-09 09:07:39 +0100116}
117
118TEST_F(ServerProxyTest, FetchListeningAddress) {
119 SocketAddress address;
120 address.set_addr(INADDR_ANY);
121 address.set_port(kTestPort);
122 WorkerConfigs configs;
123 *configs.mutable_listening_address() = address;
124 RedirectStdPipes();
125 // Redirect the worker stdin and stdout pipes.
126
127 EXPECT_TRUE(WriteProtobuf(write_scoped_fd_.get(), configs));
128 brillo_loop_.RunOnce(false);
129
130 EXPECT_EQ(server_proxy_->listening_addr_, INADDR_ANY);
131 EXPECT_EQ(server_proxy_->listening_port_, kTestPort);
132}
133
Andreea Costinase45d54b2020-03-10 09:21:14 +0100134TEST_F(ServerProxyTest, HandleConnectRequest) {
135 server_proxy_->listening_addr_ = htonl(INADDR_LOOPBACK);
136 server_proxy_->listening_port_ = kTestPort;
137 // Redirect the worker stdin and stdout pipes.
138 RedirectStdPipes();
139 server_proxy_->CreateListeningSocket();
140
141 CHECK_NE(-1, server_proxy_->listening_fd_->fd());
142 brillo_loop_.RunOnce(false);
143
144 struct sockaddr_in ipv4addr;
145 ipv4addr.sin_family = AF_INET;
146 ipv4addr.sin_port = htons(kTestPort);
147 ipv4addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
148
149 auto client_socket =
150 std::make_unique<arc_networkd::Socket>(AF_INET, SOCK_STREAM);
151 EXPECT_TRUE(client_socket->Connect((const struct sockaddr*)&ipv4addr,
152 sizeof(ipv4addr)));
153 brillo_loop_.RunOnce(false);
154
155 EXPECT_EQ(1, server_proxy_->pending_connect_jobs_.size());
156}
157
158// Tests the |OnConnectionSetupFinished| callback is handled correctly in case
159// of success or error.
160TEST_F(ServerProxyTest, HandlePendingJobs) {
161 int connection_count = 100;
162 int success_count = 51;
163 int failure_count = 49;
164 // Create |connection_count| connections.
165 for (int i = 0; i < connection_count; ++i) {
166 auto client_socket =
167 std::make_unique<arc_networkd::Socket>(AF_INET, SOCK_STREAM);
168 auto mock_connect_job = std::make_unique<MockProxyConnectJob>(
169 std::move(client_socket), "" /* credentials */,
170 base::BindOnce([](const std::string& target_url,
171 OnProxyResolvedCallback callback) {}),
172 base::BindOnce(&ServerProxy::OnConnectionSetupFinished,
173 base::Unretained(server_proxy_.get())));
174 server_proxy_->pending_connect_jobs_[mock_connect_job.get()] =
175 std::move(mock_connect_job);
176 }
177 // Resolve |failure_count| pending connections with error.
178 for (int i = 0; i < failure_count; ++i) {
179 auto job_iter = server_proxy_->pending_connect_jobs_.begin();
180 std::move(job_iter->second->setup_finished_callback_)
181 .Run(nullptr, job_iter->first);
182 }
183 // Expect failed requests have been cleared from the pending list and no
184 // forwarder.
185 EXPECT_EQ(success_count, server_proxy_->pending_connect_jobs_.size());
186 EXPECT_EQ(0, server_proxy_->forwarders_.size());
187
188 // Resolve |success_count| successful connections.
189 for (int i = 0; i < success_count; ++i) {
190 auto fwd = std::make_unique<arc_networkd::SocketForwarder>(
191 "" /* thread name */,
192 std::make_unique<arc_networkd::Socket>(AF_INET, SOCK_STREAM),
193 std::make_unique<arc_networkd::Socket>(AF_INET, SOCK_STREAM));
194 fwd->Start();
195 auto job_iter = server_proxy_->pending_connect_jobs_.begin();
196 std::move(job_iter->second->setup_finished_callback_)
197 .Run(std::move(fwd), job_iter->first);
198 }
199
200 // Expect the successful requests to have been cleared and |success_count|
201 // active forwarders.
202 EXPECT_EQ(0, server_proxy_->pending_connect_jobs_.size());
203 EXPECT_EQ(success_count, server_proxy_->forwarders_.size());
204}
205
Andreea Costinas44cefa22020-03-09 09:07:39 +0100206} // namespace system_proxy