/** * Connects to the remote server * * @return bool whether the connection can be persistent * @throws HTTP_Request2_Exception */ protected function connect() { $secure = 0 == strcasecmp($this->request->getUrl()->getScheme(), 'https'); $tunnel = HTTP_Request2::METHOD_CONNECT == $this->request->getMethod(); $headers = $this->request->getHeaders(); $reqHost = $this->request->getUrl()->getHost(); if (!($reqPort = $this->request->getUrl()->getPort())) { $reqPort = $secure ? 443 : 80; } $httpProxy = $socksProxy = false; if (!($host = $this->request->getConfig('proxy_host'))) { $host = $reqHost; $port = $reqPort; } else { if (!($port = $this->request->getConfig('proxy_port'))) { throw new HTTP_Request2_LogicException('Proxy port not provided', HTTP_Request2_Exception::MISSING_VALUE); } if ('http' == ($type = $this->request->getConfig('proxy_type'))) { $httpProxy = true; } elseif ('socks5' == $type) { $socksProxy = true; } else { throw new HTTP_Request2_NotImplementedException("Proxy type '{$type}' is not supported"); } } if ($tunnel && !$httpProxy) { throw new HTTP_Request2_LogicException("Trying to perform CONNECT request without proxy", HTTP_Request2_Exception::MISSING_VALUE); } if ($secure && !in_array('ssl', stream_get_transports())) { throw new HTTP_Request2_LogicException('Need OpenSSL support for https:// requests', HTTP_Request2_Exception::MISCONFIGURATION); } // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive // connection token to a proxy server... if ($httpProxy && !$secure && !empty($headers['connection']) && 'Keep-Alive' == $headers['connection']) { $this->request->setHeader('connection'); } $keepAlive = '1.1' == $this->request->getConfig('protocol_version') && empty($headers['connection']) || !empty($headers['connection']) && 'Keep-Alive' == $headers['connection']; $options = array(); if ($secure || $tunnel) { foreach ($this->request->getConfig() as $name => $value) { if ('ssl_' == substr($name, 0, 4) && null !== $value) { if ('ssl_verify_host' == $name) { if ($value) { $options['CN_match'] = $reqHost; } } else { $options[substr($name, 4)] = $value; } } } ksort($options); } // Use global request timeout if given, see feature requests #5735, #8964 if ($timeout = $this->request->getConfig('timeout')) { $deadline = time() + $timeout; } else { $deadline = null; } // Changing SSL context options after connection is established does *not* // work, we need a new connection if options change $remote = (!$secure || $httpProxy || $socksProxy ? 'tcp://' : 'ssl://') . $host . ':' . $port; $socketKey = $remote . ($secure && $httpProxy || $socksProxy ? "->{$reqHost}:{$reqPort}" : '') . (empty($options) ? '' : ':' . serialize($options)); unset($this->socket); // We use persistent connections and have a connected socket? // Ensure that the socket is still connected, see bug #16149 if ($keepAlive && !empty(self::$sockets[$socketKey]) && !self::$sockets[$socketKey]->eof()) { $this->socket =& self::$sockets[$socketKey]; } else { if ($socksProxy) { require_once 'HTTP/Request2/SOCKS5.php'; $this->socket = new HTTP_Request2_SOCKS5($remote, $this->request->getConfig('connect_timeout'), $options, $this->request->getConfig('proxy_user'), $this->request->getConfig('proxy_password')); // handle request timeouts ASAP $this->socket->setDeadline($deadline, $this->request->getConfig('timeout')); $this->socket->connect($reqHost, $reqPort); if (!$secure) { $conninfo = "tcp://{$reqHost}:{$reqPort} via {$remote}"; } else { $this->socket->enableCrypto(); $conninfo = "ssl://{$reqHost}:{$reqPort} via {$remote}"; } } elseif ($secure && $httpProxy && !$tunnel) { $this->establishTunnel(); $conninfo = "ssl://{$reqHost}:{$reqPort} via {$remote}"; } else { $this->socket = new HTTP_Request2_SocketWrapper($remote, $this->request->getConfig('connect_timeout'), $options); } $this->request->setLastEvent('connect', empty($conninfo) ? $remote : $conninfo); self::$sockets[$socketKey] =& $this->socket; } $this->socket->setDeadline($deadline, $this->request->getConfig('timeout')); return $keepAlive; }