Browse Source

Move redirects handling to the Client.

Robert Pustułka 8 years ago
parent
commit
bcd94078eb
2 changed files with 124 additions and 0 deletions
  1. 38 0
      src/Http/Client.php
  2. 86 0
      tests/TestCase/Http/ClientTest.php

+ 38 - 0
src/Http/Client.php

@@ -19,6 +19,7 @@ use Cake\Core\InstanceConfigTrait;
 use Cake\Http\Client\CookieCollection;
 use Cake\Http\Client\Request;
 use Cake\Utility\Hash;
+use Zend\Diactoros\Uri;
 
 /**
  * The end user interface for doing HTTP requests.
@@ -371,6 +372,43 @@ class Client
      */
     public function send(Request $request, $options = [])
     {
+        $redirects = 0;
+        if (isset($options['redirect'])) {
+            $redirects = (int)$options['redirect'];
+            unset($options['redirect']);
+        }
+
+        do {
+            $response = $this->_sendRequest($request, $options);
+
+            $handleRedirect = $response->isRedirect() && $redirects-- > 0;
+            if ($handleRedirect) {
+                $url = $request->getUri();
+                $request->cookie($this->_cookies->get($url));
+
+                $location = $response->getHeaderLine('Location');
+                $locationUrl = $this->buildUrl($location, [], [
+                    'host' => $url->getHost(),
+                    'port' => $url->getPort(),
+                    'scheme' => $url->getScheme()
+                ]);
+
+                $request = $request->withUri(new Uri($locationUrl));
+            }
+        } while ($handleRedirect);
+
+        return $response;
+    }
+
+    /**
+     * Send a request without redirection.
+     *
+     * @param \Cake\Http\Client\Request $request The request to send.
+     * @param array $options Additional options to use.
+     * @return \Cake\Http\Client\Response
+     */
+    protected function _sendRequest(Request $request, $options)
+    {
         $responses = $this->_adapter->send($request, $options);
         $url = $request->getUri();
         foreach ($responses as $response) {

+ 86 - 0
tests/TestCase/Http/ClientTest.php

@@ -659,4 +659,90 @@ class ClientTest extends TestCase
         ]);
         $this->assertSame($result, $response);
     }
+
+    /**
+     * test redirects
+     *
+     * @return void
+     */
+    public function testRedirects()
+    {
+        $url = 'http://cakephp.org';
+
+        $adapter = $this->getMockBuilder(Client\Adapter\Stream::class)
+            ->setMethods(['send'])
+            ->getMock();
+
+        $redirect = new Response([
+            'HTTP/1.0 301',
+            'Location: http://cakephp.org/redirect1?foo=bar',
+            'Set-Cookie: redirect1=true;path=/',
+        ]);
+        $adapter->expects($this->at(0))
+            ->method('send')
+            ->with(
+                $this->callback(function ($request) use ($url) {
+                    $this->assertInstanceOf(Request::class, $request);
+                    $this->assertEquals($url, $request->getUri());
+
+                    return true;
+                }),
+                $this->callback(function ($options) {
+                    $this->assertArrayNotHasKey('redirect', $options);
+
+                    return true;
+                })
+            )
+            ->willReturn([$redirect]);
+
+        $redirect2 = new Response([
+            'HTTP/1.0 301',
+            'Location: /redirect2#foo',
+            'Set-Cookie: redirect2=true;path=/',
+        ]);
+        $adapter->expects($this->at(1))
+            ->method('send')
+            ->with(
+                $this->callback(function ($request) use ($url) {
+                    $this->assertInstanceOf(Request::class, $request);
+                    $this->assertEquals($url . '/redirect1?foo=bar', $request->getUri());
+
+                    return true;
+                }),
+                $this->callback(function ($options) {
+                    $this->assertArrayNotHasKey('redirect', $options);
+
+                    return true;
+                })
+            )
+            ->willReturn([$redirect2]);
+
+        $response = new Response([
+            'HTTP/1.0 200'
+        ]);
+        $adapter->expects($this->at(2))
+            ->method('send')
+            ->with($this->callback(function ($request) use ($url) {
+                $this->assertInstanceOf(Request::class, $request);
+                $this->assertEquals($url . '/redirect2#foo', $request->getUri());
+
+                return true;
+            }))
+            ->willReturn([$response]);
+
+        $client = new Client([
+            'adapter' => $adapter
+        ]);
+
+        $result = $client->send(new Request($url), [
+            'redirect' => 10
+        ]);
+
+        $this->assertInstanceOf(Response::class, $result);
+        $this->assertTrue($result->isOk());
+        $cookies = $client->cookies()->get($url);
+
+        $this->assertArrayHasKey('redirect1', $cookies);
+        $this->assertArrayHasKey('redirect2', $cookies);
+    }
 }