Open Journal Systems  3.3.0
RedirectPlugin.php
1 <?php
2 
3 namespace Guzzle\Http;
4 
15 
20 {
21  const REDIRECT_COUNT = 'redirect.count';
22  const MAX_REDIRECTS = 'redirect.max';
23  const STRICT_REDIRECTS = 'redirect.strict';
24  const PARENT_REQUEST = 'redirect.parent_request';
25  const DISABLE = 'redirect.disable';
26 
30  protected $defaultMaxRedirects = 5;
31 
32  public static function getSubscribedEvents()
33  {
34  return array(
35  'request.sent' => array('onRequestSent', 100),
36  'request.clone' => 'cleanupRequest',
37  'request.before_send' => 'cleanupRequest'
38  );
39  }
40 
46  public function cleanupRequest(Event $event)
47  {
48  $params = $event['request']->getParams();
49  unset($params[self::REDIRECT_COUNT]);
50  unset($params[self::PARENT_REQUEST]);
51  }
52 
58  public function onRequestSent(Event $event)
59  {
60  $response = $event['response'];
61  $request = $event['request'];
62 
63  // Only act on redirect requests with Location headers
64  if (!$response || $request->getParams()->get(self::DISABLE)) {
65  return;
66  }
67 
68  // Trace the original request based on parameter history
69  $original = $this->getOriginalRequest($request);
70 
71  // Terminating condition to set the effective response on the original request
72  if (!$response->isRedirect() || !$response->hasHeader('Location')) {
73  if ($request !== $original) {
74  // This is a terminating redirect response, so set it on the original request
75  $response->getParams()->set(self::REDIRECT_COUNT, $original->getParams()->get(self::REDIRECT_COUNT));
76  $original->setResponse($response);
77  $response->setEffectiveUrl($request->getUrl());
78  }
79  return;
80  }
81 
82  $this->sendRedirectRequest($original, $request, $response);
83  }
84 
92  protected function getOriginalRequest(RequestInterface $request)
93  {
94  $original = $request;
95  // The number of redirects is held on the original request, so determine which request that is
96  while ($parent = $original->getParams()->get(self::PARENT_REQUEST)) {
97  $original = $parent;
98  }
99 
100  return $original;
101  }
102 
117  protected function createRedirectRequest(
118  RequestInterface $request,
119  $statusCode,
120  $location,
121  RequestInterface $original
122  ) {
123  $redirectRequest = null;
124  $strict = $original->getParams()->get(self::STRICT_REDIRECTS);
125 
126  // Switch method to GET for 303 redirects. 301 and 302 redirects also switch to GET unless we are forcing RFC
127  // compliance to emulate what most browsers do. NOTE: IE only switches methods on 301/302 when coming from a POST.
128  if ($request instanceof EntityEnclosingRequestInterface && ($statusCode == 303 || (!$strict && $statusCode <= 302))) {
129  $redirectRequest = RequestFactory::getInstance()->cloneRequestWithMethod($request, 'GET');
130  } else {
131  $redirectRequest = clone $request;
132  }
133 
134  $redirectRequest->setIsRedirect(true);
135  // Always use the same response body when redirecting
136  $redirectRequest->setResponseBody($request->getResponseBody());
137 
138  $location = Url::factory($location);
139  // If the location is not absolute, then combine it with the original URL
140  if (!$location->isAbsolute()) {
141  $originalUrl = $redirectRequest->getUrl(true);
142  // Remove query string parameters and just take what is present on the redirect Location header
143  $originalUrl->getQuery()->clear();
144  $location = $originalUrl->combine((string) $location, true);
145  }
146 
147  $redirectRequest->setUrl($location);
148 
149  // Add the parent request to the request before it sends (make sure it's before the onRequestClone event too)
150  $redirectRequest->getEventDispatcher()->addListener(
151  'request.before_send',
152  $func = function ($e) use (&$func, $request, $redirectRequest) {
153  $redirectRequest->getEventDispatcher()->removeListener('request.before_send', $func);
154  $e['request']->getParams()->set(RedirectPlugin::PARENT_REQUEST, $request);
155  }
156  );
157 
158  // Rewind the entity body of the request if needed
159  if ($redirectRequest instanceof EntityEnclosingRequestInterface && $redirectRequest->getBody()) {
160  $body = $redirectRequest->getBody();
161  // Only rewind the body if some of it has been read already, and throw an exception if the rewind fails
162  if ($body->ftell() && !$body->rewind()) {
164  'Unable to rewind the non-seekable entity body of the request after redirecting. cURL probably '
165  . 'sent part of body before the redirect occurred. Try adding acustom rewind function using on the '
166  . 'entity body of the request using setRewindFunction().'
167  );
168  }
169  }
170 
171  return $redirectRequest;
172  }
173 
183  protected function prepareRedirection(RequestInterface $original, RequestInterface $request, Response $response)
184  {
185  $params = $original->getParams();
186  // This is a new redirect, so increment the redirect counter
187  $current = $params[self::REDIRECT_COUNT] + 1;
188  $params[self::REDIRECT_COUNT] = $current;
189  // Use a provided maximum value or default to a max redirect count of 5
190  $max = isset($params[self::MAX_REDIRECTS]) ? $params[self::MAX_REDIRECTS] : $this->defaultMaxRedirects;
191 
192  // Throw an exception if the redirect count is exceeded
193  if ($current > $max) {
194  $this->throwTooManyRedirectsException($original, $max);
195  return false;
196  } else {
197  // Create a redirect request based on the redirect rules set on the request
198  return $this->createRedirectRequest(
199  $request,
200  $response->getStatusCode(),
201  trim($response->getLocation()),
202  $original
203  );
204  }
205  }
206 
216  protected function sendRedirectRequest(RequestInterface $original, RequestInterface $request, Response $response)
217  {
218  // Validate and create a redirect request based on the original request and current response
219  if ($redirectRequest = $this->prepareRedirection($original, $request, $response)) {
220  try {
221  $redirectRequest->send();
222  } catch (BadResponseException $e) {
223  $e->getResponse();
224  if (!$e->getResponse()) {
225  throw $e;
226  }
227  }
228  }
229  }
230 
239  protected function throwTooManyRedirectsException(RequestInterface $original, $max)
240  {
241  $original->getEventDispatcher()->addListener(
242  'request.complete',
243  $func = function ($e) use (&$func, $original, $max) {
244  $original->getEventDispatcher()->removeListener('request.complete', $func);
245  $str = "{$max} redirects were issued for this request:\n" . $e['request']->getRawHeaders();
246  throw new TooManyRedirectsException($str);
247  }
248  );
249  }
250 }
Guzzle\Http\Exception\CouldNotRewindStreamException
Definition: CouldNotRewindStreamException.php:7
Guzzle\Http\Message\RequestInterface
Definition: lib/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestInterface.php:16
Guzzle\Http\RedirectPlugin\cleanupRequest
cleanupRequest(Event $event)
Definition: RedirectPlugin.php:49
Guzzle\Http\RedirectPlugin\throwTooManyRedirectsException
throwTooManyRedirectsException(RequestInterface $original, $max)
Definition: RedirectPlugin.php:242
Guzzle\Http\RedirectPlugin\onRequestSent
onRequestSent(Event $event)
Definition: RedirectPlugin.php:61
Symfony\Component\EventDispatcher\EventSubscriberInterface
Definition: lib/vendor/symfony/event-dispatcher/EventSubscriberInterface.php:25
Guzzle\Http\RedirectPlugin\getOriginalRequest
getOriginalRequest(RequestInterface $request)
Definition: RedirectPlugin.php:95
Guzzle\Http\Message\EntityEnclosingRequestInterface
Definition: EntityEnclosingRequestInterface.php:12
Guzzle\Http\Message\RequestInterface\setResponseBody
setResponseBody($body)
Guzzle\Http\Message\Response
Definition: lib/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Response.php:17
Guzzle\Http\RedirectPlugin\createRedirectRequest
createRedirectRequest(RequestInterface $request, $statusCode, $location, RequestInterface $original)
Definition: RedirectPlugin.php:120
Guzzle\Http\Url
Definition: Url.php:10
Guzzle\Http\RedirectPlugin\REDIRECT_COUNT
const REDIRECT_COUNT
Definition: RedirectPlugin.php:21
Guzzle\Http\RedirectPlugin\prepareRedirection
prepareRedirection(RequestInterface $original, RequestInterface $request, Response $response)
Definition: RedirectPlugin.php:186
Guzzle\Http\RedirectPlugin\MAX_REDIRECTS
const MAX_REDIRECTS
Definition: RedirectPlugin.php:22
Guzzle\Common\Event
Definition: lib/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php:10
Guzzle\Http\Message\RequestInterface\getResponseBody
getResponseBody()
Guzzle\Http\RedirectPlugin\sendRedirectRequest
sendRedirectRequest(RequestInterface $original, RequestInterface $request, Response $response)
Definition: RedirectPlugin.php:219
Guzzle\Common\HasDispatcherInterface\getEventDispatcher
getEventDispatcher()
Guzzle\Http
Definition: AbstractEntityBodyDecorator.php:3
Guzzle\Http\RedirectPlugin\STRICT_REDIRECTS
const STRICT_REDIRECTS
Definition: RedirectPlugin.php:23
Guzzle\Http\Message\RequestFactory
Definition: lib/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php:14
Guzzle\Http\Exception\BadResponseException\getResponse
getResponse()
Definition: lib/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/BadResponseException.php:68
Guzzle\Http\Message\MessageInterface\getParams
getParams()
Guzzle\Http\RedirectPlugin\$defaultMaxRedirects
$defaultMaxRedirects
Definition: RedirectPlugin.php:33
Guzzle\Http\Exception\BadResponseException
Definition: lib/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/BadResponseException.php:11
Guzzle\Http\Message\RequestFactory\getInstance
static getInstance()
Definition: lib/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php:42
Guzzle\Http\RedirectPlugin\PARENT_REQUEST
const PARENT_REQUEST
Definition: RedirectPlugin.php:24
Guzzle\Http\RedirectPlugin\DISABLE
const DISABLE
Definition: RedirectPlugin.php:25
Guzzle\Http\RedirectPlugin
Definition: RedirectPlugin.php:19
Guzzle\Http\RedirectPlugin\getSubscribedEvents
static getSubscribedEvents()
Definition: RedirectPlugin.php:35
Guzzle\Http\Exception\TooManyRedirectsException
Definition: lib/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/TooManyRedirectsException.php:5
Guzzle\Http\Message\EntityEnclosingRequestInterface\getBody
getBody()
Guzzle\Http\Url\factory
static factory($url)
Definition: Url.php:34