We are no longer offering accounts on this server. Consider https://gitlab.freedesktop.org/ as a place to host projects.

Commit b22fc5b7 authored by Brion Vibber's avatar Brion Vibber

Revert "Rebuilt HTTPClient class as an extension of PEAR HTTP_Request2...

Revert "Rebuilt HTTPClient class as an extension of PEAR HTTP_Request2 package, adding redirect handling and convenience functions."
Going to restructure a little more before finalizing this...

This reverts commit fa379678.
parent d8e2d76b
......@@ -47,15 +47,18 @@ class File_redirection extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
static function _commonHttp($url, $redirs) {
$request = new HTTPClient($url);
$request->setConfig(array(
'connect_timeout' => 10, // # seconds to wait
'max_redirs' => $redirs, // # max number of http redirections to follow
'follow_redirects' => true, // Follow redirects
'store_body' => false, // We won't need body content here.
));
return $request;
function _commonCurl($url, $redirs) {
$curlh = curl_init();
curl_setopt($curlh, CURLOPT_URL, $url);
curl_setopt($curlh, CURLOPT_AUTOREFERER, true); // # setup referer header when folowing redirects
curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 10); // # seconds to wait
curl_setopt($curlh, CURLOPT_MAXREDIRS, $redirs); // # max number of http redirections to follow
curl_setopt($curlh, CURLOPT_USERAGENT, USER_AGENT);
curl_setopt($curlh, CURLOPT_FOLLOWLOCATION, true); // Follow redirects
curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curlh, CURLOPT_FILETIME, true);
curl_setopt($curlh, CURLOPT_HEADER, true); // Include header in output
return $curlh;
}
function _redirectWhere_imp($short_url, $redirs = 10, $protected = false) {
......@@ -79,39 +82,32 @@ class File_redirection extends Memcached_DataObject
if(strpos($short_url,'://') === false){
return $short_url;
}
try {
$request = self::_commonHttp($short_url, $redirs);
// Don't include body in output
$request->setMethod(HTTP_Request2::METHOD_HEAD);
$response = $request->send();
if (405 == $response->getCode()) {
// Server doesn't support HEAD method? Can this really happen?
// We'll try again as a GET and ignore the response data.
$request = self::_commonHttp($short_url, $redirs);
$response = $request->send();
}
} catch (Exception $e) {
// Invalid URL or failure to reach server
return $short_url;
$curlh = File_redirection::_commonCurl($short_url, $redirs);
// Don't include body in output
curl_setopt($curlh, CURLOPT_NOBODY, true);
curl_exec($curlh);
$info = curl_getinfo($curlh);
curl_close($curlh);
if (405 == $info['http_code']) {
$curlh = File_redirection::_commonCurl($short_url, $redirs);
curl_exec($curlh);
$info = curl_getinfo($curlh);
curl_close($curlh);
}
if ($response->getRedirectCount() && File::isProtected($response->getUrl())) {
// Bump back up the redirect chain until we find a non-protected URL
return self::_redirectWhere_imp($short_url, $response->getRedirectCount() - 1, true);
if (!empty($info['redirect_count']) && File::isProtected($info['url'])) {
return File_redirection::_redirectWhere_imp($short_url, $info['redirect_count'] - 1, true);
}
$ret = array('code' => $response->getCode()
, 'redirects' => $response->getRedirectCount()
, 'url' => $response->getUrl());
$ret = array('code' => $info['http_code']
, 'redirects' => $info['redirect_count']
, 'url' => $info['url']);
$type = $response->getHeader('Content-Type');
if ($type) $ret['type'] = $type;
if (!empty($info['content_type'])) $ret['type'] = $info['content_type'];
if ($protected) $ret['protected'] = true;
$size = $request->getHeader('Content-Length'); // @fixme bytes?
if ($size) $ret['size'] = $size;
$time = $request->getHeader('Last-Modified');
if ($time) $ret['time'] = strtotime($time);
if (!empty($info['download_content_length'])) $ret['size'] = $info['download_content_length'];
if (isset($info['filetime']) && ($info['filetime'] > 0)) $ret['time'] = $info['filetime'];
return $ret;
}
......
This diff is collapsed.
<?php
/**
* Base class for HTTP_Request2 adapters
*
* PHP version 5
*
* LICENSE:
*
* Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * The names of the authors may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @category HTTP
* @package HTTP_Request2
* @author Alexey Borzov <avb@php.net>
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: Adapter.php 274684 2009-01-26 23:07:27Z avb $
* @link http://pear.php.net/package/HTTP_Request2
*/
/**
* Class representing a HTTP response
*/
require_once 'HTTP/Request2/Response.php';
/**
* Base class for HTTP_Request2 adapters
*
* HTTP_Request2 class itself only defines methods for aggregating the request
* data, all actual work of sending the request to the remote server and
* receiving its response is performed by adapters.
*
* @category HTTP
* @package HTTP_Request2
* @author Alexey Borzov <avb@php.net>
* @version Release: 0.4.1
*/
abstract class HTTP_Request2_Adapter
{
/**
* A list of methods that MUST NOT have a request body, per RFC 2616
* @var array
*/
protected static $bodyDisallowed = array('TRACE');
/**
* Methods having defined semantics for request body
*
* Content-Length header (indicating that the body follows, section 4.3 of
* RFC 2616) will be sent for these methods even if no body was added
*
* @var array
* @link http://pear.php.net/bugs/bug.php?id=12900
* @link http://pear.php.net/bugs/bug.php?id=14740
*/
protected static $bodyRequired = array('POST', 'PUT');
/**
* Request being sent
* @var HTTP_Request2
*/
protected $request;
/**
* Request body
* @var string|resource|HTTP_Request2_MultipartBody
* @see HTTP_Request2::getBody()
*/
protected $requestBody;
/**
* Length of the request body
* @var integer
*/
protected $contentLength;
/**
* Sends request to the remote server and returns its response
*
* @param HTTP_Request2
* @return HTTP_Request2_Response
* @throws HTTP_Request2_Exception
*/
abstract public function sendRequest(HTTP_Request2 $request);
/**
* Calculates length of the request body, adds proper headers
*
* @param array associative array of request headers, this method will
* add proper 'Content-Length' and 'Content-Type' headers
* to this array (or remove them if not needed)
*/
protected function calculateRequestLength(&$headers)
{
$this->requestBody = $this->request->getBody();
if (is_string($this->requestBody)) {
$this->contentLength = strlen($this->requestBody);
} elseif (is_resource($this->requestBody)) {
$stat = fstat($this->requestBody);
$this->contentLength = $stat['size'];
rewind($this->requestBody);
} else {
$this->contentLength = $this->requestBody->getLength();
$headers['content-type'] = 'multipart/form-data; boundary=' .
$this->requestBody->getBoundary();
$this->requestBody->rewind();
}
if (in_array($this->request->getMethod(), self::$bodyDisallowed) ||
0 == $this->contentLength
) {
unset($headers['content-type']);
// No body: send a Content-Length header nonetheless (request #12900),
// but do that only for methods that require a body (bug #14740)
if (in_array($this->request->getMethod(), self::$bodyRequired)) {
$headers['content-length'] = 0;
} else {
unset($headers['content-length']);
}
} else {
if (empty($headers['content-type'])) {
$headers['content-type'] = 'application/x-www-form-urlencoded';
}
$headers['content-length'] = $this->contentLength;
}
}
}
?>
<?php
/**
* Adapter for HTTP_Request2 wrapping around cURL extension
*
* PHP version 5
*
* LICENSE:
*
* Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * The names of the authors may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @category HTTP
* @package HTTP_Request2
* @author Alexey Borzov <avb@php.net>
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: Curl.php 278226 2009-04-03 21:32:48Z avb $
* @link http://pear.php.net/package/HTTP_Request2
*/
/**
* Base class for HTTP_Request2 adapters
*/
require_once 'HTTP/Request2/Adapter.php';
/**
* Adapter for HTTP_Request2 wrapping around cURL extension
*
* @category HTTP
* @package HTTP_Request2
* @author Alexey Borzov <avb@php.net>
* @version Release: 0.4.1
*/
class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter
{
/**
* Mapping of header names to cURL options
* @var array
*/
protected static $headerMap = array(
'accept-encoding' => CURLOPT_ENCODING,
'cookie' => CURLOPT_COOKIE,
'referer' => CURLOPT_REFERER,
'user-agent' => CURLOPT_USERAGENT
);
/**
* Mapping of SSL context options to cURL options
* @var array
*/
protected static $sslContextMap = array(
'ssl_verify_peer' => CURLOPT_SSL_VERIFYPEER,
'ssl_cafile' => CURLOPT_CAINFO,
'ssl_capath' => CURLOPT_CAPATH,
'ssl_local_cert' => CURLOPT_SSLCERT,
'ssl_passphrase' => CURLOPT_SSLCERTPASSWD
);
/**
* Response being received
* @var HTTP_Request2_Response
*/
protected $response;
/**
* Whether 'sentHeaders' event was sent to observers
* @var boolean
*/
protected $eventSentHeaders = false;
/**
* Whether 'receivedHeaders' event was sent to observers
* @var boolean
*/
protected $eventReceivedHeaders = false;
/**
* Position within request body
* @var integer
* @see callbackReadBody()
*/
protected $position = 0;
/**
* Information about last transfer, as returned by curl_getinfo()
* @var array
*/
protected $lastInfo;
/**
* Sends request to the remote server and returns its response
*
* @param HTTP_Request2
* @return HTTP_Request2_Response
* @throws HTTP_Request2_Exception
*/
public function sendRequest(HTTP_Request2 $request)
{
if (!extension_loaded('curl')) {
throw new HTTP_Request2_Exception('cURL extension not available');
}
$this->request = $request;
$this->response = null;
$this->position = 0;
$this->eventSentHeaders = false;
$this->eventReceivedHeaders = false;
try {
if (false === curl_exec($ch = $this->createCurlHandle())) {
$errorMessage = 'Error sending request: #' . curl_errno($ch) .
' ' . curl_error($ch);
}
} catch (Exception $e) {
}
$this->lastInfo = curl_getinfo($ch);
curl_close($ch);
if (!empty($e)) {
throw $e;
} elseif (!empty($errorMessage)) {
throw new HTTP_Request2_Exception($errorMessage);
}
if (0 < $this->lastInfo['size_download']) {
$this->request->setLastEvent('receivedBody', $this->response);
}
return $this->response;
}
/**
* Returns information about last transfer
*
* @return array associative array as returned by curl_getinfo()
*/
public function getInfo()
{
return $this->lastInfo;
}
/**
* Creates a new cURL handle and populates it with data from the request
*
* @return resource a cURL handle, as created by curl_init()
* @throws HTTP_Request2_Exception
*/
protected function createCurlHandle()
{
$ch = curl_init();
curl_setopt_array($ch, array(
// setup callbacks
CURLOPT_READFUNCTION => array($this, 'callbackReadBody'),
CURLOPT_HEADERFUNCTION => array($this, 'callbackWriteHeader'),
CURLOPT_WRITEFUNCTION => array($this, 'callbackWriteBody'),
// disallow redirects
CURLOPT_FOLLOWLOCATION => false,
// buffer size
CURLOPT_BUFFERSIZE => $this->request->getConfig('buffer_size'),
// connection timeout
CURLOPT_CONNECTTIMEOUT => $this->request->getConfig('connect_timeout'),
// save full outgoing headers, in case someone is interested
CURLINFO_HEADER_OUT => true,
// request url
CURLOPT_URL => $this->request->getUrl()->getUrl()
));
// request timeout
if ($timeout = $this->request->getConfig('timeout')) {
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
}
// set HTTP version
switch ($this->request->getConfig('protocol_version')) {
case '1.0':
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
break;
case '1.1':
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
}
// set request method
switch ($this->request->getMethod()) {
case HTTP_Request2::METHOD_GET:
curl_setopt($ch, CURLOPT_HTTPGET, true);
break;
case HTTP_Request2::METHOD_POST:
curl_setopt($ch, CURLOPT_POST, true);
break;
default:
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->request->getMethod());
}
// set proxy, if needed
if ($host = $this->request->getConfig('proxy_host')) {
if (!($port = $this->request->getConfig('proxy_port'))) {
throw new HTTP_Request2_Exception('Proxy port not provided');
}
curl_setopt($ch, CURLOPT_PROXY, $host . ':' . $port);
if ($user = $this->request->getConfig('proxy_user')) {
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $user . ':' .
$this->request->getConfig('proxy_password'));
switch ($this->request->getConfig('proxy_auth_scheme')) {
case HTTP_Request2::AUTH_BASIC:
curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
break;
case HTTP_Request2::AUTH_DIGEST:
curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST);
}
}
}
// set authentication data
if ($auth = $this->request->getAuth()) {
curl_setopt($ch, CURLOPT_USERPWD, $auth['user'] . ':' . $auth['password']);
switch ($auth['scheme']) {
case HTTP_Request2::AUTH_BASIC:
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
break;
case HTTP_Request2::AUTH_DIGEST:
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
}
}
// set SSL options
if (0 == strcasecmp($this->request->getUrl()->getScheme(), 'https')) {
foreach ($this->request->getConfig() as $name => $value) {
if ('ssl_verify_host' == $name && null !== $value) {
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $value? 2: 0);
} elseif (isset(self::$sslContextMap[$name]) && null !== $value) {
curl_setopt($ch, self::$sslContextMap[$name], $value);
}
}
}
$headers = $this->request->getHeaders();
// make cURL automagically send proper header
if (!isset($headers['accept-encoding'])) {
$headers['accept-encoding'] = '';
}
// set headers having special cURL keys
foreach (self::$headerMap as $name => $option) {
if (isset($headers[$name])) {
curl_setopt($ch, $option, $headers[$name]);
unset($headers[$name]);
}
}
$this->calculateRequestLength($headers);
// set headers not having special keys
$headersFmt = array();
foreach ($headers as $name => $value) {
$canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
$headersFmt[] = $canonicalName . ': ' . $value;
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $headersFmt);
return $ch;
}
/**
* Callback function called by cURL for reading the request body
*
* @param resource cURL handle
* @param resource file descriptor (not used)
* @param integer maximum length of data to return
* @return string part of the request body, up to $length bytes
*/
protected function callbackReadBody($ch, $fd, $length)
{
if (!$this->eventSentHeaders) {
$this->request->setLastEvent(
'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT)
);
$this->eventSentHeaders = true;
}
if (in_array($this->request->getMethod(), self::$bodyDisallowed) ||
0 == $this->contentLength || $this->position >= $this->contentLength
) {
return '';
}
if (is_string($this->requestBody)) {
$string = substr($this->requestBody, $this->position, $length);
} elseif (is_resource($this->requestBody)) {
$string = fread($this->requestBody, $length);
} else {
$string = $this->requestBody->read($length);
}
$this->request->setLastEvent('sentBodyPart', strlen($string));
$this->position += strlen($string);
return $string;
}
/**
* 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)
);
}
$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);
}
$this->eventReceivedHeaders = true;
}
}
return strlen($string);
}
/**
* Callback function called by cURL for saving the response body
*
* @param resource cURL handle (not used)
* @param string part of the response body
* @return integer number of bytes saved
* @see HTTP_Request2_Response::appendBody()
*/
protected function callbackWriteBody($ch, $string)
{
// cURL calls WRITEFUNCTION callback without calling HEADERFUNCTION if
// response doesn't start with proper HTTP status line (see bug #15716)
if (empty($this->response)) {
throw new HTTP_Request2_Exception("Malformed response: {$string}");
}
if ($this->request->getConfig('store_body')) {
$this->response->appendBody($string);
}
$this->request->setLastEvent('receivedBodyPart', $string);
return strlen($string);
}
}
?>