shill: Support CONNECT HTTP method in proxy

This will allow HTTPS requests to be proxied.

BUG=chromium-os:24220
TEST=New unit tests +
manual "curl -v -x localhost:xxx https://www.google.com" test

Change-Id: Ibdec3536edb97018f6a7e7545025dd792998e7b0
Reviewed-on: https://gerrit.chromium.org/gerrit/13955
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Reviewed-by: Thieu Le <thieule@chromium.org>
Commit-Ready: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/http_proxy_unittest.cc b/http_proxy_unittest.cc
index d7f4ddb..ae4cd75 100644
--- a/http_proxy_unittest.cc
+++ b/http_proxy_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 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.
 
@@ -41,13 +41,17 @@
 namespace shill {
 
 namespace {
-const char kBadHeader[] = "BLAH\r\n";
+const char kBadHeaderMissingURL[] = "BLAH\r\n";
+const char kBadHeaderMissingVersion[] = "BLAH http://hostname\r\n";
 const char kBadHostnameLine[] = "GET HTTP/1.1 http://hostname\r\n";
 const char kBasicGetHeader[] = "GET / HTTP/1.1\r\n";
 const char kBasicGetHeaderWithURL[] =
     "GET http://www.chromium.org/ HTTP/1.1\r\n";
 const char kBasicGetHeaderWithURLNoTrailingSlash[] =
     "GET http://www.chromium.org HTTP/1.1\r\n";
+const char kConnectQuery[] =
+    "CONNECT 10.10.10.10:443 HTTP/1.1\r\n"
+    "Host: 10.10.10.10:443\r\n\r\n";
 const char kQueryTemplate[] = "GET %s HTTP/%s\r\n%s"
     "User-Agent: Mozilla/5.0 (X11; CrOS i686 1299.0.2011) "
     "AppleWebKit/535.8 (KHTML, like Gecko) Chrome/17.0.936.0 Safari/535.8\r\n"
@@ -75,6 +79,7 @@
 const int kServerFD = 10204;
 const int kClientFD = 10205;
 const int kServerPort = 40506;
+const int kConnectPort = 443;
 }  // namespace {}
 
 MATCHER_P(IsIPAddress, address, "") {
@@ -243,13 +248,16 @@
   void ExpectTransactionTimeout() {
     ExpectTimeout(HTTPProxy::kTransactionTimeoutSeconds);
   }
+  void ExpectInClientResponse(const string &response_data) {
+    string server_data(reinterpret_cast<char *>(proxy_.server_data_.GetData()),
+                       proxy_.server_data_.GetLength());
+    EXPECT_NE(string::npos, server_data.find(response_data));
+  }
   void ExpectClientError(int code, const string &error) {
     EXPECT_EQ(HTTPProxy::kStateFlushResponse, GetProxyState());
     string status_line = StringPrintf("HTTP/1.1 %d ERROR", code);
-    string server_data(reinterpret_cast<char *>(proxy_.server_data_.GetData()),
-                       proxy_.server_data_.GetLength());
-    EXPECT_NE(string::npos, server_data.find(status_line));
-    EXPECT_NE(string::npos, server_data.find(error));
+    ExpectInClientResponse(status_line);
+    ExpectInClientResponse(error);
   }
   void ExpectClientInternalError() {
     ExpectClientError(500, HTTPProxy::kInternalErrorMsg);
@@ -281,12 +289,15 @@
         .WillOnce(DoAll(Invoke(this, &HTTPProxyTest::InvokeSyncConnect),
                         Return(true)));
   }
-  void ExpectClientResult() {
+  void ExpectClientData() {
     EXPECT_CALL(dispatcher(),
                 CreateReadyHandler(kClientFD,
                                    IOHandler::kModeOutput,
                                    proxy_.write_client_callback_.get()))
         .WillOnce(ReturnNew<IOHandler>());
+  }
+  void ExpectClientResult() {
+    ExpectClientData();
     ExpectInputTimeout();
   }
   void ExpectServerInput() {
@@ -527,10 +538,17 @@
   EXPECT_EQ(HTTPProxy::kStateWaitConnection, GetProxyState());
 }
 
-TEST_F(HTTPProxyTest, ReadBadFirstLine) {
+TEST_F(HTTPProxyTest, ReadMissingURL) {
   SetupClient();
   ExpectClientResult();
-  ReadFromClient(kBadHeader);
+  ReadFromClient(kBadHeaderMissingURL);
+  ExpectClientError(501, "Server could not parse HTTP method");
+}
+
+TEST_F(HTTPProxyTest, ReadMissingVersion) {
+  SetupClient();
+  ExpectClientResult();
+  ReadFromClient(kBadHeaderMissingVersion);
   ExpectClientError(501, "Server only accepts HTTP/1.x requests");
 }
 
@@ -680,6 +698,18 @@
   SetupConnectComplete();
 }
 
+TEST_F(HTTPProxyTest, HTTPConnectMethod) {
+  SetupClient();
+  ExpectAsyncConnect(kServerAddress, kConnectPort, true);
+  ExpectConnectTimeout();
+  ExpectRouteRequest();
+  ReadFromClient(kConnectQuery);
+  ExpectRepeatedInputTimeout();
+  ExpectClientData();
+  OnConnectCompletion(true, kServerFD);
+  ExpectInClientResponse("HTTP/1.1 200 OK\r\n\r\n");
+}
+
 TEST_F(HTTPProxyTest, TunnelData) {
   SetupConnectComplete();