Browse Source

Add CookieCollection::addToRequest()

Copy and adapt code from the existing CookieCollection to enable it to
be used in the client context.
Mark Story 9 years ago
parent
commit
8dbf562040

+ 47 - 0
src/Http/Cookie/CookieCollection.php

@@ -175,6 +175,53 @@ class CookieCollection implements IteratorAggregate, Countable
     }
     }
 
 
     /**
     /**
+     * Add cookies that match the path/domain/expiration to the request.
+     *
+     * This allows CookieCollections to be used a 'cookie jar' in an HTTP client
+     * situation. Cookies that match the request's domain + path that are not expired
+     * when this method is called will be applied to the request.
+     *
+     * @param \Psr\Http\Message\RequestInterface $request The request to update.
+     * @return \Psr\Http\Message\RequestInterface An updated request.
+     */
+    public function addToRequest(RequestInterface $request)
+    {
+        $uri = $request->getUri();
+        $path = $uri->getPath();
+        $host = $uri->getHost();
+        $scheme = $uri->getScheme();
+
+        $out = [];
+        foreach ($this->cookies as $cookie) {
+            if ($scheme === 'http' && $cookie->isSecure()) {
+                continue;
+            }
+            if (strpos($path, $cookie->getPath()) !== 0) {
+                continue;
+            }
+            $domain = $cookie->getDomain();
+            $leadingDot = substr($domain, 0, 1) === '.';
+            if ($leadingDot) {
+                $domain = ltrim($domain, '.');
+            }
+
+            if ($cookie->getExpiry() && time() > $cookie->getExpiry()) {
+                continue;
+            }
+
+            $pattern = '/' . preg_quote($domain, '/') . '$/';
+            if (!preg_match($pattern, $host)) {
+                continue;
+            }
+
+            $out[$cookie->getName()] = $cookie->getValue();
+        }
+        $cookies = array_merge($request->getCookieParams(), $out);
+
+        return $request->withCookieParams($cookies);
+    }
+
+    /**
      * Create a new collection that includes cookies from the response.
      * Create a new collection that includes cookies from the response.
      *
      *
      * @param \Psr\Http\Message\ResponseInterface $response Response to extract cookies from.
      * @param \Psr\Http\Message\ResponseInterface $response Response to extract cookies from.

+ 88 - 2
tests/TestCase/Http/Cookie/CookieCollectionTest.php

@@ -17,6 +17,7 @@ use Cake\Http\Cookie\CookieCollection;
 use Cake\Http\ServerRequest;
 use Cake\Http\ServerRequest;
 use Cake\Http\Response;
 use Cake\Http\Response;
 use Cake\TestSuite\TestCase;
 use Cake\TestSuite\TestCase;
+use DateTime;
 
 
 /**
 /**
  * Cookie collection test.
  * Cookie collection test.
@@ -203,7 +204,7 @@ class CookieCollectionTest extends TestCase
         $response = (new Response())
         $response = (new Response())
             ->withAddedHeader('Set-Cookie', 'test=value')
             ->withAddedHeader('Set-Cookie', 'test=value')
             ->withAddedHeader('Set-Cookie', 'expiring=soon; Expires=Wed, 09-Jun-2021 10:18:14 GMT; Path=/; HttpOnly; Secure;')
             ->withAddedHeader('Set-Cookie', 'expiring=soon; Expires=Wed, 09-Jun-2021 10:18:14 GMT; Path=/; HttpOnly; Secure;')
-            ->withAddedHeader('Set-Cookie', 'session=123abc');
+            ->withAddedHeader('Set-Cookie', 'session=123abc; Domain=www.example.com');
         $new = $collection->addFromResponse($response, $request);
         $new = $collection->addFromResponse($response, $request);
         $this->assertNotSame($new, $collection, 'Should clone collection');
         $this->assertNotSame($new, $collection, 'Should clone collection');
 
 
@@ -218,12 +219,14 @@ class CookieCollectionTest extends TestCase
         $this->assertSame('/', $new->get('expiring')->getPath(), 'path attribute should be used.');
         $this->assertSame('/', $new->get('expiring')->getPath(), 'path attribute should be used.');
 
 
         $this->assertSame(0, $new->get('test')->getExpiry(), 'No expiry');
         $this->assertSame(0, $new->get('test')->getExpiry(), 'No expiry');
-        $this->assertSame(0, $new->get('session')->getExpiry(), 'No expiry');
         $this->assertSame(
         $this->assertSame(
             '2021-06-09 10:18:14',
             '2021-06-09 10:18:14',
             date('Y-m-d H:i:s', $new->get('expiring')->getExpiry()),
             date('Y-m-d H:i:s', $new->get('expiring')->getExpiry()),
             'Has expiry'
             'Has expiry'
         );
         );
+        $session = $new->get('session');
+        $this->assertSame(0, $session->getExpiry(), 'No expiry');
+        $this->assertSame('www.example.com', $session->getDomain(), 'Has domain');
     }
     }
 
 
     /**
     /**
@@ -264,4 +267,87 @@ class CookieCollectionTest extends TestCase
         $new = $collection->addFromResponse($response, $request);
         $new = $collection->addFromResponse($response, $request);
         $this->assertFalse($new->has('expired'),'Should drop expired cookies');
         $this->assertFalse($new->has('expired'),'Should drop expired cookies');
     }
     }
+
+    /**
+     * Test adding cookies from the collection to request.
+     *
+     * @return void
+     */
+    public function testAddToRequest()
+    {
+        $collection = new CookieCollection();
+        $collection = $collection
+            ->add(new Cookie('api', 'A', null, '/api', 'example.com'))
+            ->add(new Cookie('blog', 'b', null, '/blog', 'blog.example.com'))
+            ->add(new Cookie('expired', 'ex', new DateTime('-2 seconds'), '/', 'example.com'));
+        $request = new ServerRequest([
+            'environment' => [
+                'HTTP_HOST' => 'example.com',
+                'REQUEST_URI' => '/api'
+            ]
+        ]);
+        $request = $collection->addToRequest($request);
+        $this->assertCount(1, $request->getCookieParams());
+        $this->assertSame(['api' => 'A'], $request->getCookieParams());
+
+        $request = new ServerRequest([
+            'environment' => [
+                'HTTP_HOST' => 'example.com',
+                'REQUEST_URI' => '/'
+            ]
+        ]);
+        $request = $collection->addToRequest($request);
+        $this->assertCount(0, $request->getCookieParams());
+
+        $request = new ServerRequest([
+            'environment' => [
+                'HTTP_HOST' => 'example.com',
+                'REQUEST_URI' => '/blog'
+            ]
+        ]);
+        $request = $collection->addToRequest($request);
+        $this->assertCount(0, $request->getCookieParams(), 'domain matching should apply');
+
+        $request = new ServerRequest([
+            'environment' => [
+                'HTTP_HOST' => 'foo.blog.example.com',
+                'REQUEST_URI' => '/blog'
+            ]
+        ]);
+        $request = $collection->addToRequest($request);
+        $this->assertCount(1, $request->getCookieParams(), 'domain matching should apply');
+        $this->assertSame(['blog' => 'b'], $request->getCookieParams());
+    }
+
+    /**
+     * Test adding cookies checks the secure crumb
+     *
+     * @return void
+     */
+    public function testAddToRequestSecureCrumb()
+    {
+        $collection = new CookieCollection();
+        $collection = $collection
+            ->add(new Cookie('secret', 'A', null, '/', 'example.com', true))
+            ->add(new Cookie('public', 'b', null, '/', 'example.com', false));
+        $request = new ServerRequest([
+            'environment' => [
+                'HTTPS' => 'on',
+                'HTTP_HOST' => 'example.com',
+                'REQUEST_URI' => '/api'
+            ]
+        ]);
+        $request = $collection->addToRequest($request);
+        $this->assertSame(['secret' => 'A', 'public' => 'b'], $request->getCookieParams());
+
+        // no HTTPS set.
+        $request = new ServerRequest([
+            'environment' => [
+                'HTTP_HOST' => 'example.com',
+                'REQUEST_URI' => '/api'
+            ]
+        ]);
+        $request = $collection->addToRequest($request);
+        $this->assertSame(['public' => 'b'], $request->getCookieParams());
+    }
 }
 }