blob: 8b0a3adcbbd40d52a1be30d3e32cc690774acca3 [file] [log] [blame]
Andreea Costinase45d54b2020-03-10 09:21:14 +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/proxy_connect_job.h"
6
7#include <netinet/in.h>
8#include <sys/socket.h>
9#include <sys/types.h>
10
11#include <gmock/gmock.h>
12#include <gtest/gtest.h>
13#include <utility>
14
Andreea Costinase45d54b2020-03-10 09:21:14 +010015#include <base/bind.h>
16#include <base/bind_helpers.h>
17#include <base/callback_helpers.h>
18#include <base/files/file_util.h>
19#include <base/files/scoped_file.h>
Andreea Costinas08a5d182020-04-29 22:12:47 +020020#include <base/test/test_mock_time_task_runner.h>
Andreea Costinase45d54b2020-03-10 09:21:14 +010021#include <brillo/message_loops/base_message_loop.h>
Garrick Evanscd8c2972020-04-14 14:35:52 +090022#include <chromeos/patchpanel/socket.h>
23#include <chromeos/patchpanel/socket_forwarder.h>
Andreea Costinase45d54b2020-03-10 09:21:14 +010024
25#include "bindings/worker_common.pb.h"
26#include "system-proxy/protobuf_util.h"
27
28namespace {
29constexpr char kProxyServerUrl[] = "172.0.0.1:8888";
30} // namespace
31
32namespace system_proxy {
33
34using ::testing::_;
35using ::testing::Return;
36
37class ProxyConnectJobTest : public ::testing::Test {
38 public:
39 ProxyConnectJobTest() = default;
40 ProxyConnectJobTest(const ProxyConnectJobTest&) = delete;
41 ProxyConnectJobTest& operator=(const ProxyConnectJobTest&) = delete;
42 ~ProxyConnectJobTest() = default;
43
44 void SetUp() override {
45 int fds[2];
46 ASSERT_NE(-1,
47 socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
48 0 /* protocol */, fds));
49 cros_client_socket_ =
Garrick Evans3388a032020-03-24 11:25:55 +090050 std::make_unique<patchpanel::Socket>(base::ScopedFD(fds[1]));
Andreea Costinase45d54b2020-03-10 09:21:14 +010051
52 connect_job_ = std::make_unique<ProxyConnectJob>(
Garrick Evans3388a032020-03-24 11:25:55 +090053 std::make_unique<patchpanel::Socket>(base::ScopedFD(fds[0])), "",
Andreea Costinase45d54b2020-03-10 09:21:14 +010054 base::BindOnce(&ProxyConnectJobTest::ResolveProxy,
55 base::Unretained(this)),
56 base::BindOnce(&ProxyConnectJobTest::OnConnectionSetupFinished,
57 base::Unretained(this)));
Andreea Costinase45d54b2020-03-10 09:21:14 +010058 }
59
60 protected:
61 void ResolveProxy(
62 const std::string& target_url,
63 base::OnceCallback<void(const std::list<std::string>&)> callback) {
64 std::move(callback).Run({kProxyServerUrl});
65 }
66
67 void OnConnectionSetupFinished(
Garrick Evans3388a032020-03-24 11:25:55 +090068 std::unique_ptr<patchpanel::SocketForwarder> fwd,
Andreea Costinase45d54b2020-03-10 09:21:14 +010069 ProxyConnectJob* connect_job) {
70 ASSERT_EQ(connect_job, connect_job_.get());
71 }
72
73 std::unique_ptr<ProxyConnectJob> connect_job_;
74 base::MessageLoopForIO loop_;
75 brillo::BaseMessageLoop brillo_loop_{&loop_};
Garrick Evans3388a032020-03-24 11:25:55 +090076 std::unique_ptr<patchpanel::Socket> cros_client_socket_;
Andreea Costinas08a5d182020-04-29 22:12:47 +020077
78 private:
79 FRIEND_TEST(ProxyConnectJobTest, ClientConnectTimeoutJobCanceled);
Andreea Costinase45d54b2020-03-10 09:21:14 +010080};
81
82TEST_F(ProxyConnectJobTest, SuccessfulConnection) {
Andreea Costinas08a5d182020-04-29 22:12:47 +020083 connect_job_->Start();
Andreea Costinase45d54b2020-03-10 09:21:14 +010084 char validConnRequest[] =
85 "CONNECT www.example.server.com:443 HTTP/1.1\r\n\r\n";
86 cros_client_socket_->SendTo(validConnRequest, std::strlen(validConnRequest));
87 brillo_loop_.RunOnce(false);
88
Andreea Costinasa2246592020-04-12 23:24:01 +020089 EXPECT_EQ("www.example.server.com:443", connect_job_->target_url_);
Andreea Costinase45d54b2020-03-10 09:21:14 +010090 EXPECT_EQ(1, connect_job_->proxy_servers_.size());
91 EXPECT_EQ(kProxyServerUrl, connect_job_->proxy_servers_.front());
92}
93
94TEST_F(ProxyConnectJobTest, BadHttpRequestWrongMethod) {
Andreea Costinas08a5d182020-04-29 22:12:47 +020095 connect_job_->Start();
Andreea Costinase45d54b2020-03-10 09:21:14 +010096 char badConnRequest[] = "GET www.example.server.com:443 HTTP/1.1\r\n\r\n";
97 cros_client_socket_->SendTo(badConnRequest, std::strlen(badConnRequest));
98 brillo_loop_.RunOnce(false);
99
100 EXPECT_EQ("", connect_job_->target_url_);
101 EXPECT_EQ(0, connect_job_->proxy_servers_.size());
102 const std::string expected_http_response =
103 "HTTP/1.1 400 Bad Request - Origin: local proxy\r\n\r\n";
104 std::vector<char> buf(expected_http_response.size());
105 ASSERT_TRUE(
106 base::ReadFromFD(cros_client_socket_->fd(), buf.data(), buf.size()));
107 std::string actual_response(buf.data(), buf.size());
108 EXPECT_EQ(expected_http_response, actual_response);
109}
110
111TEST_F(ProxyConnectJobTest, BadHttpRequestNoEmptyLine) {
Andreea Costinas08a5d182020-04-29 22:12:47 +0200112 connect_job_->Start();
Andreea Costinase45d54b2020-03-10 09:21:14 +0100113 // No empty line after http message.
114 char badConnRequest[] = "CONNECT www.example.server.com:443 HTTP/1.1\r\n";
115 cros_client_socket_->SendTo(badConnRequest, std::strlen(badConnRequest));
116 brillo_loop_.RunOnce(false);
117
118 EXPECT_EQ("", connect_job_->target_url_);
119 EXPECT_EQ(0, connect_job_->proxy_servers_.size());
120 const std::string expected_http_response =
121 "HTTP/1.1 400 Bad Request - Origin: local proxy\r\n\r\n";
122 std::vector<char> buf(expected_http_response.size());
123 ASSERT_TRUE(
124 base::ReadFromFD(cros_client_socket_->fd(), buf.data(), buf.size()));
125 std::string actual_response(buf.data(), buf.size());
126 EXPECT_EQ(expected_http_response, actual_response);
127}
128
Andreea Costinas08a5d182020-04-29 22:12:47 +0200129TEST_F(ProxyConnectJobTest, WaitClientConnectTimeout) {
130 // Add a TaskRunner where we can control time.
131 scoped_refptr<base::TestMockTimeTaskRunner> task_runner{
132 new base::TestMockTimeTaskRunner()};
133 loop_.SetTaskRunner(task_runner);
134 base::TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner.get());
135
136 connect_job_->Start();
137
138 EXPECT_EQ(1, task_runner->GetPendingTaskCount());
139 // Move the time ahead so that the client connection timeout callback is
140 // triggered.
141 task_runner->FastForwardBy(task_runner->NextPendingTaskDelay());
142
143 const std::string expected_http_response =
144 "HTTP/1.1 408 Request Timeout - Origin: local proxy\r\n\r\n";
145 std::vector<char> buf(expected_http_response.size());
146 ASSERT_TRUE(
147 base::ReadFromFD(cros_client_socket_->fd(), buf.data(), buf.size()));
148 std::string actual_response(buf.data(), buf.size());
149
150 EXPECT_EQ(expected_http_response, actual_response);
151}
152
153// Check that the client connect timeout callback is not fired if the owning
154// proxy connect job is destroyed.
155TEST_F(ProxyConnectJobTest, ClientConnectTimeoutJobCanceled) {
156 // Add a TaskRunner where we can control time.
157 scoped_refptr<base::TestMockTimeTaskRunner> task_runner{
158 new base::TestMockTimeTaskRunner()};
159 loop_.SetTaskRunner(task_runner);
160 base::TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner.get());
161
162 // Create a proxy connect job and start the client connect timeout counter.
163 {
164 int fds[2];
165 ASSERT_NE(-1,
166 socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
167 0 /* protocol */, fds));
168 auto client_socket =
169 std::make_unique<patchpanel::Socket>(base::ScopedFD(fds[1]));
170
171 auto connect_job = std::make_unique<ProxyConnectJob>(
172 std::make_unique<patchpanel::Socket>(base::ScopedFD(fds[0])), "",
173 base::BindOnce(&ProxyConnectJobTest::ResolveProxy,
174 base::Unretained(this)),
175 base::BindOnce(&ProxyConnectJobTest::OnConnectionSetupFinished,
176 base::Unretained(this)));
177 // Post the timeout task.
178 connect_job->Start();
179 EXPECT_TRUE(task_runner->HasPendingTask());
180 }
181 // Check that the task was canceled.
182 EXPECT_FALSE(task_runner->HasPendingTask());
183}
184
Andreea Costinase45d54b2020-03-10 09:21:14 +0100185} // namespace system_proxy