Repository: rest-assured/rest-assured (java)

Issue:
Cookies with expiry date is not handled correctly in CookieFilter
Cookies which contains an expiry date that is not adhering to the 'expired date format' specified at 

> http://web.archive.org/web/20020803110822/http://wp.netscape.com/newsref/std/cookie_spec.html

 is not getting applied in subsequent requests. 

The cookie expiry date format that is now recommended by **IETF is - Expires=Fri, 07 Feb 2020 12:46:00 GMT** (https://tools.ietf.org/html/rfc6265). HttpClient though has not updated to this format yet and expects a format - **Expires=Fri, 07-Feb-2020 HH:MM:SS GMT**. Note the difference in dashes between IETF and Netscape formats. 

> HttpClient further recommends to use custom configuration of Cookie Policy (https://www.mail-archive.com/dev@hc.apache.org/msg16624.html).

Though we can set the httpClient cookie config, CookieFilter does not use the standards from CookieSpec. If we modify the below code in CookieFilter.java 
```
public CookieFilter(boolean allowMultipleCookiesWithTheSameName) {
        this.allowMultipleCookiesWithTheSameName = allowMultipleCookiesWithTheSameName;
        this.cookieSpec = new DefaultCookieSpec(); //This uses the default dateFormat from netscape
        this.cookieStore = new BasicCookieStore();
    }
```

to 

```
public CookieFilter(boolean allowMultipleCookiesWithTheSameName) {
        this.allowMultipleCookiesWithTheSameName = allowMultipleCookiesWithTheSameName;
        this.cookieSpec = new DefaultCookieSpec(new String[]{"EEE, dd MMM yyyy HH:mm:ss z"},false);
        this.cookieStore = new BasicCookieStore();
    }
```
the problem might get resolved. I can raise a PR if this approach sounds good. 

Pull request description:
Accept cookies that have an expiration date
Fixes #1430

Patch:
diff --git a/changelog.txt b/changelog.txt
index 4e15d043..1934ba8e 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,7 @@
+Changelog next-version
+----------------------
+* Upgraded Guava library to 31.1-jre
+
 Changelog 5.3.0 (2022-11-18)
 ----------------------------
 * Added (much) improved support for CSRF tokens when sent as a header and not in a form
diff --git a/modules/json-schema-validator/pom.xml b/modules/json-schema-validator/pom.xml
index 8d5a97af..d6aae200 100644
--- a/modules/json-schema-validator/pom.xml
+++ b/modules/json-schema-validator/pom.xml
@@ -36,6 +36,17 @@
             <groupId>com.github.java-json-tools</groupId>
             <artifactId>json-schema-validator</artifactId>
             <version>2.2.14</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.google.guava</groupId>
+                    <artifactId>guava</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${guava.version}</version>
         </dependency>
         <dependency>
             <groupId>org.hamcrest</groupId>
diff --git a/modules/spring-mock-mvc/src/main/java/io/restassured/module/mockmvc/internal/MockMvcRequestSenderImpl.java b/modules/spring-mock-mvc/src/main/java/io/restassured/module/mockmvc/internal/MockMvcRequestSenderImpl.java
index 7af33781..a63c2a8b 100644
--- a/modules/spring-mock-mvc/src/main/java/io/restassured/module/mockmvc/internal/MockMvcRequestSenderImpl.java
+++ b/modules/spring-mock-mvc/src/main/java/io/restassured/module/mockmvc/internal/MockMvcRequestSenderImpl.java
@@ -65,6 +65,7 @@ import java.lang.reflect.Constructor;
 import java.net.URI;
 import java.net.URL;
 import java.security.Principal;
+import java.time.ZonedDateTime;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 
@@ -182,6 +183,27 @@ class MockMvcRequestSenderImpl implements MockMvcRequestSender, MockMvcRequestAs
             cookieBuilder.setMaxAge(getMaxAge);
             cookieBuilder.setVersion(invokeMethod(servletCookie, "getVersion"));
             cookieBuilder.setSecured(invokeMethod(servletCookie, "getSecure"));
+            cookieBuilder.setHttpOnly(invokeMethod(servletCookie, "isHttpOnly"));
+
+            // Attempt to copy properties from org.springframework.mock.web.MockCookie
+            try {
+            	String sameSite = invokeMethod(servletCookie, "getSameSite");
+            	if(sameSite != null) {
+                    cookieBuilder.setSameSite(sameSite);
+            	}
+            } catch(IllegalArgumentException e) {
+            	// Do nothing as only found on MockCookie
+            }
+            
+            try {
+                ZonedDateTime expires = invokeMethod(servletCookie, "getExpires");
+                if(expires != null) {
+                    cookieBuilder.setExpiryDate(Date.from(expires.toInstant()));
+                }
+            } catch(IllegalArgumentException e) {
+            	// Do nothing as only found on MockCookie
+            }
+
             cookies.add(cookieBuilder.build());
         }
         return new Cookies(cookies);
diff --git a/pom.xml b/pom.xml
index 827fe7b8..9deea0d2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -73,6 +73,7 @@
         <assertj-core.version>3.16.1</assertj-core.version>
         <scribe.version>2.5.3</scribe.version>
         <sundrio.version>0.93.0</sundrio.version>
+        <guava.version>31.1-jre</guava.version>
     </properties>
     <scm>
         <url>http://github.com/rest-assured/rest-assured/tree/${scm.branch}</url>
diff --git a/rest-assured/src/main/groovy/io/restassured/internal/support/PathSupport.groovy b/rest-assured/src/main/groovy/io/restassured/internal/support/PathSupport.groovy
index bab4802d..4a3904ed 100644
--- a/rest-assured/src/main/groovy/io/restassured/internal/support/PathSupport.groovy
+++ b/rest-assured/src/main/groovy/io/restassured/internal/support/PathSupport.groovy
@@ -58,9 +58,9 @@ class PathSupport {
       return targetUri
     }
 
-    def indexOfScheme = targetUri.indexOf("://");
+    def path = StringUtils.substringBefore(targetUri, "?")
+    def indexOfScheme = path.indexOf("://");
     if (indexOfScheme == -1) {
-      def path = StringUtils.substringBefore(targetUri, "?")
       return StringUtils.startsWith(path, "/") ? path : "/" + path
     }
     def indexOfPath = StringUtils.indexOf(targetUri, "/", indexOfScheme + 3);
diff --git a/rest-assured/src/main/java/io/restassured/filter/cookie/CookieFilter.java b/rest-assured/src/main/java/io/restassured/filter/cookie/CookieFilter.java
index 4ffe0324..a786ee63 100644
--- a/rest-assured/src/main/java/io/restassured/filter/cookie/CookieFilter.java
+++ b/rest-assured/src/main/java/io/restassured/filter/cookie/CookieFilter.java
@@ -23,12 +23,14 @@ import io.restassured.response.Response;
 import io.restassured.specification.FilterableRequestSpecification;
 import io.restassured.specification.FilterableResponseSpecification;
 import org.apache.http.Header;
+import org.apache.http.client.CookieStore;
 import org.apache.http.cookie.Cookie;
 import org.apache.http.cookie.CookieOrigin;
 import org.apache.http.cookie.CookieSpec;
 import org.apache.http.cookie.MalformedCookieException;
 import org.apache.http.impl.client.BasicCookieStore;
 import org.apache.http.impl.cookie.DefaultCookieSpec;
+import org.apache.http.impl.cookie.RFC6265StrictSpec;
 import org.apache.http.message.BasicHeader;
 
 import java.net.MalformedURLException;
@@ -83,7 +85,7 @@ public class CookieFilter implements Filter {
      */
     public CookieFilter(boolean allowMultipleCookiesWithTheSameName) {
         this.allowMultipleCookiesWithTheSameName = allowMultipleCookiesWithTheSameName;
-        this.cookieSpec = new DefaultCookieSpec();
+        this.cookieSpec = new RFC6265StrictSpec();
         this.cookieStore = new BasicCookieStore();
     }
 
@@ -131,4 +133,8 @@ public class CookieFilter implements Filter {
             throw new IllegalArgumentException(e);
         }
     }
+
+    public CookieStore getCookieStore() {
+        return cookieStore;
+    }
 }
