--- ProtoPort.h.org	2009-05-22 16:36:34.000000000 +0900
+++ ProtoPort.h	2009-05-22 16:37:04.000000000 +0900
@@ -23,6 +23,7 @@
     unsigned int accel:1;              /**< HTTP accelerator */
     unsigned int vhost:1;              /**< uses host header */
     unsigned int sslBump:1;            /**< intercepts CONNECT requests */
+    unsigned int sslConnect:1;         /**< sslConnect */
 
     int vport;                 /* virtual port support, -1 for dynamic, >0 static*/
     bool connection_auth_disabled;     /* Don't support connection oriented auth */
--- cache_cf.cc.org	2009-05-22 16:32:34.000000000 +0900
+++ cache_cf.cc	2009-05-22 16:34:24.000000000 +0900
@@ -3062,6 +3062,9 @@
     } else if (strncmp(token, "sslcontext=", 11) == 0) {
         safe_free(s->sslcontext);
         s->sslcontext = xstrdup(token + 11);
+    // sslConnect
+    } else if (strncmp(token, "sslConnect", 10) == 0) {
+        s->sslConnect = 1;
     } else if (strcmp(token, "sslBump") == 0) {
         s->sslBump = 1; // accelerated when bumped, otherwise not
 #endif
--- client_side.cc.org	2009-05-23 22:44:21.000000000 +0900
+++ client_side.cc	2009-06-14 05:20:24.000000000 +0900
@@ -168,6 +168,9 @@
 
 static ConnStateData *connStateCreate(const IpAddress &peer, const IpAddress &me, int fd, http_port_list *port);
 
+extern "C" CSR clientGetMoreData;
+extern "C" CSS clientReplyStatus;
+extern "C" CSD clientReplyDetach;
 
 int
 ClientSocketContext::fd() const
@@ -214,6 +217,99 @@
     comm_read(fd, in.addressToReadInto(), getAvailableBufferLength(), call);
 }
 
+void
+ConnStateData::startSSLConnect()
+{
+    ConnStateData * conn = this;
+    ClientSocketContext *context;
+    ClientHttpRequest *http;
+    HttpVersion http_ver(1,0);
+    HttpRequestMethod method = METHOD_CONNECT;
+    protocol_t protocol      = PROTO_NONE;
+    char *urlpath            = NULL;
+    char ntoabuf[MAX_IPSTRLEN];
+    StoreIOBuffer tempBuffer;
+
+    debugs(95, 5, "ConnStateData::startSSLConnect: start");
+
+    /* Should not be needed anymore */
+    /* Terminate the string */
+    conn->in.buf[conn->in.notYetUsed] = '\0';
+
+    http = new ClientHttpRequest(conn);
+    context = ClientSocketContextNew(http);
+
+    if (conn->transparent()) {
+        http->flags.intercepted = conn->port->intercepted;
+        http->flags.spoof_client_ip = conn->port->spoof_client_ip;
+
+        // set url
+        int url_sz = 32 + Config.appendDomainLen;
+        http->uri = (char *)xcalloc(url_sz, 1);
+        snprintf(http->uri, url_sz, "%s:%d",
+                 http->getConn()->me.NtoA(ntoabuf,MAX_IPSTRLEN),
+                 http->getConn()->me.GetPort());
+        debugs(95, 5, "ConnStateData::startSSLConnect: " << http->uri);
+    }
+    if (context) {
+        debugs(95, 5, "ConnStateData::startSSLConnect: set http request objects");
+        commSetTimeout(conn->fd, Config.Timeout.lifetime,
+                       clientLifetimeTimeout, context->http);
+
+        // clientProcessRequest
+        HttpRequest *request = new HttpRequest(method, protocol, urlpath);
+
+        /* We have an initial client stream in place should it be needed */
+        /* setup our private context */
+        context->registerWithConn();
+
+        if (http->flags.internal) {
+            request->protocol = PROTO_HTTP;
+            request->login[0] = '\0';
+        }
+        if (conn->transparent()) {
+            request->flags.spoof_client_ip = conn->port->spoof_client_ip;
+        }
+        setLogUri (http, http->uri);
+        request->flags.sslConnect = conn->port->sslConnect;
+        request->flags.internal = http->flags.internal;
+        request->client_addr = conn->peer;
+        request->SetHost(http->getConn()->me.NtoA(ntoabuf,MAX_IPSTRLEN));
+        request->port = http->getConn()->me.GetPort();
+#if FOLLOW_X_FORWARDED_FOR
+        request->indirect_client_addr = conn->peer;
+#endif /* FOLLOW_X_FORWARDED_FOR */
+        request->my_addr = conn->me;
+        request->http_ver = http_ver;
+        http->request = HTTPMSGLOCK(request);
+        http->req_sz = conn->in.notYetUsed;
+        clientSetKeepaliveFlag(http);
+
+        tempBuffer.data = context->reqbuf;
+        tempBuffer.length = HTTP_REQBUF_SZ;
+        ClientStreamData newServer = new clientReplyContext(http);
+        ClientStreamData newClient = context;
+        clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
+                         clientReplyStatus, newServer, clientSocketRecipient,
+                         clientSocketDetach, newClient, tempBuffer);
+
+        http->calloutContext = new ClientRequestContext(http);
+
+        // check access list
+        if (Config.accessList.http) {
+            ACLFilledChecklist checklist(Config.accessList.http, request, NULL);
+            checklist.src_addr = request->client_addr;
+            checklist.my_addr  = request->my_addr;
+            if (!checklist.fastCheck()) {
+                debugs(95, 5, "ConnStateData::startSSLConnect: access denied");
+                comm_close(conn->fd);
+                return;
+            }
+        }
+        tunnelStart(http, &(http->out.size), &(http->al.http.code));
+    }
+    debugs(95, 5, "ConnStateData::startSSLConnect: end");
+}
 
 void
 ClientSocketContext::removeFromConnectionList(ConnStateData * conn)
