Ampache PHP Cross Reference Groupware Applications

Source: /modules/requests/Requests/Transport/fsockopen.php - 381 lines - 10679 bytes - Summary - Text - Print

Description: fsockopen HTTP transport

   1  <?php
   2  /**
   3   * fsockopen HTTP transport
   4   *
   5   * @package Requests
   6   * @subpackage Transport
   7   */
   8  
   9  /**
  10   * fsockopen HTTP transport
  11   *
  12   * @package Requests
  13   * @subpackage Transport
  14   */
  15  class Requests_Transport_fsockopen implements Requests_Transport {
  16      /**
  17       * Raw HTTP data
  18       *
  19       * @var string
  20       */
  21      public $headers = '';
  22  
  23      /**
  24       * Stream metadata
  25       *
  26       * @var array Associative array of properties, see {@see http://php.net/stream_get_meta_data}
  27       */
  28      public $info;
  29  
  30      protected $connect_error = '';
  31  
  32      /**
  33       * Perform a request
  34       *
  35       * @throws Requests_Exception On failure to connect to socket (`fsockopenerror`)
  36       * @throws Requests_Exception On socket timeout (`timeout`)
  37       *
  38       * @param string $url URL to request
  39       * @param array $headers Associative array of request headers
  40       * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD
  41       * @param array $options Request options, see {@see Requests::response()} for documentation
  42       * @return string Raw HTTP result
  43       */
  44  	public function request($url, $headers = array(), $data = array(), $options = array()) {
  45          $options['hooks']->dispatch('fsockopen.before_request');
  46  
  47          $url_parts = parse_url($url);
  48          $host = $url_parts['host'];
  49          $context = stream_context_create();
  50          $verifyname = false;
  51  
  52          // HTTPS support
  53          if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') {
  54              $remote_socket = 'ssl://' . $host;
  55              $url_parts['port'] = 443;
  56  
  57              $context_options = array(
  58                  'verify_peer' => true,
  59                  // 'CN_match' => $host,
  60                  'capture_peer_cert' => true
  61              );
  62              $verifyname = true;
  63  
  64              // SNI, if enabled (OpenSSL >=0.9.8j)
  65              if (defined('OPENSSL_TLSEXT_SERVER_NAME') && OPENSSL_TLSEXT_SERVER_NAME) {
  66                  $context_options['SNI_enabled'] = true;
  67                  if (isset($options['verifyname']) && $options['verifyname'] === false) {
  68                      $context_options['SNI_enabled'] = false;
  69                  }
  70              }
  71  
  72              if (isset($options['verify'])) {
  73                  if ($options['verify'] === false) {
  74                      $context_options['verify_peer'] = false;
  75                  } elseif (is_string($options['verify'])) {
  76                      $context_options['cafile'] = $options['verify'];
  77                  }
  78              }
  79  
  80              if (isset($options['verifyname']) && $options['verifyname'] === false) {
  81                  $verifyname = false;
  82              }
  83  
  84              stream_context_set_option($context, array('ssl' => $context_options));
  85          }
  86          else {
  87              $remote_socket = 'tcp://' . $host;
  88          }
  89  
  90          $proxy = isset( $options['proxy'] );
  91          $proxy_auth = $proxy && isset( $options['proxy_username'] ) && isset( $options['proxy_password'] );
  92  
  93          if (!isset($url_parts['port'])) {
  94              $url_parts['port'] = 80;
  95          }
  96          $remote_socket .= ':' . $url_parts['port'];
  97  
  98          set_error_handler(array($this, 'connect_error_handler'), E_WARNING | E_NOTICE);
  99  
 100          $options['hooks']->dispatch('fsockopen.remote_socket', array(&$remote_socket));
 101  
 102          $fp = stream_socket_client($remote_socket, $errno, $errstr, $options['timeout'], STREAM_CLIENT_CONNECT, $context);
 103  
 104          restore_error_handler();
 105  
 106          if ($verifyname) {
 107              if (!$this->verify_certificate_from_context($host, $context)) {
 108                  throw new Requests_Exception('SSL certificate did not match the requested domain name', 'ssl.no_match');
 109              }
 110          }
 111  
 112          if (!$fp) {
 113              if ($errno === 0) {
 114                  // Connection issue
 115                  throw new Requests_Exception(rtrim($this->connect_error), 'fsockopen.connect_error');
 116              }
 117              else {
 118                  throw new Requests_Exception($errstr, 'fsockopenerror');
 119                  return;
 120              }
 121          }
 122  
 123          $request_body = '';
 124          $out = '';
 125          switch ($options['type']) {
 126              case Requests::POST:
 127              case Requests::PUT:
 128              case Requests::PATCH:
 129                  if (isset($url_parts['path'])) {
 130                      $path = $url_parts['path'];
 131                      if (isset($url_parts['query'])) {
 132                          $path .= '?' . $url_parts['query'];
 133                      }
 134                  }
 135                  else {
 136                      $path = '/';
 137                  }
 138  
 139                  $options['hooks']->dispatch( 'fsockopen.remote_host_path', array( &$path, $url ) );
 140                  $out = $options['type'] . " $path HTTP/1.0\r\n";
 141  
 142                  if (is_array($data)) {
 143                      $request_body = http_build_query($data, null, '&');
 144                  }
 145                  else {
 146                      $request_body = $data;
 147                  }
 148                  if (empty($headers['Content-Length'])) {
 149                      $headers['Content-Length'] = strlen($request_body);
 150                  }
 151                  if (empty($headers['Content-Type'])) {
 152                      $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
 153                  }
 154                  break;
 155              case Requests::HEAD:
 156              case Requests::GET:
 157              case Requests::DELETE:
 158                  $path = self::format_get($url_parts, $data);
 159                  $options['hooks']->dispatch('fsockopen.remote_host_path', array(&$path, $url));
 160                  $out = $options['type'] . " $path HTTP/1.0\r\n";
 161                  break;
 162          }
 163          $out .= "Host: {$url_parts['host']}";
 164  
 165          if ($url_parts['port'] !== 80) {
 166              $out .= ":{$url_parts['port']}";
 167          }
 168          $out .= "\r\n";
 169  
 170          $out .= "User-Agent: {$options['useragent']}\r\n";
 171          $accept_encoding = $this->accept_encoding();
 172          if (!empty($accept_encoding)) {
 173              $out .= "Accept-Encoding: $accept_encoding\r\n";
 174          }
 175  
 176          $headers = Requests::flatten($headers);
 177  
 178          if (!empty($headers)) {
 179              $out .= implode($headers, "\r\n") . "\r\n";
 180          }
 181  
 182          $options['hooks']->dispatch('fsockopen.after_headers', array(&$out));
 183  
 184          if (substr($out, -2) !== "\r\n") {
 185              $out .= "\r\n";
 186          }
 187  
 188          $out .= "Connection: Close\r\n\r\n" . $request_body;
 189  
 190          $options['hooks']->dispatch('fsockopen.before_send', array(&$out));
 191  
 192          fwrite($fp, $out);
 193          $options['hooks']->dispatch('fsockopen.after_send', array(&$fake_headers));
 194  
 195          if (!$options['blocking']) {
 196              fclose($fp);
 197              $fake_headers = '';
 198              $options['hooks']->dispatch('fsockopen.after_request', array(&$fake_headers));
 199              return '';
 200          }
 201          stream_set_timeout($fp, $options['timeout']);
 202  
 203          $this->info = stream_get_meta_data($fp);
 204  
 205          $this->headers = '';
 206          $this->info = stream_get_meta_data($fp);
 207          if (!$options['filename']) {
 208              while (!feof($fp)) {
 209                  $this->info = stream_get_meta_data($fp);
 210                  if ($this->info['timed_out']) {
 211                      throw new Requests_Exception('fsocket timed out', 'timeout');
 212                  }
 213  
 214                  $this->headers .= fread($fp, 1160);
 215              }
 216          }
 217          else {
 218              $download = fopen($options['filename'], 'wb');
 219              $doingbody = false;
 220              $response = '';
 221              while (!feof($fp)) {
 222                  $this->info = stream_get_meta_data($fp);
 223                  if ($this->info['timed_out']) {
 224                      throw new Requests_Exception('fsocket timed out', 'timeout');
 225                  }
 226  
 227                  $block = fread($fp, 1160);
 228                  if ($doingbody) {
 229                      fwrite($download, $block);
 230                  }
 231                  else {
 232                      $response .= $block;
 233                      if (strpos($response, "\r\n\r\n")) {
 234                          list($this->headers, $block) = explode("\r\n\r\n", $response, 2);
 235                          $doingbody = true;
 236                          fwrite($download, $block);
 237                      }
 238                  }
 239              }
 240              fclose($download);
 241          }
 242          fclose($fp);
 243  
 244          $options['hooks']->dispatch('fsockopen.after_request', array(&$this->headers));
 245          return $this->headers;
 246      }
 247  
 248      /**
 249       * Send multiple requests simultaneously
 250       *
 251       * @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see Requests_Transport::request}
 252       * @param array $options Global options, see {@see Requests::response()} for documentation
 253       * @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well)
 254       */
 255  	public function request_multiple($requests, $options) {
 256          $responses = array();
 257          $class = get_class($this);
 258          foreach ($requests as $id => $request) {
 259              try {
 260                  $handler = new $class();
 261                  $responses[$id] = $handler->request($request['url'], $request['headers'], $request['data'], $request['options']);
 262  
 263                  $request['options']['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$id], $request));
 264              }
 265              catch (Requests_Exception $e) {
 266                  $responses[$id] = $e;
 267              }
 268  
 269              if (!is_string($responses[$id])) {
 270                  $request['options']['hooks']->dispatch('multiple.request.complete', array(&$responses[$id], $id));
 271              }
 272          }
 273  
 274          return $responses;
 275      }
 276  
 277      /**
 278       * Retrieve the encodings we can accept
 279       *
 280       * @return string Accept-Encoding header value
 281       */
 282  	protected static function accept_encoding() {
 283          $type = array();
 284          if (function_exists('gzinflate')) {
 285              $type[] = 'deflate;q=1.0';
 286          }
 287  
 288          if (function_exists('gzuncompress')) {
 289              $type[] = 'compress;q=0.5';
 290          }
 291  
 292          $type[] = 'gzip;q=0.5';
 293  
 294          return implode(', ', $type);
 295      }
 296  
 297      /**
 298       * Format a URL given GET data
 299       *
 300       * @param array $url_parts
 301       * @param array|object $data Data to build query using, see {@see http://php.net/http_build_query}
 302       * @return string URL with data
 303       */
 304  	protected static function format_get($url_parts, $data) {
 305          if (!empty($data)) {
 306              if (empty($url_parts['query']))
 307                  $url_parts['query'] = '';
 308  
 309              $url_parts['query'] .= '&' . http_build_query($data, null, '&');
 310              $url_parts['query'] = trim($url_parts['query'], '&');
 311          }
 312          if (isset($url_parts['path'])) {
 313              if (isset($url_parts['query'])) {
 314                  $get = $url_parts['path'] . '?' . $url_parts['query'];
 315              }
 316              else {
 317                  $get = $url_parts['path'];
 318              }
 319          }
 320          else {
 321              $get = '/';
 322          }
 323          return $get;
 324      }
 325  
 326      /**
 327       * Error handler for stream_socket_client()
 328       *
 329       * @param int $errno Error number (e.g. E_WARNING)
 330       * @param string $errstr Error message
 331       */
 332  	public function connect_error_handler($errno, $errstr) {
 333          // Double-check we can handle it
 334          if (($errno & E_WARNING) === 0 && ($errno & E_NOTICE) === 0) {
 335              // Return false to indicate the default error handler should engage
 336              return false;
 337          }
 338  
 339          $this->connect_error .= $errstr . "\n";
 340          return true;
 341      }
 342  
 343      /**
 344       * Verify the certificate against common name and subject alternative names
 345       *
 346       * Unfortunately, PHP doesn't check the certificate against the alternative
 347       * names, leading things like 'https://www.github.com/' to be invalid.
 348       * Instead
 349       *
 350       * @see http://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1
 351       *
 352       * @throws Requests_Exception On failure to connect via TLS (`fsockopen.ssl.connect_error`)
 353       * @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`)
 354       * @param string $host Host name to verify against
 355       * @param resource $context Stream context
 356       * @return bool
 357       */
 358  	public function verify_certificate_from_context($host, $context) {
 359          $meta = stream_context_get_options($context);
 360  
 361          // If we don't have SSL options, then we couldn't make the connection at
 362          // all
 363          if (empty($meta) || empty($meta['ssl']) || empty($meta['ssl']['peer_certificate'])) {
 364              throw new Requests_Exception(rtrim($this->connect_error), 'ssl.connect_error');
 365          }
 366  
 367          $cert = openssl_x509_parse($meta['ssl']['peer_certificate']);
 368  
 369          return Requests_SSL::verify_certificate($host, $cert);
 370      }
 371  
 372      /**
 373       * Whether this transport is valid
 374       *
 375       * @codeCoverageIgnore
 376       * @return boolean True if the transport is valid, false otherwise.
 377       */
 378  	public static function test() {
 379          return function_exists('fsockopen');
 380      }
 381  }

title

Description

title

Description

title

Description

title

title

Body