shill: Add http_proxy class
The http_proxy adds a device/connection based proxy that guarantees
to the caller that its HTTP request will go out a particular device's
connection. DNS requests occur through a bound socket to this device
and goes to DNS servers configured on this connection. HTTP requests
will also be bound to this interface. This facility will be used by
a number of peripheral bits including portal detection, activation and
cashew.
BUG=chromium-os:21664
TEST=New unit test. New (disabled) functional test, against which I
can run "curl -x" and Chrome with manual proxy settings.
Change-Id: I0d59bf0ae27d3538ef359f786742f5c2f1d6fef9
Reviewed-on: https://gerrit.chromium.org/gerrit/10165
Reviewed-by: Thieu Le <thieule@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
Commit-Ready: Paul Stewart <pstew@chromium.org>
diff --git a/http_proxy.h b/http_proxy.h
new file mode 100644
index 0000000..6bc0601
--- /dev/null
+++ b/http_proxy.h
@@ -0,0 +1,154 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_HTTP_PROXY_
+#define SHILL_HTTP_PROXY_
+
+#include <string>
+#include <vector>
+
+#include <base/callback_old.h>
+#include <base/memory/ref_counted.h>
+#include <base/memory/scoped_ptr.h>
+#include <base/task.h>
+
+#include "shill/byte_string.h"
+#include "shill/refptr_types.h"
+
+namespace shill {
+
+class AsyncConnection;
+class EventDispatcher;
+class DNSClient;
+class InputData;
+class IOHandler;
+class IPAddress;
+class Sockets;
+
+// The HTTPProxy class implements a simple web proxy that
+// is bound to a specific interface and name server. This
+// allows us to specify which connection a URL should be
+// fetched through, even though many connections
+// could be active at the same time.
+//
+// This service is meant to be low-performance, since we
+// do not want to divert resources from the rest of the
+// connection manager. As such, we serve one client request
+// at a time. This is probably okay since the use case is
+// limited -- only portal detection, activation and Cashew
+// are planned to be full-time users.
+class HTTPProxy {
+ public:
+ enum State {
+ kStateIdle,
+ kStateWaitConnection,
+ kStateReadClientHeader,
+ kStateLookupServer,
+ kStateConnectServer,
+ kStateTunnelData,
+ kStateFlushResponse,
+ };
+
+ HTTPProxy(const std::string &interface_name,
+ const std::vector<std::string> &dns_servers);
+ virtual ~HTTPProxy();
+
+ // Start HTTP proxy.
+ bool Start(EventDispatcher *dispatcher, Sockets *sockets);
+
+ // Shutdown.
+ void Stop();
+
+ int proxy_port() const { return proxy_port_; }
+
+ private:
+ friend class HTTPProxyTest;
+
+ // Time to wait for initial headers from client.
+ static const int kClientHeaderTimeoutSeconds;
+ // Time to wait for connection to remote server.
+ static const int kConnectTimeoutSeconds;
+ // Time to wait for DNS server.
+ static const int kDNSTimeoutSeconds;
+ // Default port on remote server to connect to.
+ static const int kDefaultServerPort;
+ // Time to wait for any input from either server or client.
+ static const int kInputTimeoutSeconds;
+ // Maximum clients to be kept waiting.
+ static const size_t kMaxClientQueue;
+ // Maximum number of header lines to accept.
+ static const size_t kMaxHeaderCount;
+ // Maximum length of an individual header line.
+ static const size_t kMaxHeaderSize;
+ // Timeout for whole transaction.
+ static const int kTransactionTimeoutSeconds;
+
+ static const char kHTTPURLDelimiters[];
+ static const char kHTTPURLPrefix[];
+ static const char kHTTPVersionPrefix[];
+ static const char kHTTPVersionErrorMsg[];
+ static const char kInternalErrorMsg[]; // Message to send on failure.
+
+ void AcceptClient(int fd);
+ bool ConnectServer(const IPAddress &address, int port);
+ void GetDNSResult(bool result);
+ void OnConnectCompletion(bool success, int fd);
+ bool ParseClientRequest();
+ bool ProcessLastHeaderLine();
+ bool ReadClientHeaders(InputData *data);
+ bool ReadClientHostname(std::string *header);
+ bool ReadClientHTTPVersion(std::string *header);
+ void ReadFromClient(InputData *data);
+ void ReadFromServer(InputData *data);
+ void SendClientError(int code, const std::string &error);
+ void StartIdleTimeout();
+ void StartReceive();
+ void StartTransmit();
+ void StopClient();
+ void WriteToClient(int fd);
+ void WriteToServer(int fd);
+
+ // State held for the lifetime of the proxy.
+ State state_;
+ const std::string interface_name_;
+ std::vector<std::string> dns_servers_;
+ scoped_ptr<Callback1<int>::Type> accept_callback_;
+ scoped_ptr<Callback2<bool, int>::Type> connect_completion_callback_;
+ scoped_ptr<Callback1<bool>::Type> dns_client_callback_;
+ scoped_ptr<Callback1<InputData *>::Type> read_client_callback_;
+ scoped_ptr<Callback1<InputData *>::Type> read_server_callback_;
+ scoped_ptr<Callback1<int>::Type> write_client_callback_;
+ scoped_ptr<Callback1<int>::Type> write_server_callback_;
+ ScopedRunnableMethodFactory<HTTPProxy> task_factory_;
+
+ // State held while proxy is started (even if no transaction is active).
+ scoped_ptr<IOHandler> accept_handler_;
+ EventDispatcher *dispatcher_;
+ scoped_ptr<DNSClient> dns_client_;
+ int proxy_port_;
+ int proxy_socket_;
+ scoped_ptr<AsyncConnection> server_async_connection_;
+ Sockets *sockets_;
+
+ // State held while proxy is started and a transaction is active.
+ int client_socket_;
+ std::string client_version_;
+ int server_port_;
+ int server_socket_;
+ CancelableTask *idle_timeout_;
+ std::vector<std::string> client_headers_;
+ std::string server_hostname_;
+ ByteString client_data_;
+ ByteString server_data_;
+ scoped_ptr<IOHandler> read_client_handler_;
+ scoped_ptr<IOHandler> write_client_handler_;
+ scoped_ptr<IOHandler> read_server_handler_;
+ scoped_ptr<IOHandler> write_server_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(HTTPProxy);
+};
+
+} // namespace shill
+
+#endif // SHILL_HTTP_PROXY_