5, 'useragent' => 'EasyRdf_Http_Client', 'timeout' => 10 ); /** * Request URI * * @var string */ private $uri = null; /** * Associative array of request headers * * @var array */ private $headers = array(); /** * HTTP request method * * @var string */ private $method = 'GET'; /** * Associative array of GET parameters * * @var array */ private $paramsGet = array(); /** * The raw post data to send. Could be set by setRawData($data). * * @var string */ private $rawPostData = null; /** * Redirection counter * * @var int */ private $redirectCounter = 0; /** * Constructor method. Will create a new HTTP client. Accepts the target * URL and optionally configuration array. * * @param string $uri * @param array $config Configuration key-value pairs. */ public function __construct($uri = null, $config = null) { if ($uri !== null) { $this->setUri($uri); } if ($config !== null) { $this->setConfig($config); } } /** * Set the URI for the next request * * @param string $uri * @return EasyRdf_Http_Client */ public function setUri($uri) { if (!is_string($uri)) { $uri = strval($uri); } if (!preg_match('/^http(s?):/', $uri)) { throw new InvalidArgumentException( "EasyRdf_Http_Client only supports the 'http' and 'https' schemes." ); } $this->uri = $uri; return $this; } /** * Get the URI for the next request * * @return string */ public function getUri($asString = true) { return $this->uri; } /** * Set configuration parameters for this HTTP client * * @param array $config * @return EasyRdf_Http_Client * @throws InvalidArgumentException */ public function setConfig($config = array()) { if ($config == null or !is_array($config)) { throw new InvalidArgumentException( "\$config should be an array and cannot be null" ); } foreach ($config as $k => $v) { $this->config[strtolower($k)] = $v; } return $this; } /** * Set a request header * * @param string $name Header name (e.g. 'Accept') * @param string $value Header value or null * @return EasyRdf_Http_Client */ public function setHeaders($name, $value = null) { $normalizedName = strtolower($name); // If $value is null or false, unset the header if ($value === null || $value === false) { unset($this->headers[$normalizedName]); } else { // Else, set the header $this->headers[$normalizedName] = array($name, $value); } return $this; } /** * Set the next request's method * * Validated the passed method and sets it. * * @param string $method * @return EasyRdf_Http_Client * @throws InvalidArgumentException */ public function setMethod($method) { if (!is_string($method) or !preg_match('/^[A-Z]+$/', $method)) { throw new InvalidArgumentException("Invalid HTTP request method."); } $this->method = $method; return $this; } /** * Get the method for the next request * * @return string */ public function getMethod() { return $this->method; } /** * Get the value of a specific header * * Note that if the header has more than one value, an array * will be returned. * * @param string $key * @return string|array|null The header value or null if it is not set */ public function getHeader($key) { $key = strtolower($key); if (isset($this->headers[$key])) { return $this->headers[$key][1]; } else { return null; } } /** * Set a GET parameter for the request. * * @param string $name * @param string $value * @return EasyRdf_Http_Client */ public function setParameterGet($name, $value = null) { if ($value === null) { if (isset($this->paramsGet[$name])) { unset($this->paramsGet[$name]); } } else { $this->paramsGet[$name] = $value; } return $this; } /** * Get a GET parameter for the request. * * @param string $name * @return string value */ public function getParameterGet($name) { if (isset($this->paramsGet[$name])) { return $this->paramsGet[$name]; } else { return null; } } /** * Get all the GET parameters * * @return array */ public function getParametersGet() { return $this->paramsGet; } /** * Get the number of redirections done on the last request * * @return int */ public function getRedirectionsCount() { return $this->redirectCounter; } /** * Set the raw (already encoded) POST data. * * This function is here for two reasons: * 1. For advanced user who would like to set their own data, already encoded * 2. For backwards compatibilty: If someone uses the old post($data) method. * this method will be used to set the encoded data. * * $data can also be stream (such as file) from which the data will be read. * * @param string|resource $data * @return Zend_Http_Client */ public function setRawData($data) { $this->rawPostData = $data; return $this; } /** * Get the raw (already encoded) POST data. * * @return string */ public function getRawData() { return $this->rawPostData; } /** * Clear all GET and POST parameters * * Should be used to reset the request parameters if the client is * used for several concurrent requests. * * clearAll parameter controls if we clean just parameters or also * headers * * @param bool $clearAll Should all data be cleared? * @return EasyRdf_Http_Client */ public function resetParameters($clearAll = false) { // Reset parameter data $this->paramsGet = array(); $this->rawPostData = null; $this->method = 'GET'; if ($clearAll) { $this->headers = array(); } else { // Clear outdated headers if (isset($this->headers['content-type'])) { unset($this->headers['content-type']); } if (isset($this->headers['content-length'])) { unset($this->headers['content-length']); } } return $this; } /** * Send the HTTP request and return an HTTP response object * * @return EasyRdf_Http_Response * @throws EasyRdf_Exception */ public function request($method = null) { if (!$this->uri) { throw new EasyRdf_Exception( "Set URI before calling EasyRdf_Http_Client->request()" ); } if ($method) { $this->setMethod($method); } $this->redirectCounter = 0; $response = null; // Send the first request. If redirected, continue. do { // Clone the URI and add the additional GET parameters to it $uri = parse_url($this->uri); if ($uri['scheme'] === 'http') { $host = $uri['host']; } elseif ($uri['scheme'] === 'https') { $host = 'ssl://'.$uri['host']; } else { throw new EasyRdf_Exception( "Unsupported URI scheme: ".$uri['scheme'] ); } if (isset($uri['port'])) { $port = $uri['port']; } else { if ($uri['scheme'] === 'https') { $port = 443; } else { $port = 80; } } if (!empty($this->paramsGet)) { if (!empty($uri['query'])) { $uri['query'] .= '&'; } else { $uri['query'] = ''; } $uri['query'] .= http_build_query($this->paramsGet, null, '&'); } $headers = $this->prepareHeaders($uri['host'], $port); // Open socket to remote server $socket = @fsockopen($host, $port, $errno, $errstr, $this->config['timeout']); if (!$socket) { throw new EasyRdf_Exception("Unable to connect to $host:$port ($errstr)"); } // Write the request $path = $uri['path']; if (empty($path)) { $path = '/'; } if (isset($uri['query'])) { $path .= '?' . $uri['query']; } fwrite($socket, "{$this->method} {$path} HTTP/1.1\r\n"); foreach ($headers as $k => $v) { if (is_string($k)) { $v = ucfirst($k) . ": $v"; } fwrite($socket, "$v\r\n"); } fwrite($socket, "\r\n"); // Send the request body, if there is one set if (isset($this->rawPostData)) { fwrite($socket, $this->rawPostData); } // Read in the response $content = ''; while (!feof($socket)) { $content .= fgets($socket); } // FIXME: support HTTP/1.1 100 Continue // Close the socket @fclose($socket); // Parse the response string $response = EasyRdf_Http_Response::fromString($content); // 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); // Some servers return relative URLs in the location header // resolve it in relation to previous request $baseUri = new EasyRdf_ParsedUri($this->uri); $location = $baseUri->resolve($location)->toString(); // If it is a 303 then drop the parameters and send a GET request if ($response->getStatus() == 303) { $this->resetParameters(); $this->setMethod('GET'); } // If we got a well formed absolute URI if (parse_url($location)) { $this->setHeaders('host', null); $this->setUri($location); } else { throw new EasyRdf_Exception( "Failed to parse Location header returned by ". $this->uri ); } ++$this->redirectCounter; } else { // If we didn't get any location, stop redirecting break; } } while ($this->redirectCounter < $this->config['maxredirects']); return $response; } /** * Prepare the request headers * * @ignore * @return array */ protected function prepareHeaders($host, $port) { $headers = array(); // Set the host header if (! isset($this->headers['host'])) { // If the port is not default, add it if ($port !== 80 and $port !== 443) { $host .= ':' . $port; } $headers[] = "Host: {$host}"; } // Set the connection header if (! isset($this->headers['connection'])) { $headers[] = "Connection: close"; } // Set the user agent header if (! isset($this->headers['user-agent'])) { $headers[] = "User-Agent: {$this->config['useragent']}"; } // If we have rawPostData set, set the content-length header if (isset($this->rawPostData)) { $headers[] = "Content-Length: ".strlen($this->rawPostData); } // Add all other user defined headers foreach ($this->headers as $header) { list($name, $value) = $header; if (is_array($value)) { $value = implode(', ', $value); } $headers[] = "$name: $value"; } return $headers; } }