/** * Send the HTTP request and return an HTTP response object * * @param string $method * @return EHttpResponse * @throws EHttpClientException */ public function request($method = null) { if (!$this->uri instanceof EUriHttp) { throw new EHttpClientException(Yii::t('EHttpClient', 'No valid URI has been passed to the client')); } if ($method) { $this->setMethod($method); } $this->redirectCounter = 0; $response = null; // Make sure the adapter is loaded if ($this->adapter == null) { $this->setAdapter($this->config['adapter']); } // Send the first request. If redirected, continue. do { // Clone the URI and add the additional GET parameters to it $uri = clone $this->uri; if (!empty($this->paramsGet)) { $query = $uri->getQuery(); if (!empty($query)) { $query .= '&'; } $query .= http_build_query($this->paramsGet, null, '&'); if ($this->config['rfc3986_strict']) { $query = str_replace('+', '%20', $query); } // @see ZF-11671 to unmask for some services to foo=val1&foo=val2 if ($this->getUnmaskStatus()) { if ($this->_queryBracketsEscaped) { $query = preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '=', $query); } else { $query = preg_replace('/\\[(?:[0-9]|[1-9][0-9]+)\\]=/', '=', $query); } } $uri->setQuery($query); } $body = $this->_prepareBody(); $headers = $this->_prepareHeaders(); // check that adapter supports streaming before using it if (is_resource($body) && !$this->adapter instanceof EHttpClientAdapterStream) { throw new EHttpClientException('Adapter does not support streaming'); } // Open the connection, send the request and read the response $this->adapter->connect($uri->getHost(), $uri->getPort(), $uri->getScheme() == 'https' ? true : false); if ($this->config['output_stream']) { if ($this->adapter instanceof EHttpClientAdapterStream) { $stream = $this->_openTempStream(); $this->adapter->setOutputStream($stream); } else { throw new EHttpClientException(Yii::t('EHttpClient', 'Adapter does not support streaming')); } } $this->last_request = $this->adapter->write($this->method, $uri, $this->config['httpversion'], $headers, $body); $response = $this->adapter->read(); if (!$response) { throw new EHttpClientException(Yii::t('EHttpClient', 'Unable to read response, or response is empty')); } if ($this->config['output_stream']) { $streamMetaData = stream_get_meta_data($stream); if ($streamMetaData['seekable']) { rewind($stream); } // cleanup the adapter $this->adapter->setOutputStream(null); $response = EHttpResponseStream::fromStream($response, $stream); $response->setStreamName($this->_stream_name); if (!is_string($this->config['output_stream'])) { // we used temp name, will need to clean up $response->setCleanup(true); } } else { $response = EHttpResponse::fromString($response); } if ($this->config['storeresponse']) { $this->last_response = $response; } // Load cookies into cookie jar if (isset($this->cookiejar)) { $this->cookiejar->addCookiesFromResponse($response, $uri, $this->config['encodecookies']); } // If we got redirected, look for the Location header if ($response->isRedirect() && ($location = $response->getHeader('location'))) { // Avoid problems with buggy servers that add whitespace at the // end of some headers (See ZF-11283) $location = trim($location); // Check whether we send the exact same request again, or drop the parameters // and send a GET request if ($response->getStatus() == 303 || !$this->config['strictredirects'] && ($response->getStatus() == 302 || $response->getStatus() == 301)) { $this->resetParameters(); $this->setMethod(self::GET); } // If we got a well formed absolute URI if (EUriHttp::check($location)) { $this->setHeaders('host', null); $this->setUri($location); } else { // Split into path and query and set the query if (strpos($location, '?') !== false) { list($location, $query) = explode('?', $location, 2); } else { $query = ''; } $this->uri->setQuery($query); // Else, if we got just an absolute path, set it if (strpos($location, '/') === 0) { $this->uri->setPath($location); // Else, assume we have a relative path } else { // Get the current path directory, removing any trailing slashes $path = $this->uri->getPath(); $path = rtrim(substr($path, 0, strrpos($path, '/')), "/"); $this->uri->setPath($path . '/' . $location); } } ++$this->redirectCounter; } else { // If we didn't get any location, stop redirecting break; } } while ($this->redirectCounter < $this->config['maxredirects']); return $response; }
/** * Send request to the remote server * * @param string $method * @param EUriHttp $uri * @param string $http_ver * @param array $headers * @param string $body * @return string Request as string */ public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '') { // Make sure we're properly connected if (!$this->socket) { throw new EHttpClientException(Yii::t('EHttpClient', 'Trying to write but we are not connected')); } $host = $uri->getHost(); $host = (strtolower($uri->getScheme()) == 'https' ? $this->config['ssltransport'] : 'tcp') . '://' . $host; if ($this->connected_to[0] != $host || $this->connected_to[1] != $uri->getPort()) { throw new EHttpClientException(Yii::t('EHttpClient', 'Trying to write but we are connected to the wrong host')); } // Save request method for later $this->method = $method; // Build request headers $path = $uri->getPath(); if ($uri->getQuery()) { $path .= '?' . $uri->getQuery(); } $request = "{$method} {$path} HTTP/{$http_ver}\r\n"; foreach ($headers as $k => $v) { if (is_string($k)) { $v = ucfirst($k) . ": {$v}"; } $request .= "{$v}\r\n"; } // Add the request body $request .= "\r\n" . $body; // Send the request if (!@fwrite($this->socket, $request)) { throw new EHttpClientException(Yii::t('EHttpClient', 'Error writing request to server')); } return $request; }