@@ -2886,7 +2982,11 @@
         commSetTcpKeepalive(newfd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
     }
 
-    connState->readSomeData();
+    if (s->sslConnect) {
+        connState->startSSLConnect();
+    } else {
+        connState->readSomeData();
+    }
 
     clientdbEstablished(details->peer, 1);
 
--- client_side.h.org	2009-05-23 22:55:28.000000000 +0900
+++ client_side.h	2009-05-23 22:57:32.000000000 +0900
@@ -132,6 +132,7 @@
     ~ConnStateData();
 
     void readSomeData();
+    void startSSLConnect();
     int getAvailableBufferLength() const;
     bool areAllContextsForThisConnection() const;
     void freeAllContexts();
--- structs.h.org	2009-05-23 22:33:02.000000000 +0900
+++ structs.h	2009-05-23 22:36:11.000000000 +0900
@@ -1034,6 +1034,7 @@
     unsigned int connection_proxy_auth:1; /** Request wants connection oriented auth */
     unsigned int pinned:1;      /* Request sent on a pinned connection */
     unsigned int auth_sent:1;   /* Authentication forwarded */
+    unsigned int sslConnect:1;
 
     // When adding new flags, please update cloneAdaptationImmune() as needed.
 
--- tunnel.cc.org	2009-05-22 16:38:32.000000000 +0900
+++ tunnel.cc	2009-06-12 23:20:28.000000000 +0900
@@ -480,19 +480,17 @@
     } else
         debugs(26, 1, "tunnelConnectTimeout(): tunnelState->servers is NULL");
 
-    err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
-
     *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE;
-
-    err->xerrno = ETIMEDOUT;
-
-    err->port = tunnelState->port;
-
-    err->callback = tunnelErrorComplete;
-
-    err->callback_data = tunnelState;
-
-    errorSend(tunnelState->client.fd(), err);
+    if (request->flags.sslConnect) {
+        // nothing to do
+    } else {
+        err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
+        err->xerrno = ETIMEDOUT;
+        err->port = tunnelState->port;
+        err->callback = tunnelErrorComplete;
+        err->callback_data = tunnelState;
+        errorSend(tunnelState->client.fd(), err);
+    }
     comm_close(fd);
 }
 
