home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / class-requests.php < prev    next >
Encoding:
PHP Script  |  2016-10-04  |  29.1 KB  |  981 lines

  1. <?php
  2. /**
  3.  * Requests for PHP
  4.  *
  5.  * Inspired by Requests for Python.
  6.  *
  7.  * Based on concepts from SimplePie_File, RequestCore and WP_Http.
  8.  *
  9.  * @package Requests
  10.  */
  11.  
  12. /**
  13.  * Requests for PHP
  14.  *
  15.  * Inspired by Requests for Python.
  16.  *
  17.  * Based on concepts from SimplePie_File, RequestCore and WP_Http.
  18.  *
  19.  * @package Requests
  20.  */
  21. class Requests {
  22.     /**
  23.      * POST method
  24.      *
  25.      * @var string
  26.      */
  27.     const POST = 'POST';
  28.  
  29.     /**
  30.      * PUT method
  31.      *
  32.      * @var string
  33.      */
  34.     const PUT = 'PUT';
  35.  
  36.     /**
  37.      * GET method
  38.      *
  39.      * @var string
  40.      */
  41.     const GET = 'GET';
  42.  
  43.     /**
  44.      * HEAD method
  45.      *
  46.      * @var string
  47.      */
  48.     const HEAD = 'HEAD';
  49.  
  50.     /**
  51.      * DELETE method
  52.      *
  53.      * @var string
  54.      */
  55.     const DELETE = 'DELETE';
  56.  
  57.     /**
  58.      * OPTIONS method
  59.      *
  60.      * @var string
  61.      */
  62.     const OPTIONS = 'OPTIONS';
  63.  
  64.     /**
  65.      * TRACE method
  66.      *
  67.      * @var string
  68.      */
  69.     const TRACE = 'TRACE';
  70.  
  71.     /**
  72.      * PATCH method
  73.      *
  74.      * @link https://tools.ietf.org/html/rfc5789
  75.      * @var string
  76.      */
  77.     const PATCH = 'PATCH';
  78.  
  79.     /**
  80.      * Default size of buffer size to read streams
  81.      *
  82.      * @var integer
  83.      */
  84.     const BUFFER_SIZE = 1160;
  85.  
  86.     /**
  87.      * Current version of Requests
  88.      *
  89.      * @var string
  90.      */
  91.     const VERSION = '1.7';
  92.  
  93.     /**
  94.      * Registered transport classes
  95.      *
  96.      * @var array
  97.      */
  98.     protected static $transports = array();
  99.  
  100.     /**
  101.      * Selected transport name
  102.      *
  103.      * Use {@see get_transport()} instead
  104.      *
  105.      * @var array
  106.      */
  107.     public static $transport = array();
  108.  
  109.     /**
  110.      * Default certificate path.
  111.      *
  112.      * @see Requests::get_certificate_path()
  113.      * @see Requests::set_certificate_path()
  114.      *
  115.      * @var string
  116.      */
  117.     protected static $certificate_path;
  118.  
  119.     /**
  120.      * This is a static class, do not instantiate it
  121.      *
  122.      * @codeCoverageIgnore
  123.      */
  124.     private function __construct() {}
  125.  
  126.     /**
  127.      * Autoloader for Requests
  128.      *
  129.      * Register this with {@see register_autoloader()} if you'd like to avoid
  130.      * having to create your own.
  131.      *
  132.      * (You can also use `spl_autoload_register` directly if you'd prefer.)
  133.      *
  134.      * @codeCoverageIgnore
  135.      *
  136.      * @param string $class Class name to load
  137.      */
  138.     public static function autoloader($class) {
  139.         // Check that the class starts with "Requests"
  140.         if (strpos($class, 'Requests') !== 0) {
  141.             return;
  142.         }
  143.  
  144.         $file = str_replace('_', '/', $class);
  145.         if (file_exists(dirname(__FILE__) . '/' . $file . '.php')) {
  146.             require_once(dirname(__FILE__) . '/' . $file . '.php');
  147.         }
  148.     }
  149.  
  150.     /**
  151.      * Register the built-in autoloader
  152.      *
  153.      * @codeCoverageIgnore
  154.      */
  155.     public static function register_autoloader() {
  156.         spl_autoload_register(array('Requests', 'autoloader'));
  157.     }
  158.  
  159.     /**
  160.      * Register a transport
  161.      *
  162.      * @param string $transport Transport class to add, must support the Requests_Transport interface
  163.      */
  164.     public static function add_transport($transport) {
  165.         if (empty(self::$transports)) {
  166.             self::$transports = array(
  167.                 'Requests_Transport_cURL',
  168.                 'Requests_Transport_fsockopen',
  169.             );
  170.         }
  171.  
  172.         self::$transports = array_merge(self::$transports, array($transport));
  173.     }
  174.  
  175.     /**
  176.      * Get a working transport
  177.      *
  178.      * @throws Requests_Exception If no valid transport is found (`notransport`)
  179.      * @return Requests_Transport
  180.      */
  181.     protected static function get_transport($capabilities = array()) {
  182.         // Caching code, don't bother testing coverage
  183.         // @codeCoverageIgnoreStart
  184.         // array of capabilities as a string to be used as an array key
  185.         ksort($capabilities);
  186.         $cap_string = serialize($capabilities);
  187.  
  188.         // Don't search for a transport if it's already been done for these $capabilities
  189.         if (isset(self::$transport[$cap_string]) && self::$transport[$cap_string] !== null) {
  190.             return new self::$transport[$cap_string]();
  191.         }
  192.         // @codeCoverageIgnoreEnd
  193.  
  194.         if (empty(self::$transports)) {
  195.             self::$transports = array(
  196.                 'Requests_Transport_cURL',
  197.                 'Requests_Transport_fsockopen',
  198.             );
  199.         }
  200.  
  201.         // Find us a working transport
  202.         foreach (self::$transports as $class) {
  203.             if (!class_exists($class)) {
  204.                 continue;
  205.             }
  206.  
  207.             $result = call_user_func(array($class, 'test'), $capabilities);
  208.             if ($result) {
  209.                 self::$transport[$cap_string] = $class;
  210.                 break;
  211.             }
  212.         }
  213.         if (self::$transport[$cap_string] === null) {
  214.             throw new Requests_Exception('No working transports found', 'notransport', self::$transports);
  215.         }
  216.  
  217.         return new self::$transport[$cap_string]();
  218.     }
  219.  
  220.     /**#@+
  221.      * @see request()
  222.      * @param string $url
  223.      * @param array $headers
  224.      * @param array $options
  225.      * @return Requests_Response
  226.      */
  227.     /**
  228.      * Send a GET request
  229.      */
  230.     public static function get($url, $headers = array(), $options = array()) {
  231.         return self::request($url, $headers, null, self::GET, $options);
  232.     }
  233.  
  234.     /**
  235.      * Send a HEAD request
  236.      */
  237.     public static function head($url, $headers = array(), $options = array()) {
  238.         return self::request($url, $headers, null, self::HEAD, $options);
  239.     }
  240.  
  241.     /**
  242.      * Send a DELETE request
  243.      */
  244.     public static function delete($url, $headers = array(), $options = array()) {
  245.         return self::request($url, $headers, null, self::DELETE, $options);
  246.     }
  247.  
  248.     /**
  249.      * Send a TRACE request
  250.      */
  251.     public static function trace($url, $headers = array(), $options = array()) {
  252.         return self::request($url, $headers, null, self::TRACE, $options);
  253.     }
  254.     /**#@-*/
  255.  
  256.     /**#@+
  257.      * @see request()
  258.      * @param string $url
  259.      * @param array $headers
  260.      * @param array $data
  261.      * @param array $options
  262.      * @return Requests_Response
  263.      */
  264.     /**
  265.      * Send a POST request
  266.      */
  267.     public static function post($url, $headers = array(), $data = array(), $options = array()) {
  268.         return self::request($url, $headers, $data, self::POST, $options);
  269.     }
  270.     /**
  271.      * Send a PUT request
  272.      */
  273.     public static function put($url, $headers = array(), $data = array(), $options = array()) {
  274.         return self::request($url, $headers, $data, self::PUT, $options);
  275.     }
  276.  
  277.     /**
  278.      * Send an OPTIONS request
  279.      */
  280.     public static function options($url, $headers = array(), $data = array(), $options = array()) {
  281.         return self::request($url, $headers, $data, self::OPTIONS, $options);
  282.     }
  283.  
  284.     /**
  285.      * Send a PATCH request
  286.      *
  287.      * Note: Unlike {@see post} and {@see put}, `$headers` is required, as the
  288.      * specification recommends that should send an ETag
  289.      *
  290.      * @link https://tools.ietf.org/html/rfc5789
  291.      */
  292.     public static function patch($url, $headers, $data = array(), $options = array()) {
  293.         return self::request($url, $headers, $data, self::PATCH, $options);
  294.     }
  295.     /**#@-*/
  296.  
  297.     /**
  298.      * Main interface for HTTP requests
  299.      *
  300.      * This method initiates a request and sends it via a transport before
  301.      * parsing.
  302.      *
  303.      * The `$options` parameter takes an associative array with the following
  304.      * options:
  305.      *
  306.      * - `timeout`: How long should we wait for a response?
  307.      *    Note: for cURL, a minimum of 1 second applies, as DNS resolution
  308.      *    operates at second-resolution only.
  309.      *    (float, seconds with a millisecond precision, default: 10, example: 0.01)
  310.      * - `connect_timeout`: How long should we wait while trying to connect?
  311.      *    (float, seconds with a millisecond precision, default: 10, example: 0.01)
  312.      * - `useragent`: Useragent to send to the server
  313.      *    (string, default: php-requests/$version)
  314.      * - `follow_redirects`: Should we follow 3xx redirects?
  315.      *    (boolean, default: true)
  316.      * - `redirects`: How many times should we redirect before erroring?
  317.      *    (integer, default: 10)
  318.      * - `blocking`: Should we block processing on this request?
  319.      *    (boolean, default: true)
  320.      * - `filename`: File to stream the body to instead.
  321.      *    (string|boolean, default: false)
  322.      * - `auth`: Authentication handler or array of user/password details to use
  323.      *    for Basic authentication
  324.      *    (Requests_Auth|array|boolean, default: false)
  325.      * - `proxy`: Proxy details to use for proxy by-passing and authentication
  326.      *    (Requests_Proxy|array|string|boolean, default: false)
  327.      * - `max_bytes`: Limit for the response body size.
  328.      *    (integer|boolean, default: false)
  329.      * - `idn`: Enable IDN parsing
  330.      *    (boolean, default: true)
  331.      * - `transport`: Custom transport. Either a class name, or a
  332.      *    transport object. Defaults to the first working transport from
  333.      *    {@see getTransport()}
  334.      *    (string|Requests_Transport, default: {@see getTransport()})
  335.      * - `hooks`: Hooks handler.
  336.      *    (Requests_Hooker, default: new Requests_Hooks())
  337.      * - `verify`: Should we verify SSL certificates? Allows passing in a custom
  338.      *    certificate file as a string. (Using true uses the system-wide root
  339.      *    certificate store instead, but this may have different behaviour
  340.      *    across transports.)
  341.      *    (string|boolean, default: library/Requests/Transport/cacert.pem)
  342.      * - `verifyname`: Should we verify the common name in the SSL certificate?
  343.      *    (boolean: default, true)
  344.      * - `data_format`: How should we send the `$data` parameter?
  345.      *    (string, one of 'query' or 'body', default: 'query' for
  346.      *    HEAD/GET/DELETE, 'body' for POST/PUT/OPTIONS/PATCH)
  347.      *
  348.      * @throws Requests_Exception On invalid URLs (`nonhttp`)
  349.      *
  350.      * @param string $url URL to request
  351.      * @param array $headers Extra headers to send with the request
  352.      * @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
  353.      * @param string $type HTTP request type (use Requests constants)
  354.      * @param array $options Options for the request (see description for more information)
  355.      * @return Requests_Response
  356.      */
  357.     public static function request($url, $headers = array(), $data = array(), $type = self::GET, $options = array()) {
  358.         if (empty($options['type'])) {
  359.             $options['type'] = $type;
  360.         }
  361.         $options = array_merge(self::get_default_options(), $options);
  362.  
  363.         self::set_defaults($url, $headers, $data, $type, $options);
  364.  
  365.         $options['hooks']->dispatch('requests.before_request', array(&$url, &$headers, &$data, &$type, &$options));
  366.  
  367.         if (!empty($options['transport'])) {
  368.             $transport = $options['transport'];
  369.  
  370.             if (is_string($options['transport'])) {
  371.                 $transport = new $transport();
  372.             }
  373.         }
  374.         else {
  375.             $need_ssl = (0 === stripos($url, 'https://'));
  376.             $capabilities = array('ssl' => $need_ssl);
  377.             $transport = self::get_transport($capabilities);
  378.         }
  379.         $response = $transport->request($url, $headers, $data, $options);
  380.  
  381.         $options['hooks']->dispatch('requests.before_parse', array(&$response, $url, $headers, $data, $type, $options));
  382.  
  383.         return self::parse_response($response, $url, $headers, $data, $options);
  384.     }
  385.  
  386.     /**
  387.      * Send multiple HTTP requests simultaneously
  388.      *
  389.      * The `$requests` parameter takes an associative or indexed array of
  390.      * request fields. The key of each request can be used to match up the
  391.      * request with the returned data, or with the request passed into your
  392.      * `multiple.request.complete` callback.
  393.      *
  394.      * The request fields value is an associative array with the following keys:
  395.      *
  396.      * - `url`: Request URL Same as the `$url` parameter to
  397.      *    {@see Requests::request}
  398.      *    (string, required)
  399.      * - `headers`: Associative array of header fields. Same as the `$headers`
  400.      *    parameter to {@see Requests::request}
  401.      *    (array, default: `array()`)
  402.      * - `data`: Associative array of data fields or a string. Same as the
  403.      *    `$data` parameter to {@see Requests::request}
  404.      *    (array|string, default: `array()`)
  405.      * - `type`: HTTP request type (use Requests constants). Same as the `$type`
  406.      *    parameter to {@see Requests::request}
  407.      *    (string, default: `Requests::GET`)
  408.      * - `cookies`: Associative array of cookie name to value, or cookie jar.
  409.      *    (array|Requests_Cookie_Jar)
  410.      *
  411.      * If the `$options` parameter is specified, individual requests will
  412.      * inherit options from it. This can be used to use a single hooking system,
  413.      * or set all the types to `Requests::POST`, for example.
  414.      *
  415.      * In addition, the `$options` parameter takes the following global options:
  416.      *
  417.      * - `complete`: A callback for when a request is complete. Takes two
  418.      *    parameters, a Requests_Response/Requests_Exception reference, and the
  419.      *    ID from the request array (Note: this can also be overridden on a
  420.      *    per-request basis, although that's a little silly)
  421.      *    (callback)
  422.      *
  423.      * @param array $requests Requests data (see description for more information)
  424.      * @param array $options Global and default options (see {@see Requests::request})
  425.      * @return array Responses (either Requests_Response or a Requests_Exception object)
  426.      */
  427.     public static function request_multiple($requests, $options = array()) {
  428.         $options = array_merge(self::get_default_options(true), $options);
  429.  
  430.         if (!empty($options['hooks'])) {
  431.             $options['hooks']->register('transport.internal.parse_response', array('Requests', 'parse_multiple'));
  432.             if (!empty($options['complete'])) {
  433.                 $options['hooks']->register('multiple.request.complete', $options['complete']);
  434.             }
  435.         }
  436.  
  437.         foreach ($requests as $id => &$request) {
  438.             if (!isset($request['headers'])) {
  439.                 $request['headers'] = array();
  440.             }
  441.             if (!isset($request['data'])) {
  442.                 $request['data'] = array();
  443.             }
  444.             if (!isset($request['type'])) {
  445.                 $request['type'] = self::GET;
  446.             }
  447.             if (!isset($request['options'])) {
  448.                 $request['options'] = $options;
  449.                 $request['options']['type'] = $request['type'];
  450.             }
  451.             else {
  452.                 if (empty($request['options']['type'])) {
  453.                     $request['options']['type'] = $request['type'];
  454.                 }
  455.                 $request['options'] = array_merge($options, $request['options']);
  456.             }
  457.  
  458.             self::set_defaults($request['url'], $request['headers'], $request['data'], $request['type'], $request['options']);
  459.  
  460.             // Ensure we only hook in once
  461.             if ($request['options']['hooks'] !== $options['hooks']) {
  462.                 $request['options']['hooks']->register('transport.internal.parse_response', array('Requests', 'parse_multiple'));
  463.                 if (!empty($request['options']['complete'])) {
  464.                     $request['options']['hooks']->register('multiple.request.complete', $request['options']['complete']);
  465.                 }
  466.             }
  467.         }
  468.         unset($request);
  469.  
  470.         if (!empty($options['transport'])) {
  471.             $transport = $options['transport'];
  472.  
  473.             if (is_string($options['transport'])) {
  474.                 $transport = new $transport();
  475.             }
  476.         }
  477.         else {
  478.             $transport = self::get_transport();
  479.         }
  480.         $responses = $transport->request_multiple($requests, $options);
  481.  
  482.         foreach ($responses as $id => &$response) {
  483.             // If our hook got messed with somehow, ensure we end up with the
  484.             // correct response
  485.             if (is_string($response)) {
  486.                 $request = $requests[$id];
  487.                 self::parse_multiple($response, $request);
  488.                 $request['options']['hooks']->dispatch('multiple.request.complete', array(&$response, $id));
  489.             }
  490.         }
  491.  
  492.         return $responses;
  493.     }
  494.  
  495.     /**
  496.      * Get the default options
  497.      *
  498.      * @see Requests::request() for values returned by this method
  499.      * @param boolean $multirequest Is this a multirequest?
  500.      * @return array Default option values
  501.      */
  502.     protected static function get_default_options($multirequest = false) {
  503.         $defaults = array(
  504.             'timeout' => 10,
  505.             'connect_timeout' => 10,
  506.             'useragent' => 'php-requests/' . self::VERSION,
  507.             'protocol_version' => 1.1,
  508.             'redirected' => 0,
  509.             'redirects' => 10,
  510.             'follow_redirects' => true,
  511.             'blocking' => true,
  512.             'type' => self::GET,
  513.             'filename' => false,
  514.             'auth' => false,
  515.             'proxy' => false,
  516.             'cookies' => false,
  517.             'max_bytes' => false,
  518.             'idn' => true,
  519.             'hooks' => null,
  520.             'transport' => null,
  521.             'verify' => Requests::get_certificate_path(),
  522.             'verifyname' => true,
  523.         );
  524.         if ($multirequest !== false) {
  525.             $defaults['complete'] = null;
  526.         }
  527.         return $defaults;
  528.     }
  529.  
  530.     /**
  531.      * Get default certificate path.
  532.      *
  533.      * @return string Default certificate path.
  534.      */
  535.     public static function get_certificate_path() {
  536.         if ( ! empty( Requests::$certificate_path ) ) {
  537.             return Requests::$certificate_path;
  538.         }
  539.  
  540.         return dirname(__FILE__) . '/Requests/Transport/cacert.pem';
  541.     }
  542.  
  543.     /**
  544.      * Set default certificate path.
  545.      *
  546.      * @param string $path Certificate path, pointing to a PEM file.
  547.      */
  548.     public static function set_certificate_path( $path ) {
  549.         Requests::$certificate_path = $path;
  550.     }
  551.  
  552.     /**
  553.      * Set the default values
  554.      *
  555.      * @param string $url URL to request
  556.      * @param array $headers Extra headers to send with the request
  557.      * @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
  558.      * @param string $type HTTP request type
  559.      * @param array $options Options for the request
  560.      * @return array $options
  561.      */
  562.     protected static function set_defaults(&$url, &$headers, &$data, &$type, &$options) {
  563.         if (!preg_match('/^http(s)?:\/\//i', $url, $matches)) {
  564.             throw new Requests_Exception('Only HTTP(S) requests are handled.', 'nonhttp', $url);
  565.         }
  566.  
  567.         if (empty($options['hooks'])) {
  568.             $options['hooks'] = new Requests_Hooks();
  569.         }
  570.  
  571.         if (is_array($options['auth'])) {
  572.             $options['auth'] = new Requests_Auth_Basic($options['auth']);
  573.         }
  574.         if ($options['auth'] !== false) {
  575.             $options['auth']->register($options['hooks']);
  576.         }
  577.  
  578.         if (is_string($options['proxy']) || is_array($options['proxy'])) {
  579.             $options['proxy'] = new Requests_Proxy_HTTP($options['proxy']);
  580.         }
  581.         if ($options['proxy'] !== false) {
  582.             $options['proxy']->register($options['hooks']);
  583.         }
  584.  
  585.         if (is_array($options['cookies'])) {
  586.             $options['cookies'] = new Requests_Cookie_Jar($options['cookies']);
  587.         }
  588.         elseif (empty($options['cookies'])) {
  589.             $options['cookies'] = new Requests_Cookie_Jar();
  590.         }
  591.         if ($options['cookies'] !== false) {
  592.             $options['cookies']->register($options['hooks']);
  593.         }
  594.  
  595.         if ($options['idn'] !== false) {
  596.             $iri = new Requests_IRI($url);
  597.             $iri->host = Requests_IDNAEncoder::encode($iri->ihost);
  598.             $url = $iri->uri;
  599.         }
  600.  
  601.         // Massage the type to ensure we support it.
  602.         $type = strtoupper($type);
  603.  
  604.         if (!isset($options['data_format'])) {
  605.             if (in_array($type, array(self::HEAD, self::GET, self::DELETE))) {
  606.                 $options['data_format'] = 'query';
  607.             }
  608.             else {
  609.                 $options['data_format'] = 'body';
  610.             }
  611.         }
  612.     }
  613.  
  614.     /**
  615.      * HTTP response parser
  616.      *
  617.      * @throws Requests_Exception On missing head/body separator (`requests.no_crlf_separator`)
  618.      * @throws Requests_Exception On missing head/body separator (`noversion`)
  619.      * @throws Requests_Exception On missing head/body separator (`toomanyredirects`)
  620.      *
  621.      * @param string $headers Full response text including headers and body
  622.      * @param string $url Original request URL
  623.      * @param array $req_headers Original $headers array passed to {@link request()}, in case we need to follow redirects
  624.      * @param array $req_data Original $data array passed to {@link request()}, in case we need to follow redirects
  625.      * @param array $options Original $options array passed to {@link request()}, in case we need to follow redirects
  626.      * @return Requests_Response
  627.      */
  628.     protected static function parse_response($headers, $url, $req_headers, $req_data, $options) {
  629.         $return = new Requests_Response();
  630.         if (!$options['blocking']) {
  631.             return $return;
  632.         }
  633.  
  634.         $return->raw = $headers;
  635.         $return->url = $url;
  636.  
  637.         if (!$options['filename']) {
  638.             if (($pos = strpos($headers, "\r\n\r\n")) === false) {
  639.                 // Crap!
  640.                 throw new Requests_Exception('Missing header/body separator', 'requests.no_crlf_separator');
  641.             }
  642.  
  643.             $headers = substr($return->raw, 0, $pos);
  644.             $return->body = substr($return->raw, $pos + strlen("\n\r\n\r"));
  645.         }
  646.         else {
  647.             $return->body = '';
  648.         }
  649.         // Pretend CRLF = LF for compatibility (RFC 2616, section 19.3)
  650.         $headers = str_replace("\r\n", "\n", $headers);
  651.         // Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2)
  652.         $headers = preg_replace('/\n[ \t]/', ' ', $headers);
  653.         $headers = explode("\n", $headers);
  654.         preg_match('#^HTTP/(1\.\d)[ \t]+(\d+)#i', array_shift($headers), $matches);
  655.         if (empty($matches)) {
  656.             throw new Requests_Exception('Response could not be parsed', 'noversion', $headers);
  657.         }
  658.         $return->protocol_version = (float) $matches[1];
  659.         $return->status_code = (int) $matches[2];
  660.         if ($return->status_code >= 200 && $return->status_code < 300) {
  661.             $return->success = true;
  662.         }
  663.  
  664.         foreach ($headers as $header) {
  665.             list($key, $value) = explode(':', $header, 2);
  666.             $value = trim($value);
  667.             preg_replace('#(\s+)#i', ' ', $value);
  668.             $return->headers[$key] = $value;
  669.         }
  670.         if (isset($return->headers['transfer-encoding'])) {
  671.             $return->body = self::decode_chunked($return->body);
  672.             unset($return->headers['transfer-encoding']);
  673.         }
  674.         if (isset($return->headers['content-encoding'])) {
  675.             $return->body = self::decompress($return->body);
  676.         }
  677.  
  678.         //fsockopen and cURL compatibility
  679.         if (isset($return->headers['connection'])) {
  680.             unset($return->headers['connection']);
  681.         }
  682.  
  683.         $options['hooks']->dispatch('requests.before_redirect_check', array(&$return, $req_headers, $req_data, $options));
  684.  
  685.         if ($return->is_redirect() && $options['follow_redirects'] === true) {
  686.             if (isset($return->headers['location']) && $options['redirected'] < $options['redirects']) {
  687.                 if ($return->status_code === 303) {
  688.                     $options['type'] = self::GET;
  689.                 }
  690.                 $options['redirected']++;
  691.                 $location = $return->headers['location'];
  692.                 if (strpos($location, 'http://') !== 0 && strpos($location, 'https://') !== 0) {
  693.                     // relative redirect, for compatibility make it absolute
  694.                     $location = Requests_IRI::absolutize($url, $location);
  695.                     $location = $location->uri;
  696.                 }
  697.  
  698.                 $hook_args = array(
  699.                     &$location,
  700.                     &$req_headers,
  701.                     &$req_data,
  702.                     &$options,
  703.                     $return
  704.                 );
  705.                 $options['hooks']->dispatch('requests.before_redirect', $hook_args);
  706.                 $redirected = self::request($location, $req_headers, $req_data, $options['type'], $options);
  707.                 $redirected->history[] = $return;
  708.                 return $redirected;
  709.             }
  710.             elseif ($options['redirected'] >= $options['redirects']) {
  711.                 throw new Requests_Exception('Too many redirects', 'toomanyredirects', $return);
  712.             }
  713.         }
  714.  
  715.         $return->redirects = $options['redirected'];
  716.  
  717.         $options['hooks']->dispatch('requests.after_request', array(&$return, $req_headers, $req_data, $options));
  718.         return $return;
  719.     }
  720.  
  721.     /**
  722.      * Callback for `transport.internal.parse_response`
  723.      *
  724.      * Internal use only. Converts a raw HTTP response to a Requests_Response
  725.      * while still executing a multiple request.
  726.      *
  727.      * @param string $response Full response text including headers and body (will be overwritten with Response instance)
  728.      * @param array $request Request data as passed into {@see Requests::request_multiple()}
  729.      * @return null `$response` is either set to a Requests_Response instance, or a Requests_Exception object
  730.      */
  731.     public static function parse_multiple(&$response, $request) {
  732.         try {
  733.             $url = $request['url'];
  734.             $headers = $request['headers'];
  735.             $data = $request['data'];
  736.             $options = $request['options'];
  737.             $response = self::parse_response($response, $url, $headers, $data, $options);
  738.         }
  739.         catch (Requests_Exception $e) {
  740.             $response = $e;
  741.         }
  742.     }
  743.  
  744.     /**
  745.      * Decoded a chunked body as per RFC 2616
  746.      *
  747.      * @see https://tools.ietf.org/html/rfc2616#section-3.6.1
  748.      * @param string $data Chunked body
  749.      * @return string Decoded body
  750.      */
  751.     protected static function decode_chunked($data) {
  752.         if (!preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', trim($data))) {
  753.             return $data;
  754.         }
  755.  
  756.  
  757.  
  758.         $decoded = '';
  759.         $encoded = $data;
  760.  
  761.         while (true) {
  762.             $is_chunked = (bool) preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', $encoded, $matches);
  763.             if (!$is_chunked) {
  764.                 // Looks like it's not chunked after all
  765.                 return $data;
  766.             }
  767.  
  768.             $length = hexdec(trim($matches[1]));
  769.             if ($length === 0) {
  770.                 // Ignore trailer headers
  771.                 return $decoded;
  772.             }
  773.  
  774.             $chunk_length = strlen($matches[0]);
  775.             $decoded .= substr($encoded, $chunk_length, $length);
  776.             $encoded = substr($encoded, $chunk_length + $length + 2);
  777.  
  778.             if (trim($encoded) === '0' || empty($encoded)) {
  779.                 return $decoded;
  780.             }
  781.         }
  782.  
  783.         // We'll never actually get down here
  784.         // @codeCoverageIgnoreStart
  785.     }
  786.     // @codeCoverageIgnoreEnd
  787.  
  788.     /**
  789.      * Convert a key => value array to a 'key: value' array for headers
  790.      *
  791.      * @param array $array Dictionary of header values
  792.      * @return array List of headers
  793.      */
  794.     public static function flatten($array) {
  795.         $return = array();
  796.         foreach ($array as $key => $value) {
  797.             $return[] = sprintf('%s: %s', $key, $value);
  798.         }
  799.         return $return;
  800.     }
  801.  
  802.     /**
  803.      * Convert a key => value array to a 'key: value' array for headers
  804.      *
  805.      * @codeCoverageIgnore
  806.      * @deprecated Misspelling of {@see Requests::flatten}
  807.      * @param array $array Dictionary of header values
  808.      * @return array List of headers
  809.      */
  810.     public static function flattern($array) {
  811.         return self::flatten($array);
  812.     }
  813.  
  814.     /**
  815.      * Decompress an encoded body
  816.      *
  817.      * Implements gzip, compress and deflate. Guesses which it is by attempting
  818.      * to decode.
  819.      *
  820.      * @param string $data Compressed data in one of the above formats
  821.      * @return string Decompressed string
  822.      */
  823.     public static function decompress($data) {
  824.         if (substr($data, 0, 2) !== "\x1f\x8b" && substr($data, 0, 2) !== "\x78\x9c") {
  825.             // Not actually compressed. Probably cURL ruining this for us.
  826.             return $data;
  827.         }
  828.  
  829.         if (function_exists('gzdecode') && ($decoded = @gzdecode($data)) !== false) {
  830.             return $decoded;
  831.         }
  832.         elseif (function_exists('gzinflate') && ($decoded = @gzinflate($data)) !== false) {
  833.             return $decoded;
  834.         }
  835.         elseif (($decoded = self::compatible_gzinflate($data)) !== false) {
  836.             return $decoded;
  837.         }
  838.         elseif (function_exists('gzuncompress') && ($decoded = @gzuncompress($data)) !== false) {
  839.             return $decoded;
  840.         }
  841.  
  842.         return $data;
  843.     }
  844.  
  845.     /**
  846.      * Decompression of deflated string while staying compatible with the majority of servers.
  847.      *
  848.      * Certain Servers will return deflated data with headers which PHP's gzinflate()
  849.      * function cannot handle out of the box. The following function has been created from
  850.      * various snippets on the gzinflate() PHP documentation.
  851.      *
  852.      * Warning: Magic numbers within. Due to the potential different formats that the compressed
  853.      * data may be returned in, some "magic offsets" are needed to ensure proper decompression
  854.      * takes place. For a simple progmatic way to determine the magic offset in use, see:
  855.      * https://core.trac.wordpress.org/ticket/18273
  856.      *
  857.      * @since 2.8.1
  858.      * @link https://core.trac.wordpress.org/ticket/18273
  859.      * @link https://secure.php.net/manual/en/function.gzinflate.php#70875
  860.      * @link https://secure.php.net/manual/en/function.gzinflate.php#77336
  861.      *
  862.      * @param string $gzData String to decompress.
  863.      * @return string|bool False on failure.
  864.      */
  865.     public static function compatible_gzinflate($gzData) {
  866.         // Compressed data might contain a full zlib header, if so strip it for
  867.         // gzinflate()
  868.         if (substr($gzData, 0, 3) == "\x1f\x8b\x08") {
  869.             $i = 10;
  870.             $flg = ord(substr($gzData, 3, 1));
  871.             if ($flg > 0) {
  872.                 if ($flg & 4) {
  873.                     list($xlen) = unpack('v', substr($gzData, $i, 2));
  874.                     $i = $i + 2 + $xlen;
  875.                 }
  876.                 if ($flg & 8) {
  877.                     $i = strpos($gzData, "\0", $i) + 1;
  878.                 }
  879.                 if ($flg & 16) {
  880.                     $i = strpos($gzData, "\0", $i) + 1;
  881.                 }
  882.                 if ($flg & 2) {
  883.                     $i = $i + 2;
  884.                 }
  885.             }
  886.             $decompressed = self::compatible_gzinflate(substr($gzData, $i));
  887.             if (false !== $decompressed) {
  888.                 return $decompressed;
  889.             }
  890.         }
  891.  
  892.         // If the data is Huffman Encoded, we must first strip the leading 2
  893.         // byte Huffman marker for gzinflate()
  894.         // The response is Huffman coded by many compressors such as
  895.         // java.util.zip.Deflater, RubyΓÇÖs Zlib::Deflate, and .NET's
  896.         // System.IO.Compression.DeflateStream.
  897.         //
  898.         // See https://decompres.blogspot.com/ for a quick explanation of this
  899.         // data type
  900.         $huffman_encoded = false;
  901.  
  902.         // low nibble of first byte should be 0x08
  903.         list(, $first_nibble)    = unpack('h', $gzData);
  904.  
  905.         // First 2 bytes should be divisible by 0x1F
  906.         list(, $first_two_bytes) = unpack('n', $gzData);
  907.  
  908.         if (0x08 == $first_nibble && 0 == ($first_two_bytes % 0x1F)) {
  909.             $huffman_encoded = true;
  910.         }
  911.  
  912.         if ($huffman_encoded) {
  913.             if (false !== ($decompressed = @gzinflate(substr($gzData, 2)))) {
  914.                 return $decompressed;
  915.             }
  916.         }
  917.  
  918.         if ("\x50\x4b\x03\x04" == substr($gzData, 0, 4)) {
  919.             // ZIP file format header
  920.             // Offset 6: 2 bytes, General-purpose field
  921.             // Offset 26: 2 bytes, filename length
  922.             // Offset 28: 2 bytes, optional field length
  923.             // Offset 30: Filename field, followed by optional field, followed
  924.             // immediately by data
  925.             list(, $general_purpose_flag) = unpack('v', substr($gzData, 6, 2));
  926.  
  927.             // If the file has been compressed on the fly, 0x08 bit is set of
  928.             // the general purpose field. We can use this to differentiate
  929.             // between a compressed document, and a ZIP file
  930.             $zip_compressed_on_the_fly = (0x08 == (0x08 & $general_purpose_flag));
  931.  
  932.             if (!$zip_compressed_on_the_fly) {
  933.                 // Don't attempt to decode a compressed zip file
  934.                 return $gzData;
  935.             }
  936.  
  937.             // Determine the first byte of data, based on the above ZIP header
  938.             // offsets:
  939.             $first_file_start = array_sum(unpack('v2', substr($gzData, 26, 4)));
  940.             if (false !== ($decompressed = @gzinflate(substr($gzData, 30 + $first_file_start)))) {
  941.                 return $decompressed;
  942.             }
  943.             return false;
  944.         }
  945.  
  946.         // Finally fall back to straight gzinflate
  947.         if (false !== ($decompressed = @gzinflate($gzData))) {
  948.             return $decompressed;
  949.         }
  950.  
  951.         // Fallback for all above failing, not expected, but included for
  952.         // debugging and preventing regressions and to track stats
  953.         if (false !== ($decompressed = @gzinflate(substr($gzData, 2)))) {
  954.             return $decompressed;
  955.         }
  956.  
  957.         return false;
  958.     }
  959.  
  960.     public static function match_domain($host, $reference) {
  961.         // Check for a direct match
  962.         if ($host === $reference) {
  963.             return true;
  964.         }
  965.  
  966.         // Calculate the valid wildcard match if the host is not an IP address
  967.         // Also validates that the host has 3 parts or more, as per Firefox's
  968.         // ruleset.
  969.         $parts = explode('.', $host);
  970.         if (ip2long($host) === false && count($parts) >= 3) {
  971.             $parts[0] = '*';
  972.             $wildcard = implode('.', $parts);
  973.             if ($wildcard === $reference) {
  974.                 return true;
  975.             }
  976.         }
  977.  
  978.         return false;
  979.     }
  980. }
  981.