blob: 872d3900e378af286ffb23aabe55d2f4b9b67b83 [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#ifndef SYSTEM_PROXY_PROXY_CONNECT_JOB_H_
5#define SYSTEM_PROXY_PROXY_CONNECT_JOB_H_
6
7#include <list>
8#include <memory>
9#include <string>
10#include <string_view>
11#include <vector>
12
Andreea Costinasf90a4c02020-06-12 22:30:51 +020013#include <curl/curl.h>
14
Andreea Costinase45d54b2020-03-10 09:21:14 +010015#include <base/callback_forward.h>
Andreea Costinas08a5d182020-04-29 22:12:47 +020016#include <base/cancelable_callback.h>
Andreea Costinase45d54b2020-03-10 09:21:14 +010017#include <base/files/file_descriptor_watcher_posix.h>
18#include <gtest/gtest_prod.h> // for FRIEND_TEST
19
Garrick Evans3388a032020-03-24 11:25:55 +090020namespace patchpanel {
Andreea Costinase45d54b2020-03-10 09:21:14 +010021class SocketForwarder;
22class Socket;
Garrick Evans3388a032020-03-24 11:25:55 +090023} // namespace patchpanel
Andreea Costinase45d54b2020-03-10 09:21:14 +010024
25namespace system_proxy {
26// ProxyConnectJob asynchronously sets up a connection to a remote target on
27// behalf of a client. Internally, it performs the following steps:
28// - waits for the client to send a HTTP connect request;
29// - extracts the target url from the connect request;
30// - requests proxy resolution for the target url and waits for the result;
31// - performs the proxy authentication and connection setup to the remote
32// target.
33class ProxyConnectJob {
34 public:
35 using OnConnectionSetupFinishedCallback = base::OnceCallback<void(
Garrick Evans3388a032020-03-24 11:25:55 +090036 std::unique_ptr<patchpanel::SocketForwarder>, ProxyConnectJob*)>;
Andreea Costinase45d54b2020-03-10 09:21:14 +010037
38 // Will be invoked by ProxyConnectJob to resolve the proxy for |target_url_|.
39 // The passed |callback| is expected to be called with the list of proxy
40 // servers, which will always contain at least one entry, the default proxy.
41 using ResolveProxyCallback = base::OnceCallback<void(
42 const std::string& url,
43 base::OnceCallback<void(const std::list<std::string>&)> callback)>;
44
Garrick Evans3388a032020-03-24 11:25:55 +090045 ProxyConnectJob(std::unique_ptr<patchpanel::Socket> socket,
Andreea Costinase45d54b2020-03-10 09:21:14 +010046 const std::string& credentials,
47 ResolveProxyCallback resolve_proxy_callback,
48 OnConnectionSetupFinishedCallback setup_finished_callback);
49 ProxyConnectJob(const ProxyConnectJob&) = delete;
50 ProxyConnectJob& operator=(const ProxyConnectJob&) = delete;
51 virtual ~ProxyConnectJob();
52
53 // Marks |client_socket_| as non-blocking and adds a watcher that calls
54 // |OnClientReadReady| when the socket is read ready.
55 virtual bool Start();
56 void OnProxyResolution(const std::list<std::string>& proxy_servers);
57
58 friend std::ostream& operator<<(std::ostream& stream,
59 const ProxyConnectJob& job);
60
61 private:
62 friend class ServerProxyTest;
63 friend class ProxyConnectJobTest;
64 FRIEND_TEST(ServerProxyTest, HandlePendingJobs);
Andreea Costinas5862b102020-03-19 14:45:36 +010065 FRIEND_TEST(ServerProxyTest, HandleConnectRequest);
Andreea Costinase45d54b2020-03-10 09:21:14 +010066 FRIEND_TEST(ProxyConnectJobTest, SuccessfulConnection);
Andreea Costinased3f9782020-05-20 17:09:46 +020067 FRIEND_TEST(ProxyConnectJobTest, SuccessfulConnectionAltEnding);
Andreea Costinase45d54b2020-03-10 09:21:14 +010068 FRIEND_TEST(ProxyConnectJobTest, BadHttpRequestWrongMethod);
69 FRIEND_TEST(ProxyConnectJobTest, BadHttpRequestNoEmptyLine);
70
71 // Reads data from the socket into |raw_request| until the first empty line,
72 // which would mark the end of the HTTP request header.
73 // This method does not validate the headers.
74 bool TryReadHttpHeader(std::vector<char>* raw_request);
75
76 // Called when the client socket is ready for reading.
77 void OnClientReadReady();
78
79 // Called from |OnProxyResolution|, after the proxy for |target_url_| is
80 // resolved.
81 void DoCurlServerConnection(const std::string& proxy_url);
82
83 void OnError(const std::string_view& http_error_message);
84
Andreea Costinas08a5d182020-04-29 22:12:47 +020085 void OnClientConnectTimeout();
86
Andreea Costinasf90a4c02020-06-12 22:30:51 +020087 // Sends the server response to the client. Returns true if the headers and
88 // body were sent successfully, false otherwise. In case of failure, calls
89 // |OnError|. The response headers and body can be empty if the libcurl
90 // connection fails. In this case, this will send the client an error message
91 // based on the HTTP status code |http_response_code_|.
92 bool SendHttpResponseToClient(const std::vector<char>& http_response_headers,
93 const std::vector<char>& http_response_body);
94
Andreea Costinase45d54b2020-03-10 09:21:14 +010095 std::string target_url_;
Andreea Costinasf90a4c02020-06-12 22:30:51 +020096 // HTTP proxy response code to the CONNECT request.
97 int64_t http_response_code_ = 0;
98
Andreea Costinase45d54b2020-03-10 09:21:14 +010099 const std::string credentials_;
100 std::list<std::string> proxy_servers_;
101 ResolveProxyCallback resolve_proxy_callback_;
102 OnConnectionSetupFinishedCallback setup_finished_callback_;
Andreea Costinas08a5d182020-04-29 22:12:47 +0200103 base::CancelableClosure client_connect_timeout_callback_;
104
Garrick Evans3388a032020-03-24 11:25:55 +0900105 std::unique_ptr<patchpanel::Socket> client_socket_;
Andreea Costinase45d54b2020-03-10 09:21:14 +0100106 std::unique_ptr<base::FileDescriptorWatcher::Controller> read_watcher_;
Andreea Costinas833eb7c2020-06-12 11:09:15 +0200107 base::WeakPtrFactory<ProxyConnectJob> weak_ptr_factory_{this};
Andreea Costinase45d54b2020-03-10 09:21:14 +0100108};
109} // namespace system_proxy
110
111#endif // SYSTEM_PROXY_PROXY_CONNECT_JOB_H_