@@ -522,13 +520,46 @@
 }
 
 static void
+tunnelProxyConnectedWriteDummyDone(int fd, char *buf, size_t size, comm_err_t flag, int xerrno, void *data)
+{
+    // nothing to do
+}
+
+static void
+tunnelProxyConnectedReadDone(int fd, char *buf, size_t size, comm_err_t flag, int xerrno, void *data)
+{
+    TunnelStateData *tunnelState = (TunnelStateData *)data;
+    debugs(95, 5, "tunnelProxyConnectedReadDone: start");
+    if (strncmp(buf, "\r\n", size) == 0      ||
+        strncmp(buf, "\n", size)   == 0      ||
+        squid_strnstr(buf, "\r\n\r\n", size) || 
+        squid_strnstr(buf, "\n\n", size)) {
+        debugs(95, 5, "tunnelProxyConnectedReadDone: detect the status code");
+        tunnelConnectedWriteDone(fd, buf, size, flag, xerrno, data);
+    } else {
+        // retry
+        debugs(95, 5, "tunnelProxyConnectedReadDone: retry to get the status code");
+        comm_read(tunnelState->server.fd(), tunnelState->server.buf,
+                  tunnelState->server.bytesWanted(1, SQUID_TCP_SO_RCVBUF),
+                  tunnelProxyConnectedReadDone, tunnelState);
+    }
+    debugs(95, 5, "tunnelProxyConnectedReadDone: end");
+}
+
+static void
 tunnelConnected(int fd, void *data)
 {
     TunnelStateData *tunnelState = (TunnelStateData *)data;
     debugs(26, 3, "tunnelConnected: FD " << fd << " tunnelState=" << tunnelState);
     *tunnelState->status_ptr = HTTP_OK;
-    comm_write(tunnelState->client.fd(), conn_established, strlen(conn_established),
-               tunnelConnectedWriteDone, tunnelState, NULL);
+
+    if (tunnelState->request->flags.sslConnect) {
+        debugs(95, 5, "tunnelConnected: direct connected");
+        tunnelConnectedWriteDone(fd, NULL, 0, COMM_OK, 0, tunnelState);
+    } else {
+        comm_write(tunnelState->client.fd(), conn_established, strlen(conn_established),
+                   tunnelConnectedWriteDone, tunnelState, NULL);
+    }
 }
 
 static void
@@ -568,20 +599,28 @@
 
     if (status == COMM_ERR_DNS) {
         debugs(26, 4, "tunnelConnect: Unknown host: " << tunnelState->host);
-        err = errorCon(ERR_DNS_FAIL, HTTP_NOT_FOUND, request);
         *tunnelState->status_ptr = HTTP_NOT_FOUND;
-        err->dnsserver_msg = xstrdup(dns_error_message_safe());
-        err->callback = tunnelErrorComplete;
-        err->callback_data = tunnelState;
-        errorSend(tunnelState->client.fd(), err);
+        if (request->flags.sslConnect) {
+            comm_close(tunnelState->client.fd());
+        } else {
+            err = errorCon(ERR_DNS_FAIL, HTTP_NOT_FOUND, request);
+            err->dnsserver_msg = xstrdup(dns_error_message_safe());
+            err->callback = tunnelErrorComplete;
+            err->callback_data = tunnelState;
+            errorSend(tunnelState->client.fd(), err);
+        }
     } else if (status != COMM_OK) {
-        err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
         *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE;
-        err->xerrno = xerrno;
-        err->port = tunnelState->port;
-        err->callback = tunnelErrorComplete;
-        err->callback_data = tunnelState;
-        errorSend(tunnelState->client.fd(), err);
+        if (request->flags.sslConnect) {
+            comm_close(tunnelState->client.fd());
+        } else {
+            err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
+            err->xerrno = xerrno;
+            err->port = tunnelState->port;
+            err->callback = tunnelErrorComplete;
+            err->callback_data = tunnelState;
+            errorSend(tunnelState->client.fd(), err);
+        }
     } else {
         if (tunnelState->servers->_peer)
             tunnelProxyConnected(tunnelState->server.fd(), tunnelState);
@@ -624,9 +663,13 @@
         answer = ch.fastCheck();
 
         if (answer == 0) {
-            err = errorCon(ERR_FORWARDING_DENIED, HTTP_FORBIDDEN, request);
             *status_ptr = HTTP_FORBIDDEN;
-            errorSend(fd, err);
+            if (tunnelState->request->flags.sslConnect) {
+                comm_close(fd);
+            } else {
+                err = errorCon(ERR_FORWARDING_DENIED, HTTP_FORBIDDEN, request);
+                errorSend(fd, err);
+            }
             return;
         }
     }