diff --git a/rest-assured/src/main/java/io/restassured/specification/AuthenticationSpecification.java b/rest-assured/src/main/java/io/restassured/specification/AuthenticationSpecification.java
index f7446cd3..48b4c926 100644
--- a/rest-assured/src/main/java/io/restassured/specification/AuthenticationSpecification.java
+++ b/rest-assured/src/main/java/io/restassured/specification/AuthenticationSpecification.java
@@ -158,7 +158,7 @@ public interface AuthenticationSpecification {
     PreemptiveAuthSpec preemptive();
 
     /**
-     * Explicitly state that you don't which to use any authentication in this request. This is useful only in cases where you've
+     * Explicitly state that you don't wish to use any authentication in this request. This is useful only in cases where you've
      * specified a default authentication scheme and you wish to override it for a single request.
      *
      * @return The Request specification
diff --git a/rest-assured/src/main/java/io/restassured/specification/RequestSenderOptions.java b/rest-assured/src/main/java/io/restassured/specification/RequestSenderOptions.java
index 66bc0626..e1ccf0c5 100644
--- a/rest-assured/src/main/java/io/restassured/specification/RequestSenderOptions.java
+++ b/rest-assured/src/main/java/io/restassured/specification/RequestSenderOptions.java
@@ -374,7 +374,7 @@ public interface RequestSenderOptions<R extends ResponseOptions<R>> {
      *
      * @param method The HTTP method to use
      * @param uri    The uri to send the request to.
-     * @return The response of the GET request.
+     * @return The response of the request.
      */
     R request(Method method, URI uri);
 
@@ -383,7 +383,7 @@ public interface RequestSenderOptions<R extends ResponseOptions<R>> {
      *
      * @param method The HTTP method to use
      * @param url    The url to send the request to.
-     * @return The response of the GET request.
+     * @return The response of the request.
      */
     R request(Method method, URL url);
 
@@ -392,7 +392,7 @@ public interface RequestSenderOptions<R extends ResponseOptions<R>> {
      *
      * @param method The HTTP method to use
      * @param uri    The uri to send the request to.
-     * @return The response of the GET request.
+     * @return The response of the request.
      */
     R request(String method, URI uri);
 
@@ -401,7 +401,7 @@ public interface RequestSenderOptions<R extends ResponseOptions<R>> {
      *
      * @param method The HTTP method to use
      * @param url    The url to send the request to.
-     * @return The response of the GET request.
+     * @return The response of the request.
      */
     R request(String method, URL url);
 }
\ No newline at end of file


Test patch:
diff --git a/examples/rest-assured-itest-java/pom.xml b/examples/rest-assured-itest-java/pom.xml
index abbc6356..ad2c72a8 100644
--- a/examples/rest-assured-itest-java/pom.xml
+++ b/examples/rest-assured-itest-java/pom.xml
@@ -136,6 +136,18 @@
             <artifactId>littleproxy</artifactId>
             <version>1.0.0-beta7</version>
             <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.google.guava</groupId>
+                    <artifactId>guava</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>${guava.version}</version>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>com.squareup.okhttp</groupId>
diff --git a/modules/spring-mock-mvc/src/test/java/io/restassured/module/mockmvc/CookieTest.java b/modules/spring-mock-mvc/src/test/java/io/restassured/module/mockmvc/CookieTest.java
index e93adad0..6d52098f 100644
--- a/modules/spring-mock-mvc/src/test/java/io/restassured/module/mockmvc/CookieTest.java
+++ b/modules/spring-mock-mvc/src/test/java/io/restassured/module/mockmvc/CookieTest.java
@@ -24,9 +24,13 @@ import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 
+import static io.restassured.matcher.RestAssuredMatchers.detailedCookie;
 import static org.hamcrest.Matchers.equalTo;
 
 public class CookieTest {
@@ -118,6 +122,31 @@ public class CookieTest {
                 cookie("name", "John Doe").
                 cookie("project", "rest assured");
     }
+    
+    @Test public void
+    can_receive_detailed_cookies() {
+        RestAssuredMockMvc.given().
+                queryParam("cookieName1", "name").
+                queryParam("cookieValue1", "John Doe").
+                queryParam("cookieName2", "project").
+                queryParam("cookieValue2", "rest assured").
+        when().
+                get("/setDetailedCookies").
+        then().
+                statusCode(200).
+                cookie("name", detailedCookie().
+                    value("John Doe").
+                    httpOnly(true).
+                    secured(true).
+                    sameSite("None").
+                    expiryDate(Date.from(ZonedDateTime.of(2023, 1, 1, 12, 30, 0, 0, ZoneId.of("Z")).toInstant()))).
+                cookie("project", detailedCookie().
+                    value("rest assured").
+                    httpOnly(false).
+                    secured(false).
+                    sameSite("Lax").
+                    expiryDate(Date.from(ZonedDateTime.of(2023, 1, 1, 12, 30, 0, 0, ZoneId.of("Z")).toInstant())));
+    }
 }
 
 // @formatter:on
\ No newline at end of file
diff --git a/modules/spring-mock-mvc/src/test/java/io/restassured/module/mockmvc/http/CookieController.java b/modules/spring-mock-mvc/src/test/java/io/restassured/module/mockmvc/http/CookieController.java
index dfc56760..aab5a3a2 100644
--- a/modules/spring-mock-mvc/src/test/java/io/restassured/module/mockmvc/http/CookieController.java
+++ b/modules/spring-mock-mvc/src/test/java/io/restassured/module/mockmvc/http/CookieController.java
@@ -21,6 +21,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletResponse;
 
@@ -28,6 +31,36 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 import static org.springframework.web.bind.annotation.RequestMethod.GET;
 
 public class CookieController {
+	
+    /**
+      * Mimics org.springframework.mock.web.MockCookie
+      */
+    public static class MockCookie extends Cookie {
+        private static final long serialVersionUID = 1L;
+		
+        private String sameSite;
+        private ZonedDateTime expires;
+
+        public MockCookie(String name, String value) {
+            super(name, value);
+        }
+
+        public String getSameSite() {
+            return sameSite;
+        }
+
+        public void setSameSite(String sameSite) {
+            this.sameSite = sameSite;
+        }
+
+        public ZonedDateTime getExpires() {
+            return expires;
+        }
+
+        public void setExpires(ZonedDateTime expires) {
+            this.expires = expires;
+        }
+    }
 
     @RequestMapping(value = "/cookie", method = GET, produces = APPLICATION_JSON_VALUE)
     public @ResponseBody String cookie(@CookieValue("cookieName1") String cookieValue1, @CookieValue(value = "cookieName2", required = false) String cookieValue2) {
@@ -42,4 +75,24 @@ public class CookieController {
         response.addCookie(new Cookie(cookieName2, cookieValue2));
         return "{}";
     }
+    
+    @RequestMapping(value = "/setDetailedCookies", method = GET, produces = APPLICATION_JSON_VALUE)
+    public @ResponseBody String setDetailedCookies(HttpServletResponse response,
+                     @RequestParam("cookieName1") String cookieName1, @RequestParam("cookieValue1") String cookieValue1,
+                     @RequestParam("cookieName2") String cookieName2, @RequestParam("cookieValue2") String cookieValue2) {
+        MockCookie cookie1 = new MockCookie(cookieName1, cookieValue1);
+        cookie1.setHttpOnly(true);
+        cookie1.setSameSite("None");
+        cookie1.setSecure(true);
+        cookie1.setExpires(ZonedDateTime.of(2023, 1, 1, 12, 30, 0, 0, ZoneId.of("Z")));
+        response.addCookie(cookie1);
+
+        MockCookie cookie2 = new MockCookie(cookieName2, cookieValue2);
+        cookie2.setHttpOnly(false);
+        cookie2.setSameSite("Lax");
+        cookie2.setSecure(false);
+        cookie2.setExpires(ZonedDateTime.of(2023, 1, 1, 12, 30, 0, 0, ZoneId.of("Z")));
+        response.addCookie(cookie2);
+        return "{}";
+    }
 }
diff --git a/rest-assured/src/test/java/io/restassured/filter/cookie/CookieFilterTest.java b/rest-assured/src/test/java/io/restassured/filter/cookie/CookieFilterTest.java
index 37bf8bd5..3b95ed5a 100644
--- a/rest-assured/src/test/java/io/restassured/filter/cookie/CookieFilterTest.java
+++ b/rest-assured/src/test/java/io/restassured/filter/cookie/CookieFilterTest.java
@@ -137,9 +137,9 @@ public class CookieFilterTest {
         public Response next(FilterableRequestSpecification request, FilterableResponseSpecification response) {
             RestAssuredResponseImpl restAssuredResponse = new RestAssuredResponseImpl();
             Header setCookieHeader1 =
-                    new Header("Set-Cookie", "cookieName1=cookieValue1; Domain=somedomain.com; Path=/somepath; Secure; HttpOnly");
+                    new Header("Set-Cookie", "cookieName1=cookieValue1; Domain=somedomain.com; Path=/somepath; Secure; HttpOnly; Expires=Wed, 21 Oct 3015 07:28:00 GMT");
             Header setCookieHeader2 =
-                    new Header("Set-Cookie", "cookieName2=cookieValue2; Domain=somedomain.com; Path=/somepath; Secure; HttpOnly");
+                    new Header("Set-Cookie", "cookieName2=cookieValue2; Domain=somedomain.com; Path=/somepath; Secure; HttpOnly; Expires=Wed, 21 Oct 3015 07:28:00 GMT");
             restAssuredResponse.setResponseHeaders(Headers.headers(setCookieHeader1, setCookieHeader2));
             return restAssuredResponse;
         }
diff --git a/rest-assured/src/test/java/io/restassured/internal/support/PathSupportTest.java b/rest-assured/src/test/java/io/restassured/internal/support/PathSupportTest.java
index b060da32..03b53b2f 100644
--- a/rest-assured/src/test/java/io/restassured/internal/support/PathSupportTest.java
+++ b/rest-assured/src/test/java/io/restassured/internal/support/PathSupportTest.java
@@ -119,4 +119,26 @@ public class PathSupportTest {
         // Then
         assertThat(path, is("/"));
     }
-}
\ No newline at end of file
+    @Test public void
+    returns_path_when_param_has_scheme() {
+        // Given
+        String targetUri = "/path?uri=http://localhost";
+
+        // When
+        String path = PathSupport.getPath(targetUri);
+
+        // Then
+        assertThat(path, is("/path"));
+    }
+    @Test public void
+    returns_path_when_path_is_fully_qualified_uri_and_param_has_scheme() {
+        // Given
+        String targetUri = "http://localhost/path?uri=http://localhost";
+
+        // When
+        String path = PathSupport.getPath(targetUri);
+
+        // Then
+        assertThat(path, is("/path"));
+    }
+}