Skip to content

Commit

Permalink
Merge pull request YOURLS#1998 from YOURLS/pr1824
Browse files Browse the repository at this point in the history
Dont fetch all page to get title. Fixes YOURLS#1824
  • Loading branch information
ozh committed Dec 5, 2015
2 parents ef33fef + f9c4431 commit d51c6cb
Show file tree
Hide file tree
Showing 23 changed files with 1,844 additions and 1,610 deletions.
134 changes: 93 additions & 41 deletions includes/Requests/Requests.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,20 @@ class Requests {
*/
const DELETE = 'DELETE';

/**
* OPTIONS method
*
* @var string
*/
const OPTIONS = 'OPTIONS';

/**
* TRACE method
*
* @var string
*/
const TRACE = 'TRACE';

/**
* PATCH method
*
Expand Down Expand Up @@ -176,8 +190,9 @@ protected static function get_transport($capabilities = array()) {

// Find us a working transport
foreach (self::$transports as $class) {
if (!class_exists($class))
if (!class_exists($class)) {
continue;
}

$result = call_user_func(array($class, 'test'), $capabilities);
if ($result) {
Expand All @@ -188,7 +203,7 @@ protected static function get_transport($capabilities = array()) {
if (self::$transport[$cap_string] === null) {
throw new Requests_Exception('No working transports found', 'notransport', self::$transports);
}

return new self::$transport[$cap_string]();
}

Expand Down Expand Up @@ -219,6 +234,13 @@ public static function head($url, $headers = array(), $options = array()) {
public static function delete($url, $headers = array(), $options = array()) {
return self::request($url, $headers, null, self::DELETE, $options);
}

/**
* Send a TRACE request
*/
public static function trace($url, $headers = array(), $options = array()) {
return self::request($url, $headers, null, self::TRACE, $options);
}
/**#@-*/

/**#@+
Expand All @@ -242,6 +264,13 @@ public static function put($url, $headers = array(), $data = array(), $options =
return self::request($url, $headers, $data, self::PUT, $options);
}

/**
* Send an OPTIONS request
*/
public static function options($url, $headers = array(), $data = array(), $options = array()) {
return self::request($url, $headers, $data, self::OPTIONS, $options);
}

/**
* Send a PATCH request
*
Expand Down Expand Up @@ -300,12 +329,15 @@ public static function patch($url, $headers, $data = array(), $options = array()
* (string|boolean, default: library/Requests/Transport/cacert.pem)
* - `verifyname`: Should we verify the common name in the SSL certificate?
* (boolean: default, true)
* - `data_format`: How should we send the `$data` parameter?
* (string, one of 'query' or 'body', default: 'query' for
* HEAD/GET/DELETE, 'body' for POST/PUT/OPTIONS/PATCH)
*
* @throws Requests_Exception On invalid URLs (`nonhttp`)
*
* @param string $url URL to request
* @param array $headers Extra headers to send with the request
* @param array $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
* @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
* @param string $type HTTP request type (use Requests constants)
* @param array $options Options for the request (see description for more information)
* @return Requests_Response
Expand All @@ -326,7 +358,8 @@ public static function request($url, $headers = array(), $data = array(), $type
if (is_string($options['transport'])) {
$transport = new $transport();
}
} else {
}
else {
$need_ssl = (0 === stripos($url, 'https://'));
$capabilities = array('ssl' => $need_ssl);
$transport = self::get_transport($capabilities);
Expand Down Expand Up @@ -459,6 +492,7 @@ protected static function get_default_options($multirequest = false) {
'timeout' => 10,
'connect_timeout' => 10,
'useragent' => 'php-requests/' . self::VERSION,
'protocol_version' => 1.1,
'redirected' => 0,
'redirects' => 10,
'follow_redirects' => true,
Expand All @@ -472,7 +506,7 @@ protected static function get_default_options($multirequest = false) {
'idn' => true,
'hooks' => null,
'transport' => null,
'verify' => dirname( __FILE__ ) . '/Requests/Transport/cacert.pem',
'verify' => dirname(__FILE__) . '/Requests/Transport/cacert.pem',
'verifyname' => true,
);
if ($multirequest !== false) {
Expand All @@ -486,7 +520,7 @@ protected static function get_default_options($multirequest = false) {
*
* @param string $url URL to request
* @param array $headers Extra headers to send with the request
* @param array $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
* @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
* @param string $type HTTP request type
* @param array $options Options for the request
* @return array $options
Expand Down Expand Up @@ -529,6 +563,15 @@ protected static function set_defaults(&$url, &$headers, &$data, &$type, &$optio
$iri->host = Requests_IDNAEncoder::encode($iri->ihost);
$url = $iri->uri;
}

if (!isset($options['data_format'])) {
if (in_array($type, array(self::HEAD, self::GET, self::DELETE))) {
$options['data_format'] = 'query';
}
else {
$options['data_format'] = 'body';
}
}
}

/**
Expand Down Expand Up @@ -571,11 +614,12 @@ protected static function parse_response($headers, $url, $req_headers, $req_data
// Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2)
$headers = preg_replace('/\n[ \t]/', ' ', $headers);
$headers = explode("\n", $headers);
preg_match('#^HTTP/1\.\d[ \t]+(\d+)#i', array_shift($headers), $matches);
preg_match('#^HTTP/(1\.\d)[ \t]+(\d+)#i', array_shift($headers), $matches);
if (empty($matches)) {
throw new Requests_Exception('Response could not be parsed', 'noversion', $headers);
}
$return->status_code = (int) $matches[1];
$return->protocol_version = (float) $matches[1];
$return->status_code = (int) $matches[2];
if ($return->status_code >= 200 && $return->status_code < 300) {
$return->success = true;
}
Expand All @@ -601,19 +645,19 @@ protected static function parse_response($headers, $url, $req_headers, $req_data

$options['hooks']->dispatch('requests.before_redirect_check', array(&$return, $req_headers, $req_data, $options));

if ((in_array($return->status_code, array(300, 301, 302, 303, 307)) || $return->status_code > 307 && $return->status_code < 400) && $options['follow_redirects'] === true) {
if ($return->is_redirect() && $options['follow_redirects'] === true) {
if (isset($return->headers['location']) && $options['redirected'] < $options['redirects']) {
if ($return->status_code === 303) {
$options['type'] = Requests::GET;
$options['type'] = self::GET;
}
$options['redirected']++;
$location = $return->headers['location'];
if (strpos ($location, 'http://') !== 0 && strpos ($location, 'https://') !== 0) {
if (strpos($location, 'http://') !== 0 && strpos($location, 'https://') !== 0) {
// relative redirect, for compatibility make it absolute
$location = Requests_IRI::absolutize($url, $location);
$location = $location->uri;
}
$redirected = self::request($location, $req_headers, $req_data, false, $options);
$redirected = self::request($location, $req_headers, $req_data, $options['type'], $options);
$redirected->history[] = $return;
return $redirected;
}
Expand All @@ -634,13 +678,17 @@ protected static function parse_response($headers, $url, $req_headers, $req_data
* Internal use only. Converts a raw HTTP response to a Requests_Response
* while still executing a multiple request.
*
* @param string $headers Full response text including headers and body
* @param string $response Full response text including headers and body (will be overwritten with Response instance)
* @param array $request Request data as passed into {@see Requests::request_multiple()}
* @return null `$response` is either set to a Requests_Response instance, or a Requests_Exception object
*/
public static function parse_multiple(&$response, $request) {
try {
$response = self::parse_response($response, $request['url'], $request['headers'], $request['data'], $request['options']);
$url = $request['url'];
$headers = $request['headers'];
$data = $request['data'];
$options = $request['options'];
$response = self::parse_response($response, $url, $headers, $data, $options);
}
catch (Requests_Exception $e) {
$response = $e;
Expand All @@ -663,7 +711,7 @@ protected static function decode_chunked($data) {
$encoded = $data;

while (true) {
$is_chunked = (bool) preg_match( '/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches );
$is_chunked = (bool) preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches);
if (!$is_chunked) {
// Looks like it's not chunked after all
return $data;
Expand All @@ -676,7 +724,7 @@ protected static function decode_chunked($data) {
}

$chunk_length = strlen($matches[0]);
$decoded .= $part = substr($encoded, $chunk_length, $length);
$decoded .= substr($encoded, $chunk_length, $length);
$encoded = substr($encoded, $chunk_length + $length + 2);

if (trim($encoded) === '0' || empty($encoded)) {
Expand All @@ -698,7 +746,7 @@ protected static function decode_chunked($data) {
public static function flatten($array) {
$return = array();
foreach ($array as $key => $value) {
$return[] = "$key: $value";
$return[] = sprintf('%s: %s', $key, $value);
}
return $return;
}
Expand All @@ -720,7 +768,6 @@ public static function flattern($array) {
* Implements gzip, compress and deflate. Guesses which it is by attempting
* to decode.
*
* @todo Make this smarter by defaulting to whatever the headers say first
* @param string $data Compressed data in one of the above formats
* @return string Decompressed string
*/
Expand Down Expand Up @@ -769,23 +816,26 @@ public static function decompress($data) {
public static function compatible_gzinflate($gzData) {
// Compressed data might contain a full zlib header, if so strip it for
// gzinflate()
if ( substr($gzData, 0, 3) == "\x1f\x8b\x08" ) {
if (substr($gzData, 0, 3) == "\x1f\x8b\x08") {
$i = 10;
$flg = ord( substr($gzData, 3, 1) );
if ( $flg > 0 ) {
if ( $flg & 4 ) {
list($xlen) = unpack('v', substr($gzData, $i, 2) );
$flg = ord(substr($gzData, 3, 1));
if ($flg > 0) {
if ($flg & 4) {
list($xlen) = unpack('v', substr($gzData, $i, 2));
$i = $i + 2 + $xlen;
}
if ( $flg & 8 )
if ($flg & 8) {
$i = strpos($gzData, "\0", $i) + 1;
if ( $flg & 16 )
}
if ($flg & 16) {
$i = strpos($gzData, "\0", $i) + 1;
if ( $flg & 2 )
}
if ($flg & 2) {
$i = $i + 2;
}
}
$decompressed = self::compatible_gzinflate( substr( $gzData, $i ) );
if ( false !== $decompressed ) {
$decompressed = self::compatible_gzinflate(substr($gzData, $i));
if (false !== $decompressed) {
return $decompressed;
}
}
Expand All @@ -801,55 +851,57 @@ public static function compatible_gzinflate($gzData) {
$huffman_encoded = false;

// low nibble of first byte should be 0x08
list( , $first_nibble ) = unpack( 'h', $gzData );
list(, $first_nibble) = unpack('h', $gzData);

// First 2 bytes should be divisible by 0x1F
list( , $first_two_bytes ) = unpack( 'n', $gzData );
list(, $first_two_bytes) = unpack('n', $gzData);

if ( 0x08 == $first_nibble && 0 == ( $first_two_bytes % 0x1F ) )
if (0x08 == $first_nibble && 0 == ($first_two_bytes % 0x1F)) {
$huffman_encoded = true;
}

if ( $huffman_encoded ) {
if ( false !== ( $decompressed = @gzinflate( substr( $gzData, 2 ) ) ) )
if ($huffman_encoded) {
if (false !== ($decompressed = @gzinflate(substr($gzData, 2)))) {
return $decompressed;
}
}

if ( "\x50\x4b\x03\x04" == substr( $gzData, 0, 4 ) ) {
if ("\x50\x4b\x03\x04" == substr($gzData, 0, 4)) {
// ZIP file format header
// Offset 6: 2 bytes, General-purpose field
// Offset 26: 2 bytes, filename length
// Offset 28: 2 bytes, optional field length
// Offset 30: Filename field, followed by optional field, followed
// immediately by data
list( , $general_purpose_flag ) = unpack( 'v', substr( $gzData, 6, 2 ) );
list(, $general_purpose_flag) = unpack('v', substr($gzData, 6, 2));

// If the file has been compressed on the fly, 0x08 bit is set of
// the general purpose field. We can use this to differentiate
// between a compressed document, and a ZIP file
$zip_compressed_on_the_fly = ( 0x08 == (0x08 & $general_purpose_flag ) );
$zip_compressed_on_the_fly = (0x08 == (0x08 & $general_purpose_flag));

if ( ! $zip_compressed_on_the_fly ) {
if (!$zip_compressed_on_the_fly) {
// Don't attempt to decode a compressed zip file
return $gzData;
}

// Determine the first byte of data, based on the above ZIP header
// offsets:
$first_file_start = array_sum( unpack( 'v2', substr( $gzData, 26, 4 ) ) );
if ( false !== ( $decompressed = @gzinflate( substr( $gzData, 30 + $first_file_start ) ) ) ) {
$first_file_start = array_sum(unpack('v2', substr($gzData, 26, 4)));
if (false !== ($decompressed = @gzinflate(substr($gzData, 30 + $first_file_start)))) {
return $decompressed;
}
return false;
}

// Finally fall back to straight gzinflate
if ( false !== ( $decompressed = @gzinflate( $gzData ) ) ) {
if (false !== ($decompressed = @gzinflate($gzData))) {
return $decompressed;
}

// Fallback for all above failing, not expected, but included for
// debugging and preventing regressions and to track stats
if ( false !== ( $decompressed = @gzinflate( substr( $gzData, 2 ) ) ) ) {
if (false !== ($decompressed = @gzinflate(substr($gzData, 2)))) {
return $decompressed;
}

Expand Down
2 changes: 1 addition & 1 deletion includes/Requests/Requests/Auth/Basic.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public function curl_before_send(&$handle) {
* @param string $out HTTP header string
*/
public function fsockopen_header(&$out) {
$out .= "Authorization: Basic " . base64_encode($this->getAuthString()) . "\r\n";
$out .= sprintf("Authorization: Basic %s\r\n", base64_encode($this->getAuthString()));
}

/**
Expand Down
Loading

0 comments on commit d51c6cb

Please sign in to comment.