@@ -636,19 +679,33 @@
     statCounter.server.other.requests++;
     /* Create socket. */
     IpAddress temp = getOutgoingAddr(request,NULL);
-    sock = comm_openex(SOCK_STREAM,
-                       IPPROTO_TCP,
-                       temp,
-                       COMM_NONBLOCKING,
-                       getOutgoingTOS(request),
-                       url);
+
+    if (request->flags.spoof_client_ip) {
+        sock = comm_openex(SOCK_STREAM,
+                           IPPROTO_TCP,
+                           temp,
+                           (COMM_NONBLOCKING|COMM_TRANSPARENT),
+                           getOutgoingTOS(request),
+                           url);
+    } else {
+        sock = comm_openex(SOCK_STREAM,
+                           IPPROTO_TCP,
+                           temp,
+                           COMM_NONBLOCKING,
+                           getOutgoingTOS(request),
+                           url);
+    }
 
     if (sock == COMM_ERROR) {
         debugs(26, 4, "tunnelStart: Failed because we're out of sockets.");
-        err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
         *status_ptr = HTTP_INTERNAL_SERVER_ERROR;
-        err->xerrno = errno;
-        errorSend(fd, err);
+        if (tunnelState->request->flags.sslConnect) {
+            comm_close(fd);
+        } else {
+            err = errorCon(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
+            err->xerrno = errno;
+            errorSend(fd, err);
+        }
         return;
     }
 
@@ -713,7 +770,14 @@
     packerClean(&p);
     mb.append("\r\n", 2);
 
-    comm_write_mbuf(tunnelState->server.fd(), &mb, tunnelProxyConnectedWriteDone, tunnelState);
+    if (tunnelState->request->flags.sslConnect) {
+       comm_write_mbuf(tunnelState->server.fd(), &mb, tunnelProxyConnectedWriteDummyDone, NULL);
+       comm_read(tunnelState->server.fd(), tunnelState->server.buf,
+                 tunnelState->server.bytesWanted(1, SQUID_TCP_SO_RCVBUF),
+                 tunnelProxyConnectedReadDone, tunnelState);
+    } else {
+       comm_write_mbuf(tunnelState->server.fd(), &mb, tunnelProxyConnectedWriteDone, tunnelState);
+    }
     commSetTimeout(tunnelState->server.fd(), Config.Timeout.read, tunnelTimeout, tunnelState);
 }
 
@@ -725,12 +789,17 @@
     peer *g = NULL;
 
     if (fs == NULL) {
-        ErrorState *err;
-        err = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request);
-        *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE;
-        err->callback = tunnelErrorComplete;
-        err->callback_data = tunnelState;
-        errorSend(tunnelState->client.fd(), err);
+        if (request->flags.sslConnect) {
+            *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE;
+            comm_close(tunnelState->client.fd());
+        } else {
+            ErrorState *err;
+            err = errorCon(ERR_CANNOT_FORWARD, HTTP_SERVICE_UNAVAILABLE, request);
+            *tunnelState->status_ptr = HTTP_SERVICE_UNAVAILABLE;
+            err->callback = tunnelErrorComplete;
+            err->callback_data = tunnelState;
+            errorSend(tunnelState->client.fd(), err);
+        }
         return;
     }
 
@@ -806,3 +875,4 @@
 }
 
 #endif
+
