From 1ea7a6bada66fc9b7a45f61b4892e4ea23196d89 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Sat, 28 Jul 2012 14:55:45 +0200 Subject: support CONNECT for tunneling SSL via HTTP proxies FS#2431 The included test cases currently expect a squid at localhost:3128 --- inc/HTTPClient.php | 66 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 5 deletions(-) (limited to 'inc/HTTPClient.php') diff --git a/inc/HTTPClient.php b/inc/HTTPClient.php index a25846c31..8d2c10be1 100644 --- a/inc/HTTPClient.php +++ b/inc/HTTPClient.php @@ -254,11 +254,7 @@ class HTTPClient { if(!empty($uri['port'])) $headers['Host'].= ':'.$uri['port']; $headers['User-Agent'] = $this->agent; $headers['Referer'] = $this->referer; - if ($this->keep_alive) { - $headers['Connection'] = 'Keep-Alive'; - } else { - $headers['Connection'] = 'Close'; - } + if($method == 'POST'){ if(is_array($data)){ if($headers['Content-Type'] == 'multipart/form-data'){ @@ -299,6 +295,14 @@ class HTTPClient { return false; } + // try establish a CONNECT tunnel for SSL + if($this->_ssltunnel($socket, $request_url)){ + // no keep alive for tunnels + $this->keep_alive = false; + // tunnel is authed already + if(isset($headers['Proxy-Authentication'])) unset($headers['Proxy-Authentication']); + } + // keep alive? if ($this->keep_alive) { self::$connections[$connectionId] = $socket; @@ -307,6 +311,15 @@ class HTTPClient { } } + if ($this->keep_alive && !$this->proxy_host) { + // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive + // connection token to a proxy server. We still do keep the connection the + // proxy alive (well except for CONNECT tunnels) + $headers['Connection'] = 'Keep-Alive'; + } else { + $headers['Connection'] = 'Close'; + } + try { //set non-blocking stream_set_blocking($socket, false); @@ -484,6 +497,49 @@ class HTTPClient { return true; } + /** + * Tries to establish a CONNECT tunnel via Proxy + * + * Protocol, Servername and Port will be stripped from the request URL when a successful CONNECT happened + * + * @param ressource &$socket + * @param string &$requesturl + * @return bool true if a tunnel was established + */ + function _ssltunnel(&$socket, &$requesturl){ + if(!$this->proxy_host) return false; + $requestinfo = parse_url($requesturl); + if($requestinfo['scheme'] != 'https') return false; + if(!$requestinfo['port']) $requestinfo['port'] = 443; + + // build request + $request = "CONNECT {$requestinfo['host']}:{$requestinfo['port']} HTTP/1.0".HTTP_NL; + $request .= "Host: {$requestinfo['host']}".HTTP_NL; + if($this->proxy_user) { + 'Proxy-Authorization Basic '.base64_encode($this->proxy_user.':'.$this->proxy_pass).HTTP_NL; + } + $request .= HTTP_NL; + + $this->_debug('SSL Tunnel CONNECT',$request); + $this->_sendData($socket, $request, 'SSL Tunnel CONNECT'); + + // read headers from socket + $r_headers = ''; + do{ + $r_line = $this->_readLine($socket, 'headers'); + $r_headers .= $r_line; + }while($r_line != "\r\n" && $r_line != "\n"); + + $this->_debug('SSL Tunnel Response',$r_headers); + if(preg_match('/^HTTP\/1\.0 200/i',$r_headers)){ + if (stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_SSLv3_CLIENT)) { + $requesturl = $requestinfo['path']; + return true; + } + } + return false; + } + /** * Safely write data to a socket * -- cgit v1.2.3