Ampache PHP Cross Reference Groupware Applications

Source: /modules/Guzzle/Http/Message/Request.php - 638 lines - 19332 bytes - Summary - Text - Print

   1  <?php
   2  
   3  namespace Guzzle\Http\Message;
   4  
   5  use Guzzle\Common\Version;
   6  use Guzzle\Common\Event;
   7  use Guzzle\Common\Collection;
   8  use Guzzle\Common\Exception\RuntimeException;
   9  use Guzzle\Common\Exception\InvalidArgumentException;
  10  use Guzzle\Http\Exception\RequestException;
  11  use Guzzle\Http\Exception\BadResponseException;
  12  use Guzzle\Http\ClientInterface;
  13  use Guzzle\Http\EntityBody;
  14  use Guzzle\Http\EntityBodyInterface;
  15  use Guzzle\Http\Message\Header\HeaderInterface;
  16  use Guzzle\Http\Url;
  17  use Guzzle\Parser\ParserRegistry;
  18  use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  19  use Symfony\Component\EventDispatcher\EventDispatcher;
  20  use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  21  
  22  /**
  23   * HTTP request class to send requests
  24   */
  25  class Request extends AbstractMessage implements RequestInterface
  26  {
  27      /** @var EventDispatcherInterface */
  28      protected $eventDispatcher;
  29  
  30      /** @var Url HTTP Url */
  31      protected $url;
  32  
  33      /** @var string HTTP method (GET, PUT, POST, DELETE, HEAD, OPTIONS, TRACE) */
  34      protected $method;
  35  
  36      /** @var ClientInterface */
  37      protected $client;
  38  
  39      /** @var Response Response of the request */
  40      protected $response;
  41  
  42      /** @var EntityBodyInterface Response body */
  43      protected $responseBody;
  44  
  45      /** @var string State of the request object */
  46      protected $state;
  47  
  48      /** @var string Authentication username */
  49      protected $username;
  50  
  51      /** @var string Auth password */
  52      protected $password;
  53  
  54      /** @var Collection cURL specific transfer options */
  55      protected $curlOptions;
  56  
  57      /** @var bool */
  58      protected $isRedirect = false;
  59  
  60      public static function getAllEvents()
  61      {
  62          return array(
  63              // Called when receiving or uploading data through cURL
  64              'curl.callback.read', 'curl.callback.write', 'curl.callback.progress',
  65              // Cloning a request
  66              'request.clone',
  67              // About to send the request, sent request, completed transaction
  68              'request.before_send', 'request.sent', 'request.complete',
  69              // A request received a successful response
  70              'request.success',
  71              // A request received an unsuccessful response
  72              'request.error',
  73              // An exception is being thrown because of an unsuccessful response
  74              'request.exception',
  75              // Received response status line
  76              'request.receive.status_line'
  77          );
  78      }
  79  
  80      /**
  81       * @param string           $method  HTTP method
  82       * @param string|Url       $url     HTTP URL to connect to. The URI scheme, host header, and URI are parsed from the
  83       *                                  full URL. If query string parameters are present they will be parsed as well.
  84       * @param array|Collection $headers HTTP headers
  85       */
  86      public function __construct($method, $url, $headers = array())
  87      {
  88          parent::__construct();
  89          $this->method = strtoupper($method);
  90          $this->curlOptions = new Collection();
  91          $this->setUrl($url);
  92  
  93          if ($headers) {
  94              // Special handling for multi-value headers
  95              foreach ($headers as $key => $value) {
  96                  // Deal with collisions with Host and Authorization
  97                  if ($key == 'host' || $key == 'Host') {
  98                      $this->setHeader($key, $value);
  99                  } elseif ($value instanceof HeaderInterface) {
 100                      $this->addHeader($key, $value);
 101                  } else {
 102                      foreach ((array) $value as $v) {
 103                          $this->addHeader($key, $v);
 104                      }
 105                  }
 106              }
 107          }
 108  
 109          $this->setState(self::STATE_NEW);
 110      }
 111  
 112      public function __clone()
 113      {
 114          if ($this->eventDispatcher) {
 115              $this->eventDispatcher = clone $this->eventDispatcher;
 116          }
 117          $this->curlOptions = clone $this->curlOptions;
 118          $this->params = clone $this->params;
 119          $this->url = clone $this->url;
 120          $this->response = $this->responseBody = null;
 121          $this->headers = clone $this->headers;
 122  
 123          $this->setState(RequestInterface::STATE_NEW);
 124          $this->dispatch('request.clone', array('request' => $this));
 125      }
 126  
 127      /**
 128       * Get the HTTP request as a string
 129       *
 130       * @return string
 131       */
 132      public function __toString()
 133      {
 134          return $this->getRawHeaders() . "\r\n\r\n";
 135      }
 136  
 137      /**
 138       * Default method that will throw exceptions if an unsuccessful response is received.
 139       *
 140       * @param Event $event Received
 141       * @throws BadResponseException if the response is not successful
 142       */
 143      public static function onRequestError(Event $event)
 144      {
 145          $e = BadResponseException::factory($event['request'], $event['response']);
 146          $event['request']->setState(self::STATE_ERROR, array('exception' => $e) + $event->toArray());
 147          throw $e;
 148      }
 149  
 150      public function setClient(ClientInterface $client)
 151      {
 152          $this->client = $client;
 153  
 154          return $this;
 155      }
 156  
 157      public function getClient()
 158      {
 159          return $this->client;
 160      }
 161  
 162      public function getRawHeaders()
 163      {
 164          $protocolVersion = $this->protocolVersion ?: '1.1';
 165  
 166          return trim($this->method . ' ' . $this->getResource()) . ' '
 167              . strtoupper(str_replace('https', 'http', $this->url->getScheme()))
 168              . '/' . $protocolVersion . "\r\n" . implode("\r\n", $this->getHeaderLines());
 169      }
 170  
 171      public function setUrl($url)
 172      {
 173          if ($url instanceof Url) {
 174              $this->url = $url;
 175          } else {
 176              $this->url = Url::factory($url);
 177          }
 178  
 179          // Update the port and host header
 180          $this->setPort($this->url->getPort());
 181  
 182          if ($this->url->getUsername() || $this->url->getPassword()) {
 183              $this->setAuth($this->url->getUsername(), $this->url->getPassword());
 184              // Remove the auth info from the URL
 185              $this->url->setUsername(null);
 186              $this->url->setPassword(null);
 187          }
 188  
 189          return $this;
 190      }
 191  
 192      public function send()
 193      {
 194          if (!$this->client) {
 195              throw new RuntimeException('A client must be set on the request');
 196          }
 197  
 198          return $this->client->send($this);
 199      }
 200  
 201      public function getResponse()
 202      {
 203          return $this->response;
 204      }
 205  
 206      public function getQuery($asString = false)
 207      {
 208          return $asString
 209              ? (string) $this->url->getQuery()
 210              : $this->url->getQuery();
 211      }
 212  
 213      public function getMethod()
 214      {
 215          return $this->method;
 216      }
 217  
 218      public function getScheme()
 219      {
 220          return $this->url->getScheme();
 221      }
 222  
 223      public function setScheme($scheme)
 224      {
 225          $this->url->setScheme($scheme);
 226  
 227          return $this;
 228      }
 229  
 230      public function getHost()
 231      {
 232          return $this->url->getHost();
 233      }
 234  
 235      public function setHost($host)
 236      {
 237          $this->url->setHost($host);
 238          $this->setPort($this->url->getPort());
 239  
 240          return $this;
 241      }
 242  
 243      public function getProtocolVersion()
 244      {
 245          return $this->protocolVersion;
 246      }
 247  
 248      public function setProtocolVersion($protocol)
 249      {
 250          $this->protocolVersion = $protocol;
 251  
 252          return $this;
 253      }
 254  
 255      public function getPath()
 256      {
 257          return '/' . ltrim($this->url->getPath(), '/');
 258      }
 259  
 260      public function setPath($path)
 261      {
 262          $this->url->setPath($path);
 263  
 264          return $this;
 265      }
 266  
 267      public function getPort()
 268      {
 269          return $this->url->getPort();
 270      }
 271  
 272      public function setPort($port)
 273      {
 274          $this->url->setPort($port);
 275  
 276          // Include the port in the Host header if it is not the default port for the scheme of the URL
 277          $scheme = $this->url->getScheme();
 278          if (($scheme == 'http' && $port != 80) || ($scheme == 'https' && $port != 443)) {
 279              $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost() . ':' . $port);
 280          } else {
 281              $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost());
 282          }
 283  
 284          return $this;
 285      }
 286  
 287      public function getUsername()
 288      {
 289          return $this->username;
 290      }
 291  
 292      public function getPassword()
 293      {
 294          return $this->password;
 295      }
 296  
 297      public function setAuth($user, $password = '', $scheme = CURLAUTH_BASIC)
 298      {
 299          static $authMap = array(
 300              'basic'  => CURLAUTH_BASIC,
 301              'digest' => CURLAUTH_DIGEST,
 302              'ntlm'   => CURLAUTH_NTLM,
 303              'any'    => CURLAUTH_ANY
 304          );
 305  
 306          // If we got false or null, disable authentication
 307          if (!$user) {
 308              $this->password = $this->username = null;
 309              $this->removeHeader('Authorization');
 310              $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH);
 311              return $this;
 312          }
 313  
 314          if (!is_numeric($scheme)) {
 315              $scheme = strtolower($scheme);
 316              if (!isset($authMap[$scheme])) {
 317                  throw new InvalidArgumentException($scheme . ' is not a valid authentication type');
 318              }
 319              $scheme = $authMap[$scheme];
 320          }
 321  
 322          $this->username = $user;
 323          $this->password = $password;
 324  
 325          // Bypass CURL when using basic auth to promote connection reuse
 326          if ($scheme == CURLAUTH_BASIC) {
 327              $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH);
 328              $this->setHeader('Authorization', 'Basic ' . base64_encode($this->username . ':' . $this->password));
 329          } else {
 330              $this->getCurlOptions()
 331                  ->set(CURLOPT_HTTPAUTH, $scheme)
 332                  ->set(CURLOPT_USERPWD, $this->username . ':' . $this->password);
 333          }
 334  
 335          return $this;
 336      }
 337  
 338      public function getResource()
 339      {
 340          $resource = $this->getPath();
 341          if ($query = (string) $this->url->getQuery()) {
 342              $resource .= '?' . $query;
 343          }
 344  
 345          return $resource;
 346      }
 347  
 348      public function getUrl($asObject = false)
 349      {
 350          return $asObject ? clone $this->url : (string) $this->url;
 351      }
 352  
 353      public function getState()
 354      {
 355          return $this->state;
 356      }
 357  
 358      public function setState($state, array $context = array())
 359      {
 360          $oldState = $this->state;
 361          $this->state = $state;
 362  
 363          switch ($state) {
 364              case self::STATE_NEW:
 365                  $this->response = null;
 366                  break;
 367              case self::STATE_TRANSFER:
 368                  if ($oldState !== $state) {
 369                      // Fix Content-Length and Transfer-Encoding collisions
 370                      if ($this->hasHeader('Transfer-Encoding') && $this->hasHeader('Content-Length')) {
 371                          $this->removeHeader('Transfer-Encoding');
 372                      }
 373                      $this->dispatch('request.before_send', array('request' => $this));
 374                  }
 375                  break;
 376              case self::STATE_COMPLETE:
 377                  if ($oldState !== $state) {
 378                      $this->processResponse($context);
 379                      $this->responseBody = null;
 380                  }
 381                  break;
 382              case self::STATE_ERROR:
 383                  if (isset($context['exception'])) {
 384                      $this->dispatch('request.exception', array(
 385                          'request'   => $this,
 386                          'response'  => isset($context['response']) ? $context['response'] : $this->response,
 387                          'exception' => isset($context['exception']) ? $context['exception'] : null
 388                      ));
 389                  }
 390          }
 391  
 392          return $this->state;
 393      }
 394  
 395      public function getCurlOptions()
 396      {
 397          return $this->curlOptions;
 398      }
 399  
 400      public function startResponse(Response $response)
 401      {
 402          $this->state = self::STATE_TRANSFER;
 403          $response->setEffectiveUrl((string) $this->getUrl());
 404          $this->response = $response;
 405  
 406          return $this;
 407      }
 408  
 409      public function setResponse(Response $response, $queued = false)
 410      {
 411          $response->setEffectiveUrl((string) $this->url);
 412  
 413          if ($queued) {
 414              $ed = $this->getEventDispatcher();
 415              $ed->addListener('request.before_send', $f = function ($e) use ($response, &$f, $ed) {
 416                  $e['request']->setResponse($response);
 417                  $ed->removeListener('request.before_send', $f);
 418              }, -9999);
 419          } else {
 420              $this->response = $response;
 421              // If a specific response body is specified, then use it instead of the response's body
 422              if ($this->responseBody && !$this->responseBody->getCustomData('default') && !$response->isRedirect()) {
 423                  $this->getResponseBody()->write((string) $this->response->getBody());
 424              } else {
 425                  $this->responseBody = $this->response->getBody();
 426              }
 427              $this->setState(self::STATE_COMPLETE);
 428          }
 429  
 430          return $this;
 431      }
 432  
 433      public function setResponseBody($body)
 434      {
 435          // Attempt to open a file for writing if a string was passed
 436          if (is_string($body)) {
 437              // @codeCoverageIgnoreStart
 438              if (!($body = fopen($body, 'w+'))) {
 439                  throw new InvalidArgumentException('Could not open ' . $body . ' for writing');
 440              }
 441              // @codeCoverageIgnoreEnd
 442          }
 443  
 444          $this->responseBody = EntityBody::factory($body);
 445  
 446          return $this;
 447      }
 448  
 449      public function getResponseBody()
 450      {
 451          if ($this->responseBody === null) {
 452              $this->responseBody = EntityBody::factory()->setCustomData('default', true);
 453          }
 454  
 455          return $this->responseBody;
 456      }
 457  
 458      /**
 459       * Determine if the response body is repeatable (readable + seekable)
 460       *
 461       * @return bool
 462       * @deprecated Use getResponseBody()->isSeekable()
 463       * @codeCoverageIgnore
 464       */
 465      public function isResponseBodyRepeatable()
 466      {
 467          Version::warn(__METHOD__ . ' is deprecated. Use $request->getResponseBody()->isRepeatable()');
 468          return !$this->responseBody ? true : $this->responseBody->isRepeatable();
 469      }
 470  
 471      public function getCookies()
 472      {
 473          if ($cookie = $this->getHeader('Cookie')) {
 474              $data = ParserRegistry::getInstance()->getParser('cookie')->parseCookie($cookie);
 475              return $data['cookies'];
 476          }
 477  
 478          return array();
 479      }
 480  
 481      public function getCookie($name)
 482      {
 483          $cookies = $this->getCookies();
 484  
 485          return isset($cookies[$name]) ? $cookies[$name] : null;
 486      }
 487  
 488      public function addCookie($name, $value)
 489      {
 490          if (!$this->hasHeader('Cookie')) {
 491              $this->setHeader('Cookie', "{$name}={$value}");
 492          } else {
 493              $this->getHeader('Cookie')->add("{$name}={$value}");
 494          }
 495  
 496          // Always use semicolons to separate multiple cookie headers
 497          $this->getHeader('Cookie')->setGlue(';');
 498  
 499          return $this;
 500      }
 501  
 502      public function removeCookie($name)
 503      {
 504          if ($cookie = $this->getHeader('Cookie')) {
 505              foreach ($cookie as $cookieValue) {
 506                  if (strpos($cookieValue, $name . '=') === 0) {
 507                      $cookie->removeValue($cookieValue);
 508                  }
 509              }
 510          }
 511  
 512          return $this;
 513      }
 514  
 515      public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
 516      {
 517          $this->eventDispatcher = $eventDispatcher;
 518          $this->eventDispatcher->addListener('request.error', array(__CLASS__, 'onRequestError'), -255);
 519  
 520          return $this;
 521      }
 522  
 523      public function getEventDispatcher()
 524      {
 525          if (!$this->eventDispatcher) {
 526              $this->setEventDispatcher(new EventDispatcher());
 527          }
 528  
 529          return $this->eventDispatcher;
 530      }
 531  
 532      public function dispatch($eventName, array $context = array())
 533      {
 534          $context['request'] = $this;
 535  
 536          return $this->getEventDispatcher()->dispatch($eventName, new Event($context));
 537      }
 538  
 539      public function addSubscriber(EventSubscriberInterface $subscriber)
 540      {
 541          $this->getEventDispatcher()->addSubscriber($subscriber);
 542  
 543          return $this;
 544      }
 545  
 546      /**
 547       * Get an array containing the request and response for event notifications
 548       *
 549       * @return array
 550       */
 551      protected function getEventArray()
 552      {
 553          return array(
 554              'request'  => $this,
 555              'response' => $this->response
 556          );
 557      }
 558  
 559      /**
 560       * Process a received response
 561       *
 562       * @param array $context Contextual information
 563       * @throws RequestException|BadResponseException on unsuccessful responses
 564       */
 565      protected function processResponse(array $context = array())
 566      {
 567          if (!$this->response) {
 568              // If no response, then processResponse shouldn't have been called
 569              $e = new RequestException('Error completing request');
 570              $e->setRequest($this);
 571              throw $e;
 572          }
 573  
 574          $this->state = self::STATE_COMPLETE;
 575  
 576          // A request was sent, but we don't know if we'll send more or if the final response will be successful
 577          $this->dispatch('request.sent', $this->getEventArray() + $context);
 578  
 579          // Some response processors will remove the response or reset the state (example: ExponentialBackoffPlugin)
 580          if ($this->state == RequestInterface::STATE_COMPLETE) {
 581  
 582              // The request completed, so the HTTP transaction is complete
 583              $this->dispatch('request.complete', $this->getEventArray());
 584  
 585              // If the response is bad, allow listeners to modify it or throw exceptions. You can change the response by
 586              // modifying the Event object in your listeners or calling setResponse() on the request
 587              if ($this->response->isError()) {
 588                  $event = new Event($this->getEventArray());
 589                  $this->getEventDispatcher()->dispatch('request.error', $event);
 590                  // Allow events of request.error to quietly change the response
 591                  if ($event['response'] !== $this->response) {
 592                      $this->response = $event['response'];
 593                  }
 594              }
 595  
 596              // If a successful response was received, dispatch an event
 597              if ($this->response->isSuccessful()) {
 598                  $this->dispatch('request.success', $this->getEventArray());
 599              }
 600          }
 601      }
 602  
 603      /**
 604       * @deprecated Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy
 605       * @codeCoverageIgnore
 606       */
 607      public function canCache()
 608      {
 609          Version::warn(__METHOD__ . ' is deprecated. Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy.');
 610          if (class_exists('Guzzle\Plugin\Cache\DefaultCanCacheStrategy')) {
 611              $canCache = new \Guzzle\Plugin\Cache\DefaultCanCacheStrategy();
 612              return $canCache->canCacheRequest($this);
 613          } else {
 614              return false;
 615          }
 616      }
 617  
 618      /**
 619       * @deprecated Use the history plugin (not emitting a warning as this is built-into the RedirectPlugin for now)
 620       * @codeCoverageIgnore
 621       */
 622      public function setIsRedirect($isRedirect)
 623      {
 624          $this->isRedirect = $isRedirect;
 625  
 626          return $this;
 627      }
 628  
 629      /**
 630       * @deprecated Use the history plugin
 631       * @codeCoverageIgnore
 632       */
 633      public function isRedirect()
 634      {
 635          Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin to track this.');
 636          return $this->isRedirect;
 637      }
 638  }

title

Description

title

Description

title

Description

title

title

Body