From 020f556841a60071add53144365358f4f1fd5d7b Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Wed, 12 Mar 2025 11:13:39 +0100 Subject: [PATCH] Support custom attribute with a value in MockCookie.parse() Prior to this commit, MockCookie.parse() failed with an IllegalArgumentException when attempting to parse a custom attribute with a value, such as "Version=1". This is a regression that was inadvertently introduced in 7fc493719962d2ea910866ade9b273b7bac87756 when adding support for the "Partitioned" attribute which does not support a value. This commit addresses this regression by parsing both the name and the value from an optional, custom attribute. See gh-31454 Closes gh-34575 --- .../springframework/mock/web/MockCookie.java | 9 +++++---- .../mock/web/MockCookieTests.java | 20 +++++++++++++++++-- .../web/testfixture/servlet/MockCookie.java | 7 ++++--- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockCookie.java b/spring-test/src/main/java/org/springframework/mock/web/MockCookie.java index c7ec34d088..9347e82173 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockCookie.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockCookie.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -178,7 +178,8 @@ public class MockCookie extends Cookie { cookie.setComment(extractAttributeValue(attribute, setCookieHeader)); } else if (!attribute.isEmpty()) { - cookie.setAttribute(attribute, extractOptionalAttributeValue(attribute, setCookieHeader)); + String[] nameAndValue = extractOptionalAttributeNameAndValue(attribute, setCookieHeader); + cookie.setAttribute(nameAndValue[0], nameAndValue[1]); } } return cookie; @@ -191,9 +192,9 @@ public class MockCookie extends Cookie { return nameAndValue[1]; } - private static String extractOptionalAttributeValue(String attribute, String header) { + private static String[] extractOptionalAttributeNameAndValue(String attribute, String header) { String[] nameAndValue = attribute.split("="); - return nameAndValue.length == 2 ? nameAndValue[1] : ""; + return (nameAndValue.length == 2 ? nameAndValue : new String[] {attribute, ""}); } @Override diff --git a/spring-test/src/test/java/org/springframework/mock/web/MockCookieTests.java b/spring-test/src/test/java/org/springframework/mock/web/MockCookieTests.java index e078b38094..eba5998aca 100644 --- a/spring-test/src/test/java/org/springframework/mock/web/MockCookieTests.java +++ b/spring-test/src/test/java/org/springframework/mock/web/MockCookieTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,8 +68,8 @@ class MockCookieTests { assertCookie(cookie, "SESSION", "123"); } - @SuppressWarnings("removal") @Test + @SuppressWarnings("removal") void parseHeaderWithAttributes() { MockCookie cookie = MockCookie.parse("SESSION=123; Domain=example.com; Max-Age=60; " + "Expires=Tue, 8 Oct 2019 19:50:00 GMT; Path=/; Secure; HttpOnly; Partitioned; SameSite=Lax"); @@ -87,6 +87,19 @@ class MockCookieTests { assertThat(cookie.getComment()).isNull(); } + @Test // gh-34575 + void parseHeaderWithOptionalAttributes() { + MockCookie cookie = MockCookie.parse("SESSION=123; HttpOnly; Version=1; Partitioned; Secure"); + + assertCookie(cookie, "SESSION", "123"); + assertThat(cookie.isHttpOnly()).isTrue(); + assertThat(cookie.getSecure()).isTrue(); + assertThat(cookie.isPartitioned()).isTrue(); + assertThat(cookie.getAttribute("Partitioned")).isEmpty(); + assertThat(cookie.getAttribute("Version")).isEqualTo("1"); + assertThat(cookie.getAttribute("BOGUS")).isNull(); + } + @ParameterizedTest @ValueSource(strings = {"0", "bogus"}) void parseHeaderWithInvalidExpiresAttribute(String expiresValue) { @@ -209,10 +222,13 @@ class MockCookieTests { void setPartitioned() { MockCookie cookie = new MockCookie("SESSION", "123"); assertThat(cookie.isPartitioned()).isFalse(); + assertThat(cookie.getAttribute("Partitioned")).isNull(); cookie.setPartitioned(true); assertThat(cookie.isPartitioned()).isTrue(); + assertThat(cookie.getAttribute("Partitioned")).isEmpty(); cookie.setPartitioned(false); assertThat(cookie.isPartitioned()).isFalse(); + assertThat(cookie.getAttribute("Partitioned")).isNull(); } } diff --git a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockCookie.java b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockCookie.java index 9d01ad1c6d..ee9e997df0 100644 --- a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockCookie.java +++ b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockCookie.java @@ -178,7 +178,8 @@ public class MockCookie extends Cookie { cookie.setComment(extractAttributeValue(attribute, setCookieHeader)); } else if (!attribute.isEmpty()) { - cookie.setAttribute(attribute, extractOptionalAttributeValue(attribute, setCookieHeader)); + String[] nameAndValue = extractOptionalAttributeNameAndValue(attribute, setCookieHeader); + cookie.setAttribute(nameAndValue[0], nameAndValue[1]); } } return cookie; @@ -191,9 +192,9 @@ public class MockCookie extends Cookie { return nameAndValue[1]; } - private static String extractOptionalAttributeValue(String attribute, String header) { + private static String[] extractOptionalAttributeNameAndValue(String attribute, String header) { String[] nameAndValue = attribute.split("="); - return nameAndValue.length == 2 ? nameAndValue[1] : ""; + return (nameAndValue.length == 2 ? nameAndValue : new String[] {attribute, ""}); } @Override