diff --git a/spring-web-reactive/src/main/java/org/springframework/http/HttpCookie.java b/spring-web-reactive/src/main/java/org/springframework/http/HttpCookie.java
index 81a762f352..3f1675a4a9 100644
--- a/spring-web-reactive/src/main/java/org/springframework/http/HttpCookie.java
+++ b/spring-web-reactive/src/main/java/org/springframework/http/HttpCookie.java
@@ -15,54 +15,28 @@
*/
package org.springframework.http;
-import java.time.Duration;
-
import org.springframework.util.Assert;
-import org.springframework.util.ObjectUtils;
/**
- * Representation for an HTTP Cookie.
+ * Represents an HTTP Cookie with a name and value.
*
- *
Use the {@link #clientCookie} factory method to create a client-to-server,
- * name-value pair cookie and the {@link #serverCookie} factory method to build
- * a server-to-client cookie with additional attributes.
+ *
The {@link ServerHttpCookie} sub-class exposes the extra attributes that
+ * a server can include in a Set-Cookie response header.
*
* @author Rossen Stoyanchev
* @see RFC 6265
*/
-public final class HttpCookie {
+public class HttpCookie {
private final String name;
private final String value;
- private final Duration maxAge;
-
- private final String domain;
-
- private final String path;
-
- private final boolean secure;
-
- private final boolean httpOnly;
-
-
- private HttpCookie(String name, String value) {
- this(name, value, Duration.ofSeconds(-1), null, null, false, false);
- }
-
- private HttpCookie(String name, String value, Duration maxAge, String domain, String path,
- boolean secure, boolean httpOnly) {
+ public HttpCookie(String name, String value) {
Assert.hasLength(name, "'name' is required and must not be empty.");
- Assert.notNull(maxAge);
this.name = name;
this.value = (value != null ? value : "");
- this.maxAge = maxAge;
- this.domain = domain;
- this.path = path;
- this.secure = secure;
- this.httpOnly = httpOnly;
}
/**
@@ -73,59 +47,16 @@ public final class HttpCookie {
}
/**
- * Return the cookie value.
+ * Return the cookie value or an empty string, never {@code null}.
*/
public String getValue() {
return this.value;
}
- /**
- * Return the cookie "Max-Age" attribute in seconds.
- *
- *
A positive value indicates when the cookie expires relative to the
- * current time. A value of 0 means the cookie should expire immediately.
- * A negative value means no "Max-Age" attribute in which case the cookie
- * is removed when the browser is closed.
- */
- public Duration getMaxAge() {
- return this.maxAge;
- }
-
- /**
- * Return the cookie "Domain" attribute.
- */
- public String getDomain() {
- return this.domain;
- }
-
- /**
- * Return the cookie "Path" attribute.
- */
- public String getPath() {
- return this.path;
- }
-
- /**
- * Return {@code true} if the cookie has the "Secure" attribute.
- */
- public boolean isSecure() {
- return this.secure;
- }
-
- /**
- * Return {@code true} if the cookie has the "HttpOnly" attribute.
- * @see http://www.owasp.org/index.php/HTTPOnly
- */
- public boolean isHttpOnly() {
- return this.httpOnly;
- }
@Override
public int hashCode() {
- int result = this.name.hashCode();
- result = 31 * result + ObjectUtils.nullSafeHashCode(this.domain);
- result = 31 * result + ObjectUtils.nullSafeHashCode(this.path);
- return result;
+ return this.name.hashCode();
}
@Override
@@ -137,122 +68,7 @@ public final class HttpCookie {
return false;
}
HttpCookie otherCookie = (HttpCookie) other;
- return (this.name.equalsIgnoreCase(otherCookie.getName()) &&
- ObjectUtils.nullSafeEquals(this.path, otherCookie.getPath()) &&
- ObjectUtils.nullSafeEquals(this.domain, otherCookie.getDomain()));
- }
-
- /**
- * Factory method to create a cookie sent from a client to a server.
- * Client cookies are name-value pairs only without attributes.
- * @param name the cookie name
- * @param value the cookie value
- * @return the created cookie instance
- */
- public static HttpCookie clientCookie(String name, String value) {
- return new HttpCookie(name, value);
- }
-
- /**
- * Factory method to obtain a builder for a server-defined cookie that starts
- * with a name-value pair and may also include attributes.
- * @param name the cookie name
- * @param value the cookie value
- * @return the created cookie instance
- */
- public static HttpCookieBuilder serverCookie(final String name, final String value) {
-
- return new HttpCookieBuilder() {
-
- private Duration maxAge = Duration.ofSeconds(-1);
-
- private String domain;
-
- private String path;
-
- private boolean secure;
-
- private boolean httpOnly;
-
-
- @Override
- public HttpCookieBuilder maxAge(Duration maxAge) {
- this.maxAge = maxAge;
- return this;
- }
-
- @Override
- public HttpCookieBuilder domain(String domain) {
- this.domain = domain;
- return this;
- }
-
- @Override
- public HttpCookieBuilder path(String path) {
- this.path = path;
- return this;
- }
-
- @Override
- public HttpCookieBuilder secure() {
- this.secure = true;
- return this;
- }
-
- @Override
- public HttpCookieBuilder httpOnly() {
- this.httpOnly = true;
- return this;
- }
-
- @Override
- public HttpCookie build() {
- return new HttpCookie(name, value, this.maxAge, this.domain, this.path,
- this.secure, this.httpOnly);
- }
- };
- }
-
- /**
- * A builder for a server-defined HttpCookie with attributes.
- */
- public interface HttpCookieBuilder {
-
- /**
- * Set the cookie "Max-Age" attribute.
- *
- *
A positive value indicates when the cookie should expire relative
- * to the current time. A value of 0 means the cookie should expire
- * immediately. A negative value results in no "Max-Age" attribute in
- * which case the cookie is removed when the browser is closed.
- */
- HttpCookieBuilder maxAge(Duration maxAge);
-
- /**
- * Set the cookie "Path" attribute.
- */
- HttpCookieBuilder path(String path);
-
- /**
- * Set the cookie "Domain" attribute.
- */
- HttpCookieBuilder domain(String domain);
-
- /**
- * Add the "Secure" attribute to the cookie.
- */
- HttpCookieBuilder secure();
-
- /**
- * Add the "HttpOnly" attribute to the cookie.
- * @see http://www.owasp.org/index.php/HTTPOnly
- */
- HttpCookieBuilder httpOnly();
-
- /**
- * Create the HttpCookie.
- */
- HttpCookie build();
+ return (this.name.equalsIgnoreCase(otherCookie.getName()));
}
}
diff --git a/spring-web-reactive/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web-reactive/src/main/java/org/springframework/http/HttpHeaders.java
deleted file mode 100644
index 419e94b947..0000000000
--- a/spring-web-reactive/src/main/java/org/springframework/http/HttpHeaders.java
+++ /dev/null
@@ -1,1158 +0,0 @@
-/*
- * Copyright 2002-2015 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.http;
-
-import java.io.Serializable;
-import java.net.URI;
-import java.nio.charset.Charset;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.EnumSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.TimeZone;
-
-import org.springframework.util.Assert;
-import org.springframework.util.LinkedCaseInsensitiveMap;
-import org.springframework.util.MultiValueMap;
-import org.springframework.util.StringUtils;
-
-// A copy of HttpHeaders with additional support for:
-// - HTTP cookies
-
-// To be merged into HttpHeaders from spring-web
-
-public class HttpHeaders implements MultiValueMap, Serializable {
-
- private static final long serialVersionUID = -8578554704772377436L;
-
- /**
- * The HTTP {@code Accept} header field name.
- * @see Section 5.3.2 of RFC 7231
- */
- public static final String ACCEPT = "Accept";
- /**
- * The HTTP {@code Accept-Charset} header field name.
- * @see Section 5.3.3 of RFC 7231
- */
- public static final String ACCEPT_CHARSET = "Accept-Charset";
- /**
- * The HTTP {@code Accept-Encoding} header field name.
- * @see Section 5.3.4 of RFC 7231
- */
- public static final String ACCEPT_ENCODING = "Accept-Encoding";
- /**
- * The HTTP {@code Accept-Language} header field name.
- * @see Section 5.3.5 of RFC 7231
- */
- public static final String ACCEPT_LANGUAGE = "Accept-Language";
- /**
- * The HTTP {@code Accept-Ranges} header field name.
- * @see Section 5.3.5 of RFC 7233
- */
- public static final String ACCEPT_RANGES = "Accept-Ranges";
- /**
- * The CORS {@code Access-Control-Allow-Credentials} response header field name.
- * @see CORS W3C recommandation
- */
- public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
- /**
- * The CORS {@code Access-Control-Allow-Headers} response header field name.
- * @see CORS W3C recommandation
- */
- public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
- /**
- * The CORS {@code Access-Control-Allow-Methods} response header field name.
- * @see CORS W3C recommandation
- */
- public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
- /**
- * The CORS {@code Access-Control-Allow-Origin} response header field name.
- * @see CORS W3C recommandation
- */
- public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
- /**
- * The CORS {@code Access-Control-Expose-Headers} response header field name.
- * @see CORS W3C recommandation
- */
- public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
- /**
- * The CORS {@code Access-Control-Max-Age} response header field name.
- * @see CORS W3C recommandation
- */
- public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
- /**
- * The CORS {@code Access-Control-Request-Headers} request header field name.
- * @see CORS W3C recommandation
- */
- public static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
- /**
- * The CORS {@code Access-Control-Request-Method} request header field name.
- * @see CORS W3C recommandation
- */
- public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";
- /**
- * The HTTP {@code Age} header field name.
- * @see Section 5.1 of RFC 7234
- */
- public static final String AGE = "Age";
- /**
- * The HTTP {@code Allow} header field name.
- * @see Section 7.4.1 of RFC 7231
- */
- public static final String ALLOW = "Allow";
- /**
- * The HTTP {@code Authorization} header field name.
- * @see Section 4.2 of RFC 7235
- */
- public static final String AUTHORIZATION = "Authorization";
- /**
- * The HTTP {@code Cache-Control} header field name.
- * @see Section 5.2 of RFC 7234
- */
- public static final String CACHE_CONTROL = "Cache-Control";
- /**
- * The HTTP {@code Connection} header field name.
- * @see Section 6.1 of RFC 7230
- */
- public static final String CONNECTION = "Connection";
- /**
- * The HTTP {@code Content-Encoding} header field name.
- * @see Section 3.1.2.2 of RFC 7231
- */
- public static final String CONTENT_ENCODING = "Content-Encoding";
- /**
- * The HTTP {@code Content-Disposition} header field name
- * @see RFC 6266
- */
- public static final String CONTENT_DISPOSITION = "Content-Disposition";
- /**
- * The HTTP {@code Content-Language} header field name.
- * @see Section 3.1.3.2 of RFC 7231
- */
- public static final String CONTENT_LANGUAGE = "Content-Language";
- /**
- * The HTTP {@code Content-Length} header field name.
- * @see Section 3.3.2 of RFC 7230
- */
- public static final String CONTENT_LENGTH = "Content-Length";
- /**
- * The HTTP {@code Content-Location} header field name.
- * @see Section 3.1.4.2 of RFC 7231
- */
- public static final String CONTENT_LOCATION = "Content-Location";
- /**
- * The HTTP {@code Content-Range} header field name.
- * @see Section 4.2 of RFC 7233
- */
- public static final String CONTENT_RANGE = "Content-Range";
- /**
- * The HTTP {@code Content-Type} header field name.
- * @see Section 3.1.1.5 of RFC 7231
- */
- public static final String CONTENT_TYPE = "Content-Type";
- /**
- * The HTTP {@code Cookie} header field name.
- * @see Section 4.3.4 of RFC 2109
- */
- public static final String COOKIE = "Cookie";
- /**
- * The HTTP {@code Date} header field name.
- * @see Section 7.1.1.2 of RFC 7231
- */
- public static final String DATE = "Date";
- /**
- * The HTTP {@code ETag} header field name.
- * @see Section 2.3 of RFC 7232
- */
- public static final String ETAG = "ETag";
- /**
- * The HTTP {@code Expect} header field name.
- * @see Section 5.1.1 of RFC 7231
- */
- public static final String EXPECT = "Expect";
- /**
- * The HTTP {@code Expires} header field name.
- * @see Section 5.3 of RFC 7234
- */
- public static final String EXPIRES = "Expires";
- /**
- * The HTTP {@code From} header field name.
- * @see Section 5.5.1 of RFC 7231
- */
- public static final String FROM = "From";
- /**
- * The HTTP {@code Host} header field name.
- * @see Section 5.4 of RFC 7230
- */
- public static final String HOST = "Host";
- /**
- * The HTTP {@code If-Match} header field name.
- * @see Section 3.1 of RFC 7232
- */
- public static final String IF_MATCH = "If-Match";
- /**
- * The HTTP {@code If-Modified-Since} header field name.
- * @see Section 3.3 of RFC 7232
- */
- public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
- /**
- * The HTTP {@code If-None-Match} header field name.
- * @see Section 3.2 of RFC 7232
- */
- public static final String IF_NONE_MATCH = "If-None-Match";
- /**
- * The HTTP {@code If-Range} header field name.
- * @see Section 3.2 of RFC 7233
- */
- public static final String IF_RANGE = "If-Range";
- /**
- * The HTTP {@code If-Unmodified-Since} header field name.
- * @see Section 3.4 of RFC 7232
- */
- public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
- /**
- * The HTTP {@code Last-Modified} header field name.
- * @see Section 2.2 of RFC 7232
- */
- public static final String LAST_MODIFIED = "Last-Modified";
- /**
- * The HTTP {@code Link} header field name.
- * @see RFC 5988
- */
- public static final String LINK = "Link";
- /**
- * The HTTP {@code Location} header field name.
- * @see Section 7.1.2 of RFC 7231
- */
- public static final String LOCATION = "Location";
- /**
- * The HTTP {@code Max-Forwards} header field name.
- * @see Section 5.1.2 of RFC 7231
- */
- public static final String MAX_FORWARDS = "Max-Forwards";
- /**
- * The HTTP {@code Origin} header field name.
- * @see RFC 6454
- */
- public static final String ORIGIN = "Origin";
- /**
- * The HTTP {@code Pragma} header field name.
- * @see Section 5.4 of RFC 7234
- */
- public static final String PRAGMA = "Pragma";
- /**
- * The HTTP {@code Proxy-Authenticate} header field name.
- * @see Section 4.3 of RFC 7235
- */
- public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
- /**
- * The HTTP {@code Proxy-Authorization} header field name.
- * @see Section 4.4 of RFC 7235
- */
- public static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
- /**
- * The HTTP {@code Range} header field name.
- * @see Section 3.1 of RFC 7233
- */
- public static final String RANGE = "Range";
- /**
- * The HTTP {@code Referer} header field name.
- * @see Section 5.5.2 of RFC 7231
- */
- public static final String REFERER = "Referer";
- /**
- * The HTTP {@code Retry-After} header field name.
- * @see Section 7.1.3 of RFC 7231
- */
- public static final String RETRY_AFTER = "Retry-After";
- /**
- * The HTTP {@code Server} header field name.
- * @see Section 7.4.2 of RFC 7231
- */
- public static final String SERVER = "Server";
- /**
- * The HTTP {@code Set-Cookie} header field name.
- * @see Section 4.2.2 of RFC 2109
- */
- public static final String SET_COOKIE = "Set-Cookie";
- /**
- * The HTTP {@code Set-Cookie2} header field name.
- * @see RFC 2965
- */
- public static final String SET_COOKIE2 = "Set-Cookie2";
- /**
- * The HTTP {@code TE} header field name.
- * @see Section 4.3 of RFC 7230
- */
- public static final String TE = "TE";
- /**
- * The HTTP {@code Trailer} header field name.
- * @see Section 4.4 of RFC 7230
- */
- public static final String TRAILER = "Trailer";
- /**
- * The HTTP {@code Transfer-Encoding} header field name.
- * @see Section 3.3.1 of RFC 7230
- */
- public static final String TRANSFER_ENCODING = "Transfer-Encoding";
- /**
- * The HTTP {@code Upgrade} header field name.
- * @see Section 6.7 of RFC 7230
- */
- public static final String UPGRADE = "Upgrade";
- /**
- * The HTTP {@code User-Agent} header field name.
- * @see Section 5.5.3 of RFC 7231
- */
- public static final String USER_AGENT = "User-Agent";
- /**
- * The HTTP {@code Vary} header field name.
- * @see Section 7.1.4 of RFC 7231
- */
- public static final String VARY = "Vary";
- /**
- * The HTTP {@code Via} header field name.
- * @see Section 5.7.1 of RFC 7230
- */
- public static final String VIA = "Via";
- /**
- * The HTTP {@code Warning} header field name.
- * @see Section 5.5 of RFC 7234
- */
- public static final String WARNING = "Warning";
- /**
- * The HTTP {@code WWW-Authenticate} header field name.
- * @see Section 4.1 of RFC 7235
- */
- public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
-
- /**
- * Date formats as specified in the HTTP RFC
- * @see Section 7.1.1.1 of RFC 7231
- */
- private static final String[] DATE_FORMATS = new String[] {
- "EEE, dd MMM yyyy HH:mm:ss zzz",
- "EEE, dd-MMM-yy HH:mm:ss zzz",
- "EEE MMM dd HH:mm:ss yyyy"
- };
-
- private static TimeZone GMT = TimeZone.getTimeZone("GMT");
-
-
- private final Map> headers;
-
- private final Map> cookies;
-
-
- /**
- * Constructs a new, empty instance of the {@code HttpHeaders} object.
- */
- public HttpHeaders() {
- this(new LinkedCaseInsensitiveMap>(8, Locale.ENGLISH), null, false);
- }
-
- /**
- * Constructor with a map of HTTP input cookies (e.g. cookies sent by client)
- * that enables lazy initialization on first access of the map.
- * @param inputCookies input cookies
- */
- public HttpHeaders(Map> inputCookies) {
- this(new LinkedCaseInsensitiveMap>(8, Locale.ENGLISH), inputCookies, false);
- Assert.notNull(cookies, "'inputCookies' is required.");
- }
-
- /**
- * Private constructor that can create read-only {@code HttpHeader} instances.
- */
- private HttpHeaders(Map> headers, Map> cookies,
- boolean readOnly) {
-
- Assert.notNull(headers, "'headers' must not be null");
- if (readOnly) {
- Map> map =
- new LinkedCaseInsensitiveMap>(headers.size(), Locale.ENGLISH);
- for (Entry> entry : headers.entrySet()) {
- List values = Collections.unmodifiableList(entry.getValue());
- map.put(entry.getKey(), values);
- }
- this.headers = Collections.unmodifiableMap(map);
- this.cookies = (cookies != null ? Collections.unmodifiableMap(cookies) : Collections.emptyMap());
- }
- else {
- this.headers = headers;
- this.cookies = (cookies != null ? cookies : new LinkedCaseInsensitiveMap<>());
- }
- }
-
- /**
- * Set the list of acceptable {@linkplain MediaType media types},
- * as specified by the {@code Accept} header.
- */
- public void setAccept(List acceptableMediaTypes) {
- set(ACCEPT, MediaType.toString(acceptableMediaTypes));
- }
-
- /**
- * Return the list of acceptable {@linkplain MediaType media types},
- * as specified by the {@code Accept} header.
- * Returns an empty list when the acceptable media types are unspecified.
- */
- public List getAccept() {
- String value = getFirst(ACCEPT);
- List result = (value != null ? MediaType.parseMediaTypes(value) : Collections.emptyList());
-
- // Some containers parse 'Accept' into multiple values
- if (result.size() == 1) {
- List acceptHeader = get(ACCEPT);
- if (acceptHeader.size() > 1) {
- value = StringUtils.collectionToCommaDelimitedString(acceptHeader);
- result = MediaType.parseMediaTypes(value);
- }
- }
-
- return result;
- }
-
- /**
- * Set the (new) value of the {@code Access-Control-Allow-Credentials} response header.
- */
- public void setAccessControlAllowCredentials(boolean allowCredentials) {
- set(ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.toString(allowCredentials));
- }
-
- /**
- * Returns the value of the {@code Access-Control-Allow-Credentials} response header.
- */
- public boolean getAccessControlAllowCredentials() {
- return new Boolean(getFirst(ACCESS_CONTROL_ALLOW_CREDENTIALS));
- }
-
- /**
- * Set the (new) value of the {@code Access-Control-Allow-Headers} response header.
- */
- public void setAccessControlAllowHeaders(List allowedHeaders) {
- set(ACCESS_CONTROL_ALLOW_HEADERS, toCommaDelimitedString(allowedHeaders));
- }
-
- /**
- * Returns the value of the {@code Access-Control-Allow-Headers} response header.
- */
- public List getAccessControlAllowHeaders() {
- return getFirstValueAsList(ACCESS_CONTROL_ALLOW_HEADERS);
- }
-
- /**
- * Set the (new) value of the {@code Access-Control-Allow-Methods} response header.
- */
- public void setAccessControlAllowMethods(List allowedMethods) {
- set(ACCESS_CONTROL_ALLOW_METHODS, StringUtils.collectionToCommaDelimitedString(allowedMethods));
- }
-
- /**
- * Returns the value of the {@code Access-Control-Allow-Methods} response header.
- */
- public List getAccessControlAllowMethods() {
- List result = new ArrayList();
- String value = getFirst(ACCESS_CONTROL_ALLOW_METHODS);
- if (value != null) {
- String[] tokens = value.split(",\\s*");
- for (String token : tokens) {
- result.add(HttpMethod.valueOf(token));
- }
- }
- return result;
- }
-
- /**
- * Set the (new) value of the {@code Access-Control-Allow-Origin} response header.
- */
- public void setAccessControlAllowOrigin(String allowedOrigin) {
- set(ACCESS_CONTROL_ALLOW_ORIGIN, allowedOrigin);
- }
-
- /**
- * Returns the value of the {@code Access-Control-Allow-Origin} response header.
- */
- public String getAccessControlAllowOrigin() {
- return getFirst(ACCESS_CONTROL_ALLOW_ORIGIN);
- }
-
- /**
- * Set the (new) value of the {@code Access-Control-Expose-Headers} response header.
- */
- public void setAccessControlExposeHeaders(List exposedHeaders) {
- set(ACCESS_CONTROL_EXPOSE_HEADERS, toCommaDelimitedString(exposedHeaders));
- }
-
- /**
- * Returns the value of the {@code Access-Control-Expose-Headers} response header.
- */
- public List getAccessControlExposeHeaders() {
- return getFirstValueAsList(ACCESS_CONTROL_EXPOSE_HEADERS);
- }
-
- /**
- * Set the (new) value of the {@code Access-Control-Max-Age} response header.
- */
- public void setAccessControlMaxAge(long maxAge) {
- set(ACCESS_CONTROL_MAX_AGE, Long.toString(maxAge));
- }
-
- /**
- * Returns the value of the {@code Access-Control-Max-Age} response header.
- * Returns -1 when the max age is unknown.
- */
- public long getAccessControlMaxAge() {
- String value = getFirst(ACCESS_CONTROL_MAX_AGE);
- return (value != null ? Long.parseLong(value) : -1);
- }
-
- /**
- * Set the (new) value of the {@code Access-Control-Request-Headers} request header.
- */
- public void setAccessControlRequestHeaders(List requestHeaders) {
- set(ACCESS_CONTROL_REQUEST_HEADERS, toCommaDelimitedString(requestHeaders));
- }
-
- /**
- * Returns the value of the {@code Access-Control-Request-Headers} request header.
- */
- public List getAccessControlRequestHeaders() {
- return getFirstValueAsList(ACCESS_CONTROL_REQUEST_HEADERS);
- }
-
- /**
- * Set the (new) value of the {@code Access-Control-Request-Method} request header.
- */
- public void setAccessControlRequestMethod(HttpMethod requestedMethod) {
- set(ACCESS_CONTROL_REQUEST_METHOD, requestedMethod.name());
- }
-
- /**
- * Returns the value of the {@code Access-Control-Request-Method} request header.
- */
- public HttpMethod getAccessControlRequestMethod() {
- String value = getFirst(ACCESS_CONTROL_REQUEST_METHOD);
- return (value != null ? HttpMethod.valueOf(value) : null);
- }
-
- /**
- * Set the list of acceptable {@linkplain Charset charsets},
- * as specified by the {@code Accept-Charset} header.
- */
- public void setAcceptCharset(List acceptableCharsets) {
- StringBuilder builder = new StringBuilder();
- for (Iterator iterator = acceptableCharsets.iterator(); iterator.hasNext();) {
- Charset charset = iterator.next();
- builder.append(charset.name().toLowerCase(Locale.ENGLISH));
- if (iterator.hasNext()) {
- builder.append(", ");
- }
- }
- set(ACCEPT_CHARSET, builder.toString());
- }
-
- /**
- * Return the list of acceptable {@linkplain Charset charsets},
- * as specified by the {@code Accept-Charset} header.
- */
- public List getAcceptCharset() {
- List result = new ArrayList();
- String value = getFirst(ACCEPT_CHARSET);
- if (value != null) {
- String[] tokens = value.split(",\\s*");
- for (String token : tokens) {
- int paramIdx = token.indexOf(';');
- String charsetName;
- if (paramIdx == -1) {
- charsetName = token;
- }
- else {
- charsetName = token.substring(0, paramIdx);
- }
- if (!charsetName.equals("*")) {
- result.add(Charset.forName(charsetName));
- }
- }
- }
- return result;
- }
-
- /**
- * Set the set of allowed {@link HttpMethod HTTP methods},
- * as specified by the {@code Allow} header.
- */
- public void setAllow(Set allowedMethods) {
- set(ALLOW, StringUtils.collectionToCommaDelimitedString(allowedMethods));
- }
-
- /**
- * Return the set of allowed {@link HttpMethod HTTP methods},
- * as specified by the {@code Allow} header.
- * Returns an empty set when the allowed methods are unspecified.
- */
- public Set getAllow() {
- String value = getFirst(ALLOW);
- if (!StringUtils.isEmpty(value)) {
- List allowedMethod = new ArrayList(5);
- String[] tokens = value.split(",\\s*");
- for (String token : tokens) {
- allowedMethod.add(HttpMethod.valueOf(token));
- }
- return EnumSet.copyOf(allowedMethod);
- }
- else {
- return EnumSet.noneOf(HttpMethod.class);
- }
- }
-
- /**
- * Set the (new) value of the {@code Cache-Control} header.
- */
- public void setCacheControl(String cacheControl) {
- set(CACHE_CONTROL, cacheControl);
- }
-
- /**
- * Returns the value of the {@code Cache-Control} header.
- */
- public String getCacheControl() {
- return getFirst(CACHE_CONTROL);
- }
-
- /**
- * Set the (new) value of the {@code Connection} header.
- */
- public void setConnection(String connection) {
- set(CONNECTION, connection);
- }
-
- /**
- * Set the (new) value of the {@code Connection} header.
- */
- public void setConnection(List connection) {
- set(CONNECTION, toCommaDelimitedString(connection));
- }
-
- /**
- * Returns the value of the {@code Connection} header.
- */
- public List getConnection() {
- return getFirstValueAsList(CONNECTION);
- }
-
- /**
- * Set the (new) value of the {@code Content-Disposition} header
- * for {@code form-data}.
- * @param name the control name
- * @param filename the filename (may be {@code null})
- */
- public void setContentDispositionFormData(String name, String filename) {
- Assert.notNull(name, "'name' must not be null");
- StringBuilder builder = new StringBuilder("form-data; name=\"");
- builder.append(name).append('\"');
- if (filename != null) {
- builder.append("; filename=\"");
- builder.append(filename).append('\"');
- }
- set(CONTENT_DISPOSITION, builder.toString());
- }
-
- /**
- * Set the length of the body in bytes, as specified by the
- * {@code Content-Length} header.
- */
- public void setContentLength(long contentLength) {
- set(CONTENT_LENGTH, Long.toString(contentLength));
- }
-
- /**
- * Return the length of the body in bytes, as specified by the
- * {@code Content-Length} header.
- * Returns -1 when the content-length is unknown.
- */
- public long getContentLength() {
- String value = getFirst(CONTENT_LENGTH);
- return (value != null ? Long.parseLong(value) : -1);
- }
-
- /**
- * Set the {@linkplain MediaType media type} of the body,
- * as specified by the {@code Content-Type} header.
- */
- public void setContentType(MediaType mediaType) {
- Assert.isTrue(!mediaType.isWildcardType(), "'Content-Type' cannot contain wildcard type '*'");
- Assert.isTrue(!mediaType.isWildcardSubtype(), "'Content-Type' cannot contain wildcard subtype '*'");
- set(CONTENT_TYPE, mediaType.toString());
- }
-
- /**
- * Return the {@linkplain MediaType media type} of the body, as specified
- * by the {@code Content-Type} header.
- *
Returns {@code null} when the content-type is unknown.
- */
- public MediaType getContentType() {
- String value = getFirst(CONTENT_TYPE);
- return (StringUtils.hasLength(value) ? MediaType.parseMediaType(value) : null);
- }
-
- /**
- * Add an HTTP cookie.
- */
- public void addCookie(HttpCookie cookie) {
- String name = cookie.getName();
- List set = this.cookies.get(name);
- if (set == null) {
- set = new ArrayList<>();
- this.cookies.put(name, set);
- }
- set.add(cookie);
- }
-
- /**
- * Return a map with {@link HttpCookie}s. When reading input cookies this map
- * cannot be modified. When writing output cookies, this map is mutable.
- */
- public Map> getCookies() {
- return this.cookies;
- }
-
- /**
- * Set the date and time at which the message was created, as specified
- * by the {@code Date} header.
- * The date should be specified as the number of milliseconds since
- * January 1, 1970 GMT.
- */
- public void setDate(long date) {
- setDate(DATE, date);
- }
-
- /**
- * Return the date and time at which the message was created, as specified
- * by the {@code Date} header.
- *
The date is returned as the number of milliseconds since
- * January 1, 1970 GMT. Returns -1 when the date is unknown.
- * @throws IllegalArgumentException if the value can't be converted to a date
- */
- public long getDate() {
- return getFirstDate(DATE);
- }
-
- /**
- * Set the (new) entity tag of the body, as specified by the {@code ETag} header.
- */
- public void setETag(String eTag) {
- if (eTag != null) {
- Assert.isTrue(eTag.startsWith("\"") || eTag.startsWith("W/"),
- "Invalid eTag, does not start with W/ or \"");
- Assert.isTrue(eTag.endsWith("\""), "Invalid eTag, does not end with \"");
- }
- set(ETAG, eTag);
- }
-
- /**
- * Return the entity tag of the body, as specified by the {@code ETag} header.
- */
- public String getETag() {
- return getFirst(ETAG);
- }
-
- /**
- * Set the date and time at which the message is no longer valid,
- * as specified by the {@code Expires} header.
- *
The date should be specified as the number of milliseconds since
- * January 1, 1970 GMT.
- */
- public void setExpires(long expires) {
- setDate(EXPIRES, expires);
- }
-
- /**
- * Return the date and time at which the message is no longer valid,
- * as specified by the {@code Expires} header.
- *
The date is returned as the number of milliseconds since
- * January 1, 1970 GMT. Returns -1 when the date is unknown.
- */
- public long getExpires() {
- try {
- return getFirstDate(EXPIRES);
- }
- catch (IllegalArgumentException ex) {
- return -1;
- }
- }
-
- /**
- * Set the (new) value of the {@code If-Modified-Since} header.
- *
The date should be specified as the number of milliseconds since
- * January 1, 1970 GMT.
- */
- public void setIfModifiedSince(long ifModifiedSince) {
- setDate(IF_MODIFIED_SINCE, ifModifiedSince);
- }
-
- /**
- * Return the value of the {@code If-Modified-Since} header.
- *
The date is returned as the number of milliseconds since
- * January 1, 1970 GMT. Returns -1 when the date is unknown.
- */
- public long getIfModifiedSince() {
- return getFirstDate(IF_MODIFIED_SINCE);
- }
-
- /**
- * Set the (new) value of the {@code If-None-Match} header.
- */
- public void setIfNoneMatch(String ifNoneMatch) {
- set(IF_NONE_MATCH, ifNoneMatch);
- }
-
- /**
- * Set the (new) values of the {@code If-None-Match} header.
- */
- public void setIfNoneMatch(List ifNoneMatchList) {
- set(IF_NONE_MATCH, toCommaDelimitedString(ifNoneMatchList));
- }
-
- protected String toCommaDelimitedString(List list) {
- StringBuilder builder = new StringBuilder();
- for (Iterator iterator = list.iterator(); iterator.hasNext();) {
- String ifNoneMatch = iterator.next();
- builder.append(ifNoneMatch);
- if (iterator.hasNext()) {
- builder.append(", ");
- }
- }
- return builder.toString();
- }
-
- /**
- * Return the value of the {@code If-None-Match} header.
- */
- public List getIfNoneMatch() {
- return getFirstValueAsList(IF_NONE_MATCH);
- }
-
- protected List getFirstValueAsList(String header) {
- List result = new ArrayList();
- String value = getFirst(header);
- if (value != null) {
- String[] tokens = value.split(",\\s*");
- for (String token : tokens) {
- result.add(token);
- }
- }
- return result;
- }
-
- /**
- * Set the time the resource was last changed, as specified by the
- * {@code Last-Modified} header.
- * The date should be specified as the number of milliseconds since
- * January 1, 1970 GMT.
- */
- public void setLastModified(long lastModified) {
- setDate(LAST_MODIFIED, lastModified);
- }
-
- /**
- * Return the time the resource was last changed, as specified by the
- * {@code Last-Modified} header.
- *
The date is returned as the number of milliseconds since
- * January 1, 1970 GMT. Returns -1 when the date is unknown.
- */
- public long getLastModified() {
- return getFirstDate(LAST_MODIFIED);
- }
-
- /**
- * Set the (new) location of a resource,
- * as specified by the {@code Location} header.
- */
- public void setLocation(URI location) {
- set(LOCATION, location.toASCIIString());
- }
-
- /**
- * Return the (new) location of a resource
- * as specified by the {@code Location} header.
- *
Returns {@code null} when the location is unknown.
- */
- public URI getLocation() {
- String value = getFirst(LOCATION);
- return (value != null ? URI.create(value) : null);
- }
-
- /**
- * Set the (new) value of the {@code Origin} header.
- */
- public void setOrigin(String origin) {
- set(ORIGIN, origin);
- }
-
- /**
- * Return the value of the {@code Origin} header.
- */
- public String getOrigin() {
- return getFirst(ORIGIN);
- }
-
- /**
- * Set the (new) value of the {@code Pragma} header.
- */
- public void setPragma(String pragma) {
- set(PRAGMA, pragma);
- }
-
- /**
- * Return the value of the {@code Pragma} header.
- */
- public String getPragma() {
- return getFirst(PRAGMA);
- }
-
- /**
- * Sets the (new) value of the {@code Range} header.
- */
- public void setRange(List ranges) {
- String value = HttpRange.toString(ranges);
- set(RANGE, value);
- }
-
- /**
- * Returns the value of the {@code Range} header.
- * Returns an empty list when the range is unknown.
- */
- public List getRange() {
- String value = getFirst(RANGE);
- return HttpRange.parseRanges(value);
- }
-
- /**
- * Set the (new) value of the {@code Upgrade} header.
- */
- public void setUpgrade(String upgrade) {
- set(UPGRADE, upgrade);
- }
-
- /**
- * Returns the value of the {@code Upgrade} header.
- */
- public String getUpgrade() {
- return getFirst(UPGRADE);
- }
-
- /**
- * Parse the first header value for the given header name as a date,
- * return -1 if there is no value, or raise {@link IllegalArgumentException}
- * if the value cannot be parsed as a date.
- */
- public long getFirstDate(String headerName) {
- String headerValue = getFirst(headerName);
- if (headerValue == null) {
- return -1;
- }
- for (String dateFormat : DATE_FORMATS) {
- SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US);
- simpleDateFormat.setTimeZone(GMT);
- try {
- return simpleDateFormat.parse(headerValue).getTime();
- }
- catch (ParseException ex) {
- // ignore
- }
- }
- throw new IllegalArgumentException("Cannot parse date value \"" + headerValue +
- "\" for \"" + headerName + "\" header");
- }
-
- /**
- * Set the given date under the given header name after formatting it as a string
- * using the pattern {@code "EEE, dd MMM yyyy HH:mm:ss zzz"}. The equivalent of
- * {@link #set(String, String)} but for date headers.
- */
- public void setDate(String headerName, long date) {
- SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMATS[0], Locale.US);
- dateFormat.setTimeZone(GMT);
- set(headerName, dateFormat.format(new Date(date)));
- }
-
- /**
- * Return the first header value for the given header name, if any.
- * @param headerName the header name
- * @return the first header value, or {@code null} if none
- */
- @Override
- public String getFirst(String headerName) {
- List headerValues = this.headers.get(headerName);
- return (headerValues != null ? headerValues.get(0) : null);
- }
-
- /**
- * Add the given, single header value under the given name.
- * @param headerName the header name
- * @param headerValue the header value
- * @throws UnsupportedOperationException if adding headers is not supported
- * @see #put(String, List)
- * @see #set(String, String)
- */
- @Override
- public void add(String headerName, String headerValue) {
- List headerValues = this.headers.get(headerName);
- if (headerValues == null) {
- headerValues = new LinkedList();
- this.headers.put(headerName, headerValues);
- }
- headerValues.add(headerValue);
- }
-
- /**
- * Set the given, single header value under the given name.
- * @param headerName the header name
- * @param headerValue the header value
- * @throws UnsupportedOperationException if adding headers is not supported
- * @see #put(String, List)
- * @see #add(String, String)
- */
- @Override
- public void set(String headerName, String headerValue) {
- List headerValues = new LinkedList();
- headerValues.add(headerValue);
- this.headers.put(headerName, headerValues);
- }
-
- @Override
- public void setAll(Map values) {
- for (Entry entry : values.entrySet()) {
- set(entry.getKey(), entry.getValue());
- }
- }
-
- @Override
- public Map toSingleValueMap() {
- LinkedHashMap singleValueMap = new LinkedHashMap(this.headers.size());
- for (Entry> entry : this.headers.entrySet()) {
- singleValueMap.put(entry.getKey(), entry.getValue().get(0));
- }
- return singleValueMap;
- }
-
-
- // Map implementation
-
- @Override
- public int size() {
- return this.headers.size();
- }
-
- @Override
- public boolean isEmpty() {
- return this.headers.isEmpty();
- }
-
- @Override
- public boolean containsKey(Object key) {
- return this.headers.containsKey(key);
- }
-
- @Override
- public boolean containsValue(Object value) {
- return this.headers.containsValue(value);
- }
-
- @Override
- public List get(Object key) {
- return this.headers.get(key);
- }
-
- @Override
- public List put(String key, List value) {
- return this.headers.put(key, value);
- }
-
- @Override
- public List remove(Object key) {
- return this.headers.remove(key);
- }
-
- @Override
- public void putAll(Map extends String, ? extends List> map) {
- this.headers.putAll(map);
- }
-
- @Override
- public void clear() {
- this.headers.clear();
- }
-
- @Override
- public Set keySet() {
- return this.headers.keySet();
- }
-
- @Override
- public Collection> values() {
- return this.headers.values();
- }
-
- @Override
- public Set>> entrySet() {
- return this.headers.entrySet();
- }
-
-
- @Override
- public boolean equals(Object other) {
- if (this == other) {
- return true;
- }
- if (!(other instanceof HttpHeaders)) {
- return false;
- }
- HttpHeaders otherHeaders = (HttpHeaders) other;
- return this.headers.equals(otherHeaders.headers);
- }
-
- @Override
- public int hashCode() {
- return this.headers.hashCode();
- }
-
- @Override
- public String toString() {
- return this.headers.toString();
- }
-
-
- /**
- * Return a {@code HttpHeaders} object that can only be read, not written to.
- */
- public static HttpHeaders readOnlyHttpHeaders(HttpHeaders headers) {
- return new HttpHeaders(headers, headers.getCookies(), true);
- }
-
-}
diff --git a/spring-web-reactive/src/main/java/org/springframework/http/ServerHttpCookie.java b/spring-web-reactive/src/main/java/org/springframework/http/ServerHttpCookie.java
new file mode 100644
index 0000000000..4d7d698d6e
--- /dev/null
+++ b/spring-web-reactive/src/main/java/org/springframework/http/ServerHttpCookie.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2002-2015 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.http;
+
+import java.time.Duration;
+import java.util.Optional;
+
+import org.springframework.util.Assert;
+import org.springframework.util.ObjectUtils;
+
+/**
+ * Represents a server-side cookie with extra attributes that a server can
+ * include in a Set-Cookie response header.
+ *
+ * Use {@link #with} to create a {@code ServerHttpCookie}.
+ *
+ * @author Rossen Stoyanchev
+ * @see RFC 6265
+ */
+public final class ServerHttpCookie extends HttpCookie {
+
+ private final Duration maxAge;
+
+ private final Optional domain;
+
+ private final Optional path;
+
+ private final boolean secure;
+
+ private final boolean httpOnly;
+
+
+ /**
+ * Private constructor. See {@link #with(String, String)}.
+ */
+ private ServerHttpCookie(String name, String value, Duration maxAge, String domain,
+ String path, boolean secure, boolean httpOnly) {
+
+ super(name, value);
+ Assert.notNull(maxAge);
+ this.maxAge = maxAge;
+ this.domain = Optional.ofNullable(domain);
+ this.path = Optional.ofNullable(path);
+ this.secure = secure;
+ this.httpOnly = httpOnly;
+ }
+
+
+ /**
+ * Return the cookie "Max-Age" attribute in seconds.
+ *
+ * A positive value indicates when the cookie expires relative to the
+ * current time. A value of 0 means the cookie should expire immediately.
+ * A negative value means no "Max-Age" attribute in which case the cookie
+ * is removed when the browser is closed.
+ */
+ public Duration getMaxAge() {
+ return this.maxAge;
+ }
+
+ /**
+ * Return the cookie "Domain" attribute.
+ */
+ public Optional getDomain() {
+ return this.domain;
+ }
+
+ /**
+ * Return the cookie "Path" attribute.
+ */
+ public Optional getPath() {
+ return this.path;
+ }
+
+ /**
+ * Return {@code true} if the cookie has the "Secure" attribute.
+ */
+ public boolean isSecure() {
+ return this.secure;
+ }
+
+ /**
+ * Return {@code true} if the cookie has the "HttpOnly" attribute.
+ * @see http://www.owasp.org/index.php/HTTPOnly
+ */
+ public boolean isHttpOnly() {
+ return this.httpOnly;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + ObjectUtils.nullSafeHashCode(this.domain);
+ result = 31 * result + ObjectUtils.nullSafeHashCode(this.path);
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof ServerHttpCookie)) {
+ return false;
+ }
+ ServerHttpCookie otherCookie = (ServerHttpCookie) other;
+ return (getName().equalsIgnoreCase(otherCookie.getName()) &&
+ ObjectUtils.nullSafeEquals(this.path, otherCookie.getPath()) &&
+ ObjectUtils.nullSafeEquals(this.domain, otherCookie.getDomain()));
+ }
+
+
+ /**
+ * Factory method to obtain a builder for a server-defined cookie that starts
+ * with a name-value pair and may also include attributes.
+ * @param name the cookie name
+ * @param value the cookie value
+ * @return the created cookie instance
+ */
+ public static ServerHttpCookieBuilder with(final String name, final String value) {
+
+ return new ServerHttpCookieBuilder() {
+
+ private Duration maxAge = Duration.ofSeconds(-1);
+
+ private String domain;
+
+ private String path;
+
+ private boolean secure;
+
+ private boolean httpOnly;
+
+
+ @Override
+ public ServerHttpCookieBuilder maxAge(Duration maxAge) {
+ this.maxAge = maxAge;
+ return this;
+ }
+
+ @Override
+ public ServerHttpCookieBuilder domain(String domain) {
+ this.domain = domain;
+ return this;
+ }
+
+ @Override
+ public ServerHttpCookieBuilder path(String path) {
+ this.path = path;
+ return this;
+ }
+
+ @Override
+ public ServerHttpCookieBuilder secure() {
+ this.secure = true;
+ return this;
+ }
+
+ @Override
+ public ServerHttpCookieBuilder httpOnly() {
+ this.httpOnly = true;
+ return this;
+ }
+
+ @Override
+ public ServerHttpCookie build() {
+ return new ServerHttpCookie(name, value, this.maxAge, this.domain, this.path,
+ this.secure, this.httpOnly);
+ }
+ };
+ }
+
+ /**
+ * A builder for a server-defined HttpCookie with attributes.
+ */
+ public interface ServerHttpCookieBuilder {
+
+ /**
+ * Set the cookie "Max-Age" attribute.
+ *
+ * A positive value indicates when the cookie should expire relative
+ * to the current time. A value of 0 means the cookie should expire
+ * immediately. A negative value results in no "Max-Age" attribute in
+ * which case the cookie is removed when the browser is closed.
+ */
+ ServerHttpCookieBuilder maxAge(Duration maxAge);
+
+ /**
+ * Set the cookie "Path" attribute.
+ */
+ ServerHttpCookieBuilder path(String path);
+
+ /**
+ * Set the cookie "Domain" attribute.
+ */
+ ServerHttpCookieBuilder domain(String domain);
+
+ /**
+ * Add the "Secure" attribute to the cookie.
+ */
+ ServerHttpCookieBuilder secure();
+
+ /**
+ * Add the "HttpOnly" attribute to the cookie.
+ * @see http://www.owasp.org/index.php/HTTPOnly
+ */
+ ServerHttpCookieBuilder httpOnly();
+
+ /**
+ * Create the HttpCookie.
+ */
+ ServerHttpCookie build();
+ }
+
+}
diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java
index ebc5641cb1..f9301eab6d 100644
--- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java
+++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java
@@ -17,14 +17,14 @@ package org.springframework.http.server.reactive;
import java.net.URI;
import java.net.URISyntaxException;
-import java.util.Collection;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.util.LinkedCaseInsensitiveMap;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
/**
* Common base class for {@link ServerHttpRequest} implementations.
@@ -37,6 +37,8 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest {
private HttpHeaders headers;
+ private MultiValueMap cookies;
+
@Override
public URI getURI() {
@@ -61,7 +63,7 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest {
@Override
public HttpHeaders getHeaders() {
if (this.headers == null) {
- this.headers = new HttpHeaders(new HttpCookieInputMap());
+ this.headers = new HttpHeaders();
initHeaders(this.headers);
}
return this.headers;
@@ -74,89 +76,20 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest {
*/
protected abstract void initHeaders(HttpHeaders headers);
+ @Override
+ public MultiValueMap getCookies() {
+ if (this.cookies == null) {
+ this.cookies = new LinkedMultiValueMap();
+ initCookies(this.cookies);
+ }
+ return this.cookies;
+ }
+
/**
* Initialize the cookies from the underlying request. Invoked lazily on the
* first access to cookies via {@link #getHeaders()} and then cached.
* @param cookies the map to add cookies to
*/
- protected abstract void initCookies(Map> cookies);
-
-
- /**
- * Read-only map of input cookies with lazy initialization.
- */
- private class HttpCookieInputMap implements Map> {
-
- private Map> cookies;
-
-
- private Map> getCookies() {
- if (this.cookies == null) {
- this.cookies = new LinkedCaseInsensitiveMap<>();
- initCookies(this.cookies);
- }
- return this.cookies;
- }
-
- @Override
- public int size() {
- return getCookies().size();
- }
-
- @Override
- public boolean isEmpty() {
- return getCookies().isEmpty();
- }
-
- @Override
- public boolean containsKey(Object key) {
- return getCookies().containsKey(key);
- }
-
- @Override
- public boolean containsValue(Object value) {
- return getCookies().containsValue(value);
- }
-
- @Override
- public List get(Object key) {
- return getCookies().get(key);
- }
-
- @Override
- public Set keySet() {
- return getCookies().keySet();
- }
-
- @Override
- public Collection> values() {
- return getCookies().values();
- }
-
- @Override
- public Set>> entrySet() {
- return getCookies().entrySet();
- }
-
- @Override
- public List put(String key, List value) {
- throw new UnsupportedOperationException("Can't modify client sent cookies.");
- }
-
- @Override
- public List remove(Object key) {
- throw new UnsupportedOperationException("Can't modify client sent cookies.");
- }
-
- @Override
- public void putAll(Map extends String, ? extends List> map) {
- throw new UnsupportedOperationException("Can't modify client sent cookies.");
- }
-
- @Override
- public void clear() {
- throw new UnsupportedOperationException("Can't modify client sent cookies.");
- }
- }
+ protected abstract void initCookies(MultiValueMap cookies);
}
diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpResponse.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpResponse.java
index 42dbf1c200..e4eb3e4feb 100644
--- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpResponse.java
+++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpResponse.java
@@ -17,6 +17,7 @@ package org.springframework.http.server.reactive;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
@@ -24,8 +25,13 @@ import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
+import org.springframework.http.ServerHttpCookie;
import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
/**
@@ -37,6 +43,8 @@ public abstract class AbstractServerHttpResponse implements ServerHttpResponse {
private final HttpHeaders headers;
+ private final MultiValueMap cookies;
+
private AtomicReference state = new AtomicReference<>(State.NEW);
private final List>> beforeCommitActions = new ArrayList<>(4);
@@ -44,6 +52,7 @@ public abstract class AbstractServerHttpResponse implements ServerHttpResponse {
protected AbstractServerHttpResponse() {
this.headers = new HttpHeaders();
+ this.cookies = new LinkedMultiValueMap();
}
@@ -55,6 +64,14 @@ public abstract class AbstractServerHttpResponse implements ServerHttpResponse {
return this.headers;
}
+ @Override
+ public MultiValueMap getCookies() {
+ if (State.COMITTED.equals(this.state.get())) {
+ return CollectionUtils.unmodifiableMultiValueMap(this.cookies);
+ }
+ return this.cookies;
+ }
+
@Override
public Mono setBody(Publisher publisher) {
return new WriteWithOperator<>(publisher, writePublisher ->
diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpRequest.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpRequest.java
index 8f40ed958d..db3bb6186e 100644
--- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpRequest.java
+++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpRequest.java
@@ -18,9 +18,6 @@ package org.springframework.http.server.reactive;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
import reactor.core.publisher.Flux;
import reactor.io.buffer.Buffer;
@@ -33,6 +30,7 @@ import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
+import org.springframework.util.MultiValueMap;
/**
* Adapt {@link ServerHttpRequest} to the Reactor Net {@link HttpChannel}.
@@ -76,15 +74,11 @@ public class ReactorServerHttpRequest extends AbstractServerHttpRequest {
}
@Override
- protected void initCookies(Map> cookies) {
+ protected void initCookies(MultiValueMap cookies) {
for (String name : this.channel.cookies().keySet()) {
- List list = cookies.get(name);
- if (list == null) {
- list = new ArrayList<>();
- cookies.put(name, list);
- }
for (Cookie cookie : this.channel.cookies().get(name)) {
- list.add(HttpCookie.clientCookie(name, cookie.value()));
+ HttpCookie httpCookie = new HttpCookie(name, cookie.value());
+ cookies.add(name, httpCookie);
}
}
}
diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpResponse.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpResponse.java
index 011b3d599a..37ec2b309e 100644
--- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpResponse.java
+++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ReactorServerHttpResponse.java
@@ -16,6 +16,7 @@
package org.springframework.http.server.reactive;
import java.time.Duration;
+import java.util.Optional;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
@@ -26,8 +27,8 @@ import reactor.io.net.http.model.Cookie;
import reactor.io.net.http.model.Status;
import org.springframework.core.io.buffer.DataBuffer;
-import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
+import org.springframework.http.ServerHttpCookie;
import org.springframework.util.Assert;
/**
@@ -73,10 +74,10 @@ public class ReactorServerHttpResponse extends AbstractServerHttpResponse {
@Override
protected void writeCookies() {
- for (String name : getHeaders().getCookies().keySet()) {
- for (HttpCookie httpCookie : getHeaders().getCookies().get(name)) {
- Cookie reactorCookie = new ReactorCookie(httpCookie);
- this.channel.addResponseCookie(name, reactorCookie);
+ for (String name : getCookies().keySet()) {
+ for (ServerHttpCookie httpCookie : getCookies().get(name)) {
+ Cookie cookie = new ReactorCookie(httpCookie);
+ this.channel.addResponseCookie(name, cookie);
}
}
}
@@ -87,10 +88,10 @@ public class ReactorServerHttpResponse extends AbstractServerHttpResponse {
*/
private final static class ReactorCookie extends Cookie {
- private final HttpCookie httpCookie;
+ private final ServerHttpCookie httpCookie;
- public ReactorCookie(HttpCookie httpCookie) {
+ public ReactorCookie(ServerHttpCookie httpCookie) {
this.httpCookie = httpCookie;
}
@@ -117,12 +118,14 @@ public class ReactorServerHttpResponse extends AbstractServerHttpResponse {
@Override
public String domain() {
- return this.httpCookie.getDomain();
+ Optional domain = this.httpCookie.getDomain();
+ return (domain.isPresent() ? domain.get() : null);
}
@Override
public String path() {
- return this.httpCookie.getPath();
+ Optional path = this.httpCookie.getPath();
+ return (path.isPresent() ? path.get() : null);
}
@Override
diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java
index 7fd9e01d2a..e3b1562005 100644
--- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java
+++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpRequest.java
@@ -18,9 +18,6 @@ package org.springframework.http.server.reactive;
import java.net.URI;
import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.cookie.Cookie;
@@ -35,6 +32,7 @@ import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
+import org.springframework.util.MultiValueMap;
/**
* Adapt {@link ServerHttpRequest} to the RxNetty {@link HttpServerRequest}.
@@ -79,15 +77,11 @@ public class RxNettyServerHttpRequest extends AbstractServerHttpRequest {
}
@Override
- protected void initCookies(Map> map) {
+ protected void initCookies(MultiValueMap cookies) {
for (String name : this.request.getCookies().keySet()) {
- List list = map.get(name);
- if (list == null) {
- list = new ArrayList<>();
- map.put(name, list);
- }
for (Cookie cookie : this.request.getCookies().get(name)) {
- list.add(HttpCookie.clientCookie(name, cookie.value()));
+ HttpCookie httpCookie = new HttpCookie(name, cookie.value());
+ cookies.add(name, httpCookie);
}
}
}
diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpResponse.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpResponse.java
index ca064ad2b5..9cb04d6719 100644
--- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpResponse.java
+++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/RxNettyServerHttpResponse.java
@@ -29,8 +29,8 @@ import rx.Observable;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.NettyDataBuffer;
-import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
+import org.springframework.http.ServerHttpCookie;
import org.springframework.util.Assert;
/**
@@ -85,14 +85,18 @@ public class RxNettyServerHttpResponse extends AbstractServerHttpResponse {
@Override
protected void writeCookies() {
- for (String name : getHeaders().getCookies().keySet()) {
- for (HttpCookie httpCookie : getHeaders().getCookies().get(name)) {
+ for (String name : getCookies().keySet()) {
+ for (ServerHttpCookie httpCookie : getCookies().get(name)) {
Cookie cookie = new DefaultCookie(name, httpCookie.getValue());
if (!httpCookie.getMaxAge().isNegative()) {
cookie.setMaxAge(httpCookie.getMaxAge().getSeconds());
}
- cookie.setDomain(httpCookie.getDomain());
- cookie.setPath(httpCookie.getPath());
+ if (httpCookie.getDomain().isPresent()) {
+ cookie.setDomain(httpCookie.getDomain().get());
+ }
+ if (httpCookie.getPath().isPresent()) {
+ cookie.setPath(httpCookie.getPath().get());
+ }
cookie.setSecure(httpCookie.isSecure());
cookie.setHttpOnly(httpCookie.isHttpOnly());
this.response.addCookie(cookie);
diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServerHttpRequest.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServerHttpRequest.java
index 90a52dd13c..695e944b0a 100644
--- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServerHttpRequest.java
+++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServerHttpRequest.java
@@ -16,8 +16,13 @@
package org.springframework.http.server.reactive;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.http.HttpCookie;
import org.springframework.http.HttpRequest;
import org.springframework.http.ReactiveHttpInputMessage;
+import org.springframework.util.MultiValueMap;
/**
* Represents a reactive server-side HTTP request
@@ -26,4 +31,9 @@ import org.springframework.http.ReactiveHttpInputMessage;
*/
public interface ServerHttpRequest extends HttpRequest, ReactiveHttpInputMessage {
+ /**
+ * Return a read-only map of cookies sent by the client.
+ */
+ MultiValueMap getCookies();
+
}
diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServerHttpResponse.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServerHttpResponse.java
index c7449eca34..c6d53276e8 100644
--- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServerHttpResponse.java
+++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServerHttpResponse.java
@@ -16,10 +16,16 @@
package org.springframework.http.server.reactive;
+import java.util.List;
+import java.util.Map;
+
import reactor.core.publisher.Mono;
+import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.ReactiveHttpOutputMessage;
+import org.springframework.http.ServerHttpCookie;
+import org.springframework.util.MultiValueMap;
/**
* Represents a reactive server-side HTTP response.
@@ -34,6 +40,11 @@ public interface ServerHttpResponse extends ReactiveHttpOutputMessage {
*/
void setStatusCode(HttpStatus status);
+ /**
+ * Return a mutable map with cookies to be sent to the client.
+ */
+ MultiValueMap getCookies();
+
/**
* Indicate that request handling is complete, allowing for any cleanup or
* end-of-processing tasks to be performed such as applying header changes
diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java
index 2374d86231..289d20b8c8 100644
--- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java
+++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java
@@ -20,9 +20,7 @@ import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
-import java.util.ArrayList;
import java.util.Enumeration;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.ReadListener;
@@ -45,6 +43,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.util.LinkedCaseInsensitiveMap;
+import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
@@ -127,17 +126,13 @@ public class ServletServerHttpRequest extends AbstractServerHttpRequest {
}
@Override
- protected void initCookies(Map> map) {
+ protected void initCookies(MultiValueMap httpCookies) {
Cookie[] cookies = this.request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
String name = cookie.getName();
- List list = map.get(name);
- if (list == null) {
- list = new ArrayList<>();
- map.put(name, list);
- }
- list.add(HttpCookie.clientCookie(name, cookie.getValue()));
+ HttpCookie httpCookie = new HttpCookie(name, cookie.getValue());
+ httpCookies.add(name, httpCookie);
}
}
}
diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServletServerHttpResponse.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServletServerHttpResponse.java
index 2ee70f42a9..3c0a5fd9cf 100644
--- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServletServerHttpResponse.java
+++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/ServletServerHttpResponse.java
@@ -34,9 +34,9 @@ import org.reactivestreams.Subscription;
import reactor.core.publisher.Mono;
import org.springframework.core.io.buffer.DataBuffer;
-import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
+import org.springframework.http.ServerHttpCookie;
import org.springframework.util.Assert;
/**
@@ -98,17 +98,17 @@ public class ServletServerHttpResponse extends AbstractServerHttpResponse {
@Override
protected void writeCookies() {
- for (String name : getHeaders().getCookies().keySet()) {
- for (HttpCookie httpCookie : getHeaders().getCookies().get(name)) {
+ for (String name : getCookies().keySet()) {
+ for (ServerHttpCookie httpCookie : getCookies().get(name)) {
Cookie cookie = new Cookie(name, httpCookie.getValue());
if (!httpCookie.getMaxAge().isNegative()) {
cookie.setMaxAge((int) httpCookie.getMaxAge().getSeconds());
}
- if (httpCookie.getDomain() != null) {
- cookie.setDomain(httpCookie.getDomain());
+ if (httpCookie.getDomain().isPresent()) {
+ cookie.setDomain(httpCookie.getDomain().get());
}
- if (httpCookie.getPath() != null) {
- cookie.setPath(httpCookie.getPath());
+ if (httpCookie.getPath().isPresent()) {
+ cookie.setPath(httpCookie.getPath().get());
}
cookie.setSecure(httpCookie.isSecure());
cookie.setHttpOnly(httpCookie.isHttpOnly());
diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java
index da38edd041..cebcc776ed 100644
--- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java
+++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java
@@ -18,9 +18,6 @@ package org.springframework.http.server.reactive;
import java.net.URI;
import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.Cookie;
@@ -33,6 +30,7 @@ import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
+import org.springframework.util.MultiValueMap;
/**
* Adapt {@link ServerHttpRequest} to the Underow {@link HttpServerExchange}.
@@ -79,15 +77,11 @@ public class UndertowServerHttpRequest extends AbstractServerHttpRequest {
}
@Override
- protected void initCookies(Map> map) {
+ protected void initCookies(MultiValueMap cookies) {
for (String name : this.exchange.getRequestCookies().keySet()) {
- List list = map.get(name);
- if (list == null) {
- list = new ArrayList<>();
- map.put(name, list);
- }
Cookie cookie = this.exchange.getRequestCookies().get(name);
- list.add(HttpCookie.clientCookie(name, cookie.getValue()));
+ HttpCookie httpCookie = new HttpCookie(name, cookie.getValue());
+ cookies.add(name, httpCookie);
}
}
diff --git a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpResponse.java b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpResponse.java
index dcaa12d233..091bf80fdd 100644
--- a/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpResponse.java
+++ b/spring-web-reactive/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpResponse.java
@@ -28,8 +28,8 @@ import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
import org.springframework.core.io.buffer.DataBuffer;
-import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
+import org.springframework.http.ServerHttpCookie;
import org.springframework.util.Assert;
/**
@@ -80,14 +80,18 @@ public class UndertowServerHttpResponse extends AbstractServerHttpResponse {
@Override
protected void writeCookies() {
- for (String name : getHeaders().getCookies().keySet()) {
- for (HttpCookie httpCookie : getHeaders().getCookies().get(name)) {
+ for (String name : getCookies().keySet()) {
+ for (ServerHttpCookie httpCookie : getCookies().get(name)) {
Cookie cookie = new CookieImpl(name, httpCookie.getValue());
if (!httpCookie.getMaxAge().isNegative()) {
cookie.setMaxAge((int) httpCookie.getMaxAge().getSeconds());
}
- cookie.setDomain(httpCookie.getDomain());
- cookie.setPath(httpCookie.getPath());
+ if (httpCookie.getDomain().isPresent()) {
+ cookie.setDomain(httpCookie.getDomain().get());
+ }
+ if (httpCookie.getPath().isPresent()) {
+ cookie.setPath(httpCookie.getPath().get());
+ }
cookie.setSecure(httpCookie.isSecure());
cookie.setHttpOnly(httpCookie.isHttpOnly());
this.exchange.getResponseCookies().putIfAbsent(name, cookie);
diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/session/CookieWebSessionIdResolver.java b/spring-web-reactive/src/main/java/org/springframework/web/server/session/CookieWebSessionIdResolver.java
index f325be9e67..51aeaae895 100644
--- a/spring-web-reactive/src/main/java/org/springframework/web/server/session/CookieWebSessionIdResolver.java
+++ b/spring-web-reactive/src/main/java/org/springframework/web/server/session/CookieWebSessionIdResolver.java
@@ -18,12 +18,14 @@ package org.springframework.web.server.session;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import org.springframework.http.HttpCookie;
-import org.springframework.http.HttpHeaders;
+import org.springframework.http.ServerHttpCookie;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
+import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
@@ -58,7 +60,7 @@ public class CookieWebSessionIdResolver implements WebSessionIdResolver {
/**
* Set the value for the "Max-Age" attribute of the cookie that holds the
- * session id. For the range of values see {@link HttpCookie#getMaxAge()}.
+ * session id. For the range of values see {@link ServerHttpCookie#getMaxAge()}.
* By default set to -1.
* @param maxAge the maxAge duration value
*/
@@ -76,18 +78,17 @@ public class CookieWebSessionIdResolver implements WebSessionIdResolver {
@Override
public Optional resolveSessionId(ServerWebExchange exchange) {
- HttpHeaders headers = exchange.getRequest().getHeaders();
- List cookies = headers.getCookies().get(getCookieName());
- return (CollectionUtils.isEmpty(cookies) ?
- Optional.empty() : Optional.of(cookies.get(0).getValue()));
+ MultiValueMap cookieMap = exchange.getRequest().getCookies();
+ HttpCookie cookie = cookieMap.getFirst(getCookieName());
+ return (cookie != null ? Optional.of(cookie.getValue()) : Optional.empty());
}
@Override
public void setSessionId(ServerWebExchange exchange, String id) {
Duration maxAge = (StringUtils.hasText(id) ? getCookieMaxAge() : Duration.ofSeconds(0));
- HttpCookie cookie = HttpCookie.serverCookie(getCookieName(), id).maxAge(maxAge).build();
- HttpHeaders headers = exchange.getResponse().getHeaders();
- headers.getCookies().put(getCookieName(), Collections.singletonList(cookie));
+ ServerHttpCookie cookie = ServerHttpCookie.with(getCookieName(), id).maxAge(maxAge).build();
+ MultiValueMap cookieMap = exchange.getResponse().getCookies();
+ cookieMap.set(getCookieName(), cookie);
}
}
diff --git a/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/CookieIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/CookieIntegrationTests.java
index 3a20401b74..547a6f0457 100644
--- a/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/CookieIntegrationTests.java
+++ b/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/CookieIntegrationTests.java
@@ -28,6 +28,7 @@ import reactor.core.publisher.Mono;
import org.springframework.http.HttpCookie;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
+import org.springframework.http.ServerHttpCookie;
import org.springframework.web.client.RestTemplate;
import static org.hamcrest.CoreMatchers.equalTo;
@@ -98,12 +99,12 @@ public class CookieIntegrationTests extends AbstractHttpHandlerIntegrationTests
@Override
public Mono handle(ServerHttpRequest request, ServerHttpResponse response) {
- this.requestCookies = request.getHeaders().getCookies();
+ this.requestCookies = request.getCookies();
this.requestCookies.size(); // Cause lazy loading
- response.getHeaders().addCookie(HttpCookie.serverCookie("SID", "31d4d96e407aad42")
+ response.getCookies().add("SID", ServerHttpCookie.with("SID", "31d4d96e407aad42")
.path("/").secure().httpOnly().build());
- response.getHeaders().addCookie(HttpCookie.serverCookie("lang", "en-US")
+ response.getCookies().add("lang", ServerHttpCookie.with("lang", "en-US")
.domain("example.com").path("/").build());
return response.setComplete();
diff --git a/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/MockServerHttpRequest.java b/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/MockServerHttpRequest.java
index 01612d73d1..7678536a38 100644
--- a/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/MockServerHttpRequest.java
+++ b/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/MockServerHttpRequest.java
@@ -21,8 +21,11 @@ import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
/**
* @author Rossen Stoyanchev
@@ -35,6 +38,8 @@ public class MockServerHttpRequest implements ServerHttpRequest {
private HttpHeaders headers = new HttpHeaders();
+ private MultiValueMap cookies = new LinkedMultiValueMap<>();
+
private Flux body;
@@ -74,8 +79,9 @@ public class MockServerHttpRequest implements ServerHttpRequest {
return this.headers;
}
- public void setHeaders(HttpHeaders headers) {
- this.headers = headers;
+ @Override
+ public MultiValueMap getCookies() {
+ return this.cookies;
}
@Override
diff --git a/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/MockServerHttpResponse.java b/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/MockServerHttpResponse.java
index 4fd1d1e2b2..995846464b 100644
--- a/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/MockServerHttpResponse.java
+++ b/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/MockServerHttpResponse.java
@@ -24,6 +24,9 @@ import reactor.core.publisher.Mono;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
+import org.springframework.http.ServerHttpCookie;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
/**
* @author Rossen Stoyanchev
@@ -34,6 +37,8 @@ public class MockServerHttpResponse implements ServerHttpResponse {
private HttpHeaders headers = new HttpHeaders();
+ private MultiValueMap cookies = new LinkedMultiValueMap<>();
+
private Publisher body;
@@ -51,6 +56,11 @@ public class MockServerHttpResponse implements ServerHttpResponse {
return this.headers;
}
+ @Override
+ public MultiValueMap getCookies() {
+ return this.cookies;
+ }
+
public Publisher getBody() {
return this.body;
}
diff --git a/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/ServerHttpResponseTests.java b/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/ServerHttpResponseTests.java
index af049f0f29..260e18bdb2 100644
--- a/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/ServerHttpResponseTests.java
+++ b/spring-web-reactive/src/test/java/org/springframework/http/server/reactive/ServerHttpResponseTests.java
@@ -27,8 +27,8 @@ import reactor.core.publisher.Mono;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferAllocator;
-import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
+import org.springframework.http.ServerHttpCookie;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
@@ -81,17 +81,17 @@ public class ServerHttpResponseTests {
@Test
public void beforeCommitWithSetBody() throws Exception {
- HttpCookie cookie = HttpCookie.serverCookie("ID", "123").build();
+ ServerHttpCookie cookie = ServerHttpCookie.with("ID", "123").build();
TestServerHttpResponse response = new TestServerHttpResponse();
response.beforeCommit(() -> {
- response.getHeaders().addCookie(cookie);
+ response.getCookies().add(cookie.getName(), cookie);
return Mono.empty();
});
response.setBody(Flux.just(wrap("a"), wrap("b"), wrap("c"))).get();
assertTrue(response.headersWritten);
assertTrue(response.cookiesWritten);
- assertSame(cookie, response.getHeaders().getCookies().get("ID").get(0));
+ assertSame(cookie, response.getCookies().getFirst("ID"));
assertEquals(3, response.content.size());
assertEquals("a", new String(response.content.get(0).asByteBuffer().array(), UTF_8));
@@ -108,7 +108,7 @@ public class ServerHttpResponseTests {
assertTrue("beforeCommit action errors should be ignored", response.headersWritten);
assertTrue("beforeCommit action errors should be ignored", response.cookiesWritten);
- assertNull(response.getHeaders().getCookies().get("ID"));
+ assertNull(response.getCookies().get("ID"));
assertEquals(3, response.content.size());
assertEquals("a", new String(response.content.get(0).asByteBuffer().array(), UTF_8));
@@ -118,10 +118,10 @@ public class ServerHttpResponseTests {
@Test
public void beforeCommitActionWithSetComplete() throws Exception {
- HttpCookie cookie = HttpCookie.serverCookie("ID", "123").build();
+ ServerHttpCookie cookie = ServerHttpCookie.with("ID", "123").build();
TestServerHttpResponse response = new TestServerHttpResponse();
response.beforeCommit(() -> {
- response.getHeaders().addCookie(cookie);
+ response.getCookies().add(cookie.getName(), cookie);
return Mono.empty();
});
response.setComplete().get();
@@ -129,7 +129,7 @@ public class ServerHttpResponseTests {
assertTrue(response.headersWritten);
assertTrue(response.cookiesWritten);
assertTrue(response.content.isEmpty());
- assertSame(cookie, response.getHeaders().getCookies().get("ID").get(0));
+ assertSame(cookie, response.getCookies().getFirst("ID"));
}