/** * Sends the request and returns the response * * @throws HTTP_Request2_Exception * @return HTTP_Request2_Response */ public function send() { // Sanity check for URL if (!$this->url instanceof Net_URL2) { throw new HTTP_Request2_Exception('No URL given'); } elseif (!$this->url->isAbsolute()) { throw new HTTP_Request2_Exception('Absolute URL required'); } elseif (!in_array(strtolower($this->url->getScheme()), array('https', 'http'))) { throw new HTTP_Request2_Exception('Not a HTTP URL'); } if (empty($this->adapter)) { $this->setAdapter($this->getConfig('adapter')); } // magic_quotes_runtime may break file uploads and chunked response // processing; see bug #4543 if ($magicQuotes = ini_get('magic_quotes_runtime')) { ini_set('magic_quotes_runtime', false); } // force using single byte encoding if mbstring extension overloads // strlen() and substr(); see bug #1781, bug #10605 if (extension_loaded('mbstring') && 2 & ini_get('mbstring.func_overload')) { $oldEncoding = mb_internal_encoding(); mb_internal_encoding('iso-8859-1'); } try { $response = $this->adapter->sendRequest($this); } catch (Exception $e) { } // cleanup in either case (poor man's "finally" clause) if ($magicQuotes) { ini_set('magic_quotes_runtime', true); } if (!empty($oldEncoding)) { mb_internal_encoding($oldEncoding); } // rethrow the exception if (!empty($e)) { throw $e; } return $response; }
/** * Handles HTTP redirection * * This method will throw an Exception if redirect to a non-HTTP(S) location * is attempted, also if number of redirects performed already is equal to * 'max_redirects' configuration parameter. * * @param HTTP_Request2 Original request * @param HTTP_Request2_Response Response containing redirect * @return HTTP_Request2_Response Response from a new location * @throws HTTP_Request2_Exception */ protected function handleRedirect(HTTP_Request2 $request, HTTP_Request2_Response $response) { if (is_null($this->redirectCountdown)) { $this->redirectCountdown = $request->getConfig('max_redirects'); } if (0 == $this->redirectCountdown) { $this->redirectCountdown = null; // Copying cURL behaviour throw new HTTP_Request2_MessageException('Maximum (' . $request->getConfig('max_redirects') . ') redirects followed', HTTP_Request2_Exception::TOO_MANY_REDIRECTS); } $redirectUrl = new Net_URL2($response->getHeader('location'), array(Net_URL2::OPTION_USE_BRACKETS => $request->getConfig('use_brackets'))); // refuse non-HTTP redirect if ($redirectUrl->isAbsolute() && !in_array($redirectUrl->getScheme(), array('http', 'https'))) { $this->redirectCountdown = null; throw new HTTP_Request2_MessageException('Refusing to redirect to a non-HTTP URL ' . $redirectUrl->__toString(), HTTP_Request2_Exception::NON_HTTP_REDIRECT); } // Theoretically URL should be absolute (see http://tools.ietf.org/html/rfc2616#section-14.30), // but in practice it is often not if (!$redirectUrl->isAbsolute()) { $redirectUrl = $request->getUrl()->resolve($redirectUrl); } $redirect = clone $request; $redirect->setUrl($redirectUrl); if (303 == $response->getStatus() || !$request->getConfig('strict_redirects') && in_array($response->getStatus(), array(301, 302))) { $redirect->setMethod(HTTP_Request2::METHOD_GET); $redirect->setBody(''); } if (0 < $this->redirectCountdown) { $this->redirectCountdown--; } return $this->sendRequest($redirect); }
/** * Sends the request and returns the response * * @throws HTTP_Request2_Exception * @return HTTP_Request2_Response */ public function send() { // Sanity check for URL if (!$this->url instanceof Net_URL2 || !$this->url->isAbsolute() || !in_array(strtolower($this->url->getScheme()), array('https', 'http'))) { throw new HTTP_Request2_LogicException('HTTP_Request2 needs an absolute HTTP(S) request URL, ' . ($this->url instanceof Net_URL2 ? "'" . $this->url->__toString() . "'" : 'none') . ' given', HTTP_Request2_Exception::INVALID_ARGUMENT); } if (empty($this->adapter)) { $this->setAdapter($this->getConfig('adapter')); } // magic_quotes_runtime may break file uploads and chunked response // processing; see bug #4543. Don't use ini_get() here; see bug #16440. if ($magicQuotes = get_magic_quotes_runtime()) { set_magic_quotes_runtime(false); } // force using single byte encoding if mbstring extension overloads // strlen() and substr(); see bug #1781, bug #10605 if (extension_loaded('mbstring') && 2 & ini_get('mbstring.func_overload')) { $oldEncoding = mb_internal_encoding(); mb_internal_encoding('8bit'); } try { $response = $this->adapter->sendRequest($this); } catch (Exception $e) { } // cleanup in either case (poor man's "finally" clause) if ($magicQuotes) { set_magic_quotes_runtime(true); } if (!empty($oldEncoding)) { mb_internal_encoding($oldEncoding); } // rethrow the exception if (!empty($e)) { throw $e; } return $response; }
/** * Callback function called by cURL for saving the response headers * * @param resource cURL handle * @param string response header (with trailing CRLF) * @return integer number of bytes saved * @see HTTP_Request2_Response::parseHeaderLine() */ protected function callbackWriteHeader($ch, $string) { // we may receive a second set of headers if doing e.g. digest auth if ($this->eventReceivedHeaders || !$this->eventSentHeaders) { // don't bother with 100-Continue responses (bug #15785) if (!$this->eventSentHeaders || $this->response->getStatus() >= 200) { $this->request->setLastEvent('sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT)); } $upload = curl_getinfo($ch, CURLINFO_SIZE_UPLOAD); // if body wasn't read by a callback, send event with total body size if ($upload > $this->position) { $this->request->setLastEvent('sentBodyPart', $upload - $this->position); $this->position = $upload; } if ($upload && (!$this->eventSentHeaders || $this->response->getStatus() >= 200)) { $this->request->setLastEvent('sentBody', $upload); } $this->eventSentHeaders = true; // we'll need a new response object if ($this->eventReceivedHeaders) { $this->eventReceivedHeaders = false; $this->response = null; } } if (empty($this->response)) { $this->response = new HTTP_Request2_Response($string, false); } else { $this->response->parseHeaderLine($string); if ('' == trim($string)) { // don't bother with 100-Continue responses (bug #15785) if (200 <= $this->response->getStatus()) { $this->request->setLastEvent('receivedHeaders', $this->response); } if ($this->request->getConfig('follow_redirects') && $this->response->isRedirect()) { $redirectUrl = new Net_URL2($this->response->getHeader('location')); // for versions lower than 5.2.10, check the redirection URL protocol if (!defined('CURLOPT_REDIR_PROTOCOLS') && $redirectUrl->isAbsolute() && !in_array($redirectUrl->getScheme(), array('http', 'https'))) { return -1; } if ($jar = $this->request->getCookieJar()) { $jar->addCookiesFromResponse($this->response, $this->request->getUrl()); if (!$redirectUrl->isAbsolute()) { $redirectUrl = $this->request->getUrl()->resolve($redirectUrl); } if ($cookies = $jar->getMatching($redirectUrl, true)) { curl_setopt($ch, CURLOPT_COOKIE, $cookies); } } } $this->eventReceivedHeaders = true; } } return strlen($string); }
/** * Resolve a possibly relative URL against some absolute base URL * @param string $rel relative or absolute URL * @param string $base absolute URL * @return string absolute URL, or original URL if could not be resolved. */ function resolveURI($rel, $base) { require_once "Net/URL2.php"; try { $relUrl = new Net_URL2($rel); if ($relUrl->isAbsolute()) { return $rel; } $baseUrl = new Net_URL2($base); $absUrl = $baseUrl->resolve($relUrl); return $absUrl->getURL(); } catch (Exception $e) { common_log(LOG_WARNING, 'Unable to resolve relative link "' . $rel . '" against base "' . $base . '": ' . $e->getMessage()); return $rel; } }
/** * Property setter interceptor to handle "host" special case. * * @param string $property The property to retrieve * @param mixed $value The property value * * @return void */ public function __set($property, $value) { switch ($property) { case 'uri': if ($value == null) { return; } $this->_uri = new Net_URL2($value); $this->_uri->normalize(); // if we have a proxy set, URI must be absolute if ($this->_proxy !== null && !$this->_uri->isAbsolute()) { throw new HTTP_Request2_Exception('URI must be absolute when using a proxy'); } // if we use HTTP/1.0, URI must also be absolute if ($this->httpVersion == self::HTTP_VERSION_1_0 && !$this->_uri->isAbsolute()) { throw new HTTP_Request2_Exception('URI must be absolute when using ' . self::HTTP_VERSION_1_0); } break; case 'proxy': // if we have a proxy set, URI must be absolute if ($this->_uri !== null && !$this->_uri->isAbsolute()) { throw new HTTP_Request2_Exception('URI must be absolute when using a proxy'); } $this->_proxy = $value; break; case 'connection': if (!$value instanceof HTTP_Connection) { throw new HTTP_Request2_Exception('connection must be an instance of HTTP_Connection'); } $this->_connection = $value; break; case 'path': case 'host': case 'port': throw new HTTP_Request2_Exception('"' . $property . '" is a read-only property'); } }
/** * This is a regression test to test that recovering from * a wrongly encoded URL is possible. * * It was requested as Request #19684 on 2011-12-31 02:07 UTC * that redirects containing spaces should work. * * @return void */ public function test19684() { // Location: URL obtained Thu, 25 Apr 2013 20:51:31 GMT $urlWithSpace = 'http://www.sigmaaldrich.com/catalog/search?interface=CAS N' . 'o.&term=108-88-3&lang=en®ion=US&mode=match+partialmax&N=0+2200030' . '48+219853269+219853286'; $urlCorrect = 'http://www.sigmaaldrich.com/catalog/search?interface=CAS%20N' . 'o.&term=108-88-3&lang=en®ion=US&mode=match+partialmax&N=0+2200030' . '48+219853269+219853286'; $url = new Net_URL2($urlWithSpace); $this->assertTrue($url->isAbsolute()); $urlPart = parse_url($urlCorrect, PHP_URL_PATH); $this->assertSame($urlPart, $url->getPath()); $urlPart = parse_url($urlCorrect, PHP_URL_QUERY); $this->assertSame($urlPart, $url->getQuery()); $this->assertSame($urlCorrect, (string) $url); $input = 'http://example.com/get + + to my nose/'; $expected = 'http://example.com/get%20+%20+%20to%20my%20nose/'; $actual = new Net_URL2($input); $this->assertEquals($expected, $actual); $actual->normalize(); }