From 19cf5035343922111e7c03b60933bf178170f9d4 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 6 Nov 2022 16:08:30 +0100 Subject: [PATCH] Align with Servlet 6.0 and introduce support for Jakarta WebSocket 2.1 Includes corresponding build upgrade to Tomcat 10.1.1 and Undertow 2.3.0 (while retaining runtime compatibility with Tomcat 10.0 and Undertow 2.2) Closes gh-29435 Closes gh-29436 --- .../src/docs/asciidoc/overview.adoc | 7 +- framework-platform/framework-platform.gradle | 27 +++---- spring-test/spring-test.gradle | 3 +- .../springframework/mock/web/MockCookie.java | 1 + .../mock/web/MockHttpServletRequest.java | 49 ++++++++---- .../mock/web/MockHttpServletResponse.java | 24 +----- .../mock/web/MockHttpSession.java | 31 +------- .../web/MockMultipartHttpServletRequest.java | 4 +- .../mock/web/MockServletContext.java | 29 +------ .../mock/web/MockSessionCookieConfig.java | 25 +++++- .../web/socket/MockServerContainer.java | 10 ++- .../htmlunit/MockMvcWebConnection.java | 3 +- .../servlet/result/CookieResultMatchers.java | 6 +- .../servlet/result/PrintingResultHandler.java | 1 + .../mock/web/MockCookieTests.java | 5 +- .../web/MockHttpServletResponseTests.java | 41 +--------- .../mock/web/MockHttpSessionTests.java | 30 +------- .../result/PrintingResultHandlerTests.java | 9 +-- .../reactive/UndertowServerHttpRequest.java | 12 ++- .../reactive/UndertowServerHttpResponse.java | 11 +-- .../util/ContentCachingResponseWrapper.java | 5 +- .../web/testfixture/servlet/MockCookie.java | 1 + .../servlet/MockHttpServletRequest.java | 49 ++++++++---- .../servlet/MockHttpServletResponse.java | 24 +----- .../testfixture/servlet/MockHttpSession.java | 31 +------- .../MockMultipartHttpServletRequest.java | 20 ++--- .../servlet/MockServletContext.java | 29 +------ .../servlet/MockSessionCookieConfig.java | 25 +++++- spring-webflux/spring-webflux.gradle | 3 +- .../function/DefaultServerRequest.java | 18 ----- .../web/servlet/DispatcherServletTests.java | 1 + .../AbstractServletHandlerMethodTests.java | 4 +- spring-websocket/spring-websocket.gradle | 5 +- .../AbstractTyrusRequestUpgradeStrategy.java | 4 +- .../StandardWebSocketUpgradeStrategy.java | 77 +++++++++++++++++++ .../TomcatRequestUpgradeStrategy.java | 1 + .../support/AbstractHandshakeHandler.java | 4 +- 37 files changed, 289 insertions(+), 340 deletions(-) create mode 100644 spring-websocket/src/main/java/org/springframework/web/socket/server/standard/StandardWebSocketUpgradeStrategy.java diff --git a/framework-docs/src/docs/asciidoc/overview.adoc b/framework-docs/src/docs/asciidoc/overview.adoc index fd737bf55b78..33afeb22909b 100644 --- a/framework-docs/src/docs/asciidoc/overview.adoc +++ b/framework-docs/src/docs/asciidoc/overview.adoc @@ -78,9 +78,10 @@ by the Spring Framework. Originally, those were based on common `javax` packages As of Spring Framework 6.0, Spring has been upgraded to the Jakarta EE 9 level (e.g. Servlet 5.0+, JPA 3.0+), based on the `jakarta` namespace instead of the -traditional `javax` packages. With EE 9 as the minimum, Spring is prepared to -provide out-of-the-box support for further API evolution in EE 10+ once available. -This makes Spring Framework 6 fully compatible with e.g. Tomcat 10+ and Jetty 11+. +traditional `javax` packages. With EE 9 as the minimum and EE 10 supported already, +Spring is prepared to provide out-of-the-box support for the further evolution of +the Jakarta EE APIs. Spring Framework 6.0 is fully compatible with Tomcat 10.1, +Jetty 11 and Undertow 2.3 as web servers, and also with Hibernate ORM 6.1. Over time, the role of Java/Jakarta EE in application development has evolved. In the early days of J2EE and Spring, applications were created to be deployed to an application diff --git a/framework-platform/framework-platform.gradle b/framework-platform/framework-platform.gradle index 4f155cd927d3..250c7cee142a 100644 --- a/framework-platform/framework-platform.gradle +++ b/framework-platform/framework-platform.gradle @@ -54,9 +54,9 @@ dependencies { api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE") api("io.reactivex.rxjava3:rxjava:3.1.5") api("io.smallrye.reactive:mutiny:1.7.0") - api("io.undertow:undertow-core:2.2.19.Final") - api("io.undertow:undertow-servlet-jakarta:2.2.19.Final") - api("io.undertow:undertow-websockets-jsr-jakarta:2.2.19.Final") + api("io.undertow:undertow-core:2.3.0.Final") + api("io.undertow:undertow-servlet:2.3.0.Final") + api("io.undertow:undertow-websockets-jsr:2.3.0.Final") api("io.vavr:vavr:0.10.4") api("jakarta.activation:jakarta.activation-api:2.0.1") api("jakarta.annotation:jakarta.annotation-api:2.0.0") @@ -73,12 +73,13 @@ dependencies { api("jakarta.mail:jakarta.mail-api:2.0.1") api("jakarta.persistence:jakarta.persistence-api:3.0.0") api("jakarta.resource:jakarta.resource-api:2.0.0") - api("jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api:2.0.0") - api("jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.0.0") - api("jakarta.servlet:jakarta.servlet-api:5.0.0") - api("jakarta.transaction:jakarta.transaction-api:2.0.0") - api("jakarta.validation:jakarta.validation-api:3.0.0") - api("jakarta.websocket:jakarta.websocket-api:2.0.0") + api("jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api:3.0.0") + api("jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.1.0") + api("jakarta.servlet:jakarta.servlet-api:6.0.0") + api("jakarta.transaction:jakarta.transaction-api:2.0.1") + api("jakarta.validation:jakarta.validation-api:3.0.2") + api("jakarta.websocket:jakarta.websocket-api:2.1.0") + api("jakarta.websocket:jakarta.websocket-client-api:2.1.0") api("jakarta.xml.bind:jakarta.xml.bind-api:3.0.1") api("javax.cache:cache-api:1.1.1") api("javax.money:money-api:1.1") @@ -97,10 +98,10 @@ dependencies { api("org.apache.httpcomponents.client5:httpclient5:5.1.3") api("org.apache.httpcomponents.core5:httpcore5-reactive:5.1.3") api("org.apache.poi:poi-ooxml:5.2.2") - api("org.apache.tomcat.embed:tomcat-embed-core:10.1.0") - api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.0") - api("org.apache.tomcat:tomcat-util:10.0.22") - api("org.apache.tomcat:tomcat-websocket:10.0.22") + api("org.apache.tomcat.embed:tomcat-embed-core:10.1.1") + api("org.apache.tomcat.embed:tomcat-embed-websocket:10.1.1") + api("org.apache.tomcat:tomcat-util:10.1.1") + api("org.apache.tomcat:tomcat-websocket:10.1.1") api("org.aspectj:aspectjrt:1.9.9.1") api("org.aspectj:aspectjtools:1.9.9.1") api("org.aspectj:aspectjweaver:1.9.9.1") diff --git a/spring-test/spring-test.gradle b/spring-test/spring-test.gradle index 320820dd8b62..70263f972a6c 100644 --- a/spring-test/spring-test.gradle +++ b/spring-test/spring-test.gradle @@ -23,9 +23,10 @@ dependencies { optional("jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api") optional("jakarta.xml.bind:jakarta.xml.bind-api") optional("jakarta.websocket:jakarta.websocket-api") + optional("jakarta.websocket:jakarta.websocket-client-api") optional("junit:junit") optional("org.apache.tomcat.embed:tomcat-embed-core") - optional("org.junit.platform:junit-platform-launcher") // for AOT processing + optional("org.junit.platform:junit-platform-launcher") // for AOT processing optional("org.junit.jupiter:junit-jupiter-api") optional("org.testng:testng") optional("org.aspectj:aspectjweaver") 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 3dc81d56cd99..ca28a9aa9763 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 @@ -36,6 +36,7 @@ * @author Sam Brannen * @since 5.1 */ +@SuppressWarnings("removal") public class MockCookie extends Cookie { private static final long serialVersionUID = 4312531139502726325L; diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java index ec6339e84186..ad730966f0d2 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java @@ -46,6 +46,7 @@ import jakarta.servlet.AsyncContext; import jakarta.servlet.DispatcherType; import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletConnection; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.ServletInputStream; @@ -79,7 +80,7 @@ * is {@link Locale#ENGLISH}. This value can be changed via {@link #addPreferredLocale} * or {@link #setPreferredLocales}. * - *

As of Spring Framework 5.0, this set of mocks is designed on a Servlet 4.0 baseline. + *

As of Spring 6.0, this set of mocks is designed on a Servlet 6.0 baseline. * * @author Juergen Hoeller * @author Rod Johnson @@ -878,12 +879,6 @@ public RequestDispatcher getRequestDispatcher(String path) { return new MockRequestDispatcher(path); } - @Override - @Deprecated - public String getRealPath(String path) { - return this.servletContext.getRealPath(path); - } - public void setRemotePort(int remotePort) { this.remotePort = remotePort; } @@ -970,6 +965,38 @@ public DispatcherType getDispatcherType() { return this.dispatcherType; } + @Override + public String getRequestId() { + return ""; + } + + @Override + public String getProtocolRequestId() { + return ""; + } + + @Override + public ServletConnection getServletConnection() { + return new ServletConnection() { + @Override + public String getConnectionId() { + return MockHttpServletRequest.this.getRequestId(); + } + @Override + public String getProtocol() { + return MockHttpServletRequest.this.getProtocol(); + } + @Override + public String getProtocolConnectionId() { + return MockHttpServletRequest.this.getProtocolRequestId(); + } + @Override + public boolean isSecure() { + return MockHttpServletRequest.this.isSecure(); + } + }; + } + // --------------------------------------------------------------------- // HttpServletRequest interface @@ -1183,7 +1210,7 @@ public String getPathInfo() { @Override @Nullable public String getPathTranslated() { - return (this.pathInfo != null ? getRealPath(this.pathInfo) : null); + return (this.pathInfo != null ? this.servletContext.getRealPath(this.pathInfo) : null); } public void setContextPath(String contextPath) { @@ -1352,12 +1379,6 @@ public boolean isRequestedSessionIdFromURL() { return this.requestedSessionIdFromURL; } - @Override - @Deprecated - public boolean isRequestedSessionIdFromUrl() { - return isRequestedSessionIdFromURL(); - } - @Override public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { throw new UnsupportedOperationException(); diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java index 1203fe845084..88af4672bfb1 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletResponse.java @@ -53,7 +53,7 @@ /** * Mock implementation of the {@link jakarta.servlet.http.HttpServletResponse} interface. * - *

As of Spring Framework 5.0, this set of mocks is designed on a Servlet 4.0 baseline. + *

As of Spring 6.0, this set of mocks is designed on a Servlet 6.0 baseline. * * @author Juergen Hoeller * @author Rod Johnson @@ -413,6 +413,7 @@ public void addCookie(Cookie cookie) { doAddHeaderValue(HttpHeaders.SET_COOKIE, getCookieHeader(cookie), false); } + @SuppressWarnings("removal") private String getCookieHeader(Cookie cookie) { StringBuilder buf = new StringBuilder(); buf.append(cookie.getName()).append('=').append(cookie.getValue() == null ? "" : cookie.getValue()); @@ -572,18 +573,6 @@ public String encodeRedirectURL(String url) { return encodeURL(url); } - @Override - @Deprecated - public String encodeUrl(String url) { - return encodeURL(url); - } - - @Override - @Deprecated - public String encodeRedirectUrl(String url) { - return encodeRedirectURL(url); - } - @Override public void sendError(int status, String errorMessage) throws IOException { Assert.state(!isCommitted(), "Cannot set error status - response is already committed"); @@ -758,15 +747,6 @@ public void setStatus(int status) { } } - @Override - @Deprecated - public void setStatus(int status, String errorMessage) { - if (!this.isCommitted()) { - this.status = status; - this.errorMessage = errorMessage; - } - } - @Override public int getStatus() { return this.status; diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpSession.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpSession.java index 244880d8d798..e8d1d890e001 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpSession.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2022 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. @@ -32,12 +32,11 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.StringUtils; /** * Mock implementation of the {@link jakarta.servlet.http.HttpSession} interface. * - *

As of Spring 5.0, this set of mocks is designed on a Servlet 4.0 baseline. + *

As of Spring 6.0, this set of mocks is designed on a Servlet 6.0 baseline. * * @author Juergen Hoeller * @author Rod Johnson @@ -148,11 +147,6 @@ public int getMaxInactiveInterval() { return this.maxInactiveInterval; } - @Override - public jakarta.servlet.http.HttpSessionContext getSessionContext() { - throw new UnsupportedOperationException("getSessionContext"); - } - @Override public Object getAttribute(String name) { assertIsValid(); @@ -160,23 +154,12 @@ public Object getAttribute(String name) { return this.attributes.get(name); } - @Override - public Object getValue(String name) { - return getAttribute(name); - } - @Override public Enumeration getAttributeNames() { assertIsValid(); return Collections.enumeration(new LinkedHashSet<>(this.attributes.keySet())); } - @Override - public String[] getValueNames() { - assertIsValid(); - return StringUtils.toStringArray(this.attributes.keySet()); - } - @Override public void setAttribute(String name, @Nullable Object value) { assertIsValid(); @@ -197,11 +180,6 @@ public void setAttribute(String name, @Nullable Object value) { } } - @Override - public void putValue(String name, Object value) { - setAttribute(name, value); - } - @Override public void removeAttribute(String name) { assertIsValid(); @@ -212,11 +190,6 @@ public void removeAttribute(String name) { } } - @Override - public void removeValue(String name) { - removeAttribute(name); - } - /** * Clear all of this session's attributes. */ diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java b/spring-test/src/main/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java index 9357c9b58480..aaf0299dd788 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -43,7 +43,7 @@ * Mock implementation of the * {@link org.springframework.web.multipart.MultipartHttpServletRequest} interface. * - *

As of Spring 5.0, this set of mocks is designed on a Servlet 4.0 baseline. + *

As of Spring 6.0, this set of mocks is designed on a Servlet 6.0 baseline. * *

Useful for testing application controllers that access multipart uploads. * {@link MockMultipartFile} can be used to populate these mock requests with files. diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java b/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java index 736d44168ef1..42f2e7f3e9b8 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockServletContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -60,7 +60,7 @@ /** * Mock implementation of the {@link jakarta.servlet.ServletContext} interface. * - *

As of Spring 5.0, this set of mocks is designed on a Servlet 4.0 baseline. + *

As of Spring 6.0, this set of mocks is designed on a Servlet 6.0 baseline. * *

Compatible with Servlet 3.1 but can be configured to expose a specific version * through {@link #setMajorVersion}/{@link #setMinorVersion}; default is 3.1. @@ -430,36 +430,11 @@ public void setDefaultServletName(String defaultServletName) { registerNamedDispatcher(this.defaultServletName, new MockRequestDispatcher(this.defaultServletName)); } - @Deprecated - @Override - @Nullable - public Servlet getServlet(String name) { - return null; - } - - @Override - @Deprecated - public Enumeration getServlets() { - return Collections.enumeration(Collections.emptySet()); - } - - @Override - @Deprecated - public Enumeration getServletNames() { - return Collections.enumeration(Collections.emptySet()); - } - @Override public void log(String message) { logger.info(message); } - @Override - @Deprecated - public void log(Exception ex, String message) { - logger.info(message, ex); - } - @Override public void log(String message, Throwable ex) { logger.info(message, ex); diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockSessionCookieConfig.java b/spring-test/src/main/java/org/springframework/mock/web/MockSessionCookieConfig.java index 3a98a87a6027..66b98a3fe347 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockSessionCookieConfig.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockSessionCookieConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2022 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. @@ -16,6 +16,10 @@ package org.springframework.mock.web; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + import jakarta.servlet.SessionCookieConfig; import org.springframework.lang.Nullable; @@ -47,6 +51,8 @@ public class MockSessionCookieConfig implements SessionCookieConfig { private int maxAge = -1; + private Map attributes = new LinkedHashMap<>(); + @Override public void setName(@Nullable String name) { @@ -81,11 +87,13 @@ public String getPath() { return this.path; } + @SuppressWarnings("removal") @Override public void setComment(@Nullable String comment) { this.comment = comment; } + @SuppressWarnings("removal") @Override @Nullable public String getComment() { @@ -122,4 +130,19 @@ public int getMaxAge() { return this.maxAge; } + @Override + public void setAttribute(String name, String value) { + this.attributes.put(name, value); + } + + @Override + public String getAttribute(String name) { + return this.attributes.get(name); + } + + @Override + public Map getAttributes() { + return Collections.unmodifiableMap(this.attributes); + } + } diff --git a/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainer.java b/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainer.java index cc86220b62d5..e87e453183e4 100644 --- a/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainer.java +++ b/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2022 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. @@ -19,6 +19,7 @@ import java.io.IOException; import java.net.URI; import java.util.Collections; +import java.util.Map; import java.util.Set; import jakarta.websocket.ClientEndpointConfig; @@ -133,4 +134,11 @@ public void addEndpoint(ServerEndpointConfig serverConfig) throws DeploymentExce "MockServerContainer does not support addEndpoint(ServerEndpointConfig)"); } + @Override + public void upgradeHttpToWebSocket(Object httpServletRequest, Object httpServletResponse, + ServerEndpointConfig sec, Map pathParameters) throws IOException, DeploymentException { + + throw new UnsupportedOperationException("MockServerContainer does not support upgradeHttpToWebSocket"); + } + } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java index 8b3608772a0d..826d91a3db98 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 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. @@ -173,6 +173,7 @@ private void storeCookies(WebRequest webRequest, jakarta.servlet.http.Cookie[] c } } + @SuppressWarnings("removal") private static com.gargoylesoftware.htmlunit.util.Cookie createCookie(jakarta.servlet.http.Cookie cookie) { Date expires = null; if (cookie.getMaxAge() > -1) { diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/result/CookieResultMatchers.java b/spring-test/src/main/java/org/springframework/test/web/servlet/result/CookieResultMatchers.java index a8a40a29c448..46c8533477e9 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/result/CookieResultMatchers.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/result/CookieResultMatchers.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2022 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. @@ -149,6 +149,7 @@ public ResultMatcher domain(String name, String domain) { /** * Assert a cookie's comment with a Hamcrest {@link Matcher}. */ + @SuppressWarnings("removal") public ResultMatcher comment(String name, Matcher matcher) { return result -> { Cookie cookie = getCookie(result, name); @@ -159,6 +160,7 @@ public ResultMatcher comment(String name, Matcher matcher) { /** * Assert a cookie's comment. */ + @SuppressWarnings("removal") public ResultMatcher comment(String name, String comment) { return result -> { Cookie cookie = getCookie(result, name); @@ -169,6 +171,7 @@ public ResultMatcher comment(String name, String comment) { /** * Assert a cookie's version with a Hamcrest {@link Matcher}. */ + @SuppressWarnings("removal") public ResultMatcher version(String name, Matcher matcher) { return result -> { Cookie cookie = getCookie(result, name); @@ -179,6 +182,7 @@ public ResultMatcher version(String name, Matcher matcher) { /** * Assert a cookie's version. */ + @SuppressWarnings("removal") public ResultMatcher version(String name, int version) { return result -> { Cookie cookie = getCookie(result, name); diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/result/PrintingResultHandler.java b/spring-test/src/main/java/org/springframework/test/web/servlet/result/PrintingResultHandler.java index fc754a0669b2..bccce3305613 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/result/PrintingResultHandler.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/result/PrintingResultHandler.java @@ -263,6 +263,7 @@ protected void printResponse(MockHttpServletResponse response) throws Exception * {@link Cookie} implementation does not provide its own {@code toString()}. * @since 4.2 */ + @SuppressWarnings("removal") private void printCookies(Cookie[] cookies) { String[] cookieStrings = new String[cookies.length]; for (int i = 0; i < cookies.length; i++) { 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 9cb2247f8eb0..9c14a4f4bf32 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 @@ -65,9 +65,10 @@ void parseHeaderWithoutAttributes() { assertCookie(cookie, "SESSION", "123"); } + @SuppressWarnings("removal") @Test void parseHeaderWithAttributes() { - MockCookie cookie = MockCookie.parse("SESSION=123; Comment=Session Cookie; Domain=example.com; Max-Age=60; " + + MockCookie cookie = MockCookie.parse("SESSION=123; Domain=example.com; Max-Age=60; " + "Expires=Tue, 8 Oct 2019 19:50:00 GMT; Path=/; Secure; HttpOnly; SameSite=Lax"); assertCookie(cookie, "SESSION", "123"); @@ -79,7 +80,7 @@ void parseHeaderWithAttributes() { assertThat(cookie.getExpires()).isEqualTo(ZonedDateTime.parse("Tue, 8 Oct 2019 19:50:00 GMT", DateTimeFormatter.RFC_1123_DATE_TIME)); assertThat(cookie.getSameSite()).isEqualTo("Lax"); - assertThat(cookie.getComment()).isEqualTo("Session Cookie"); + assertThat(cookie.getComment()).isNull(); } @ParameterizedTest diff --git a/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletResponseTests.java b/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletResponseTests.java index 058d1ae51217..316c30132f2d 100644 --- a/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletResponseTests.java +++ b/spring-test/src/test/java/org/springframework/mock/web/MockHttpServletResponseTests.java @@ -421,10 +421,9 @@ void modifyStatusAfterSendError() throws IOException { } @Test // SPR-10414 - @SuppressWarnings("deprecation") void modifyStatusMessageAfterSendError() throws IOException { response.sendError(HttpServletResponse.SC_NOT_FOUND); - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Server Error"); + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_NOT_FOUND); } @@ -474,23 +473,6 @@ void setCookieHeaderWithZeroExpiresAttribute() { assertThat(header).startsWith("SESSION=123; Path=/; Max-Age=100; Expires="); } - /** - * @since 5.3.22 - */ - @Test - void setCookieHeaderWithComment() { - response.setHeader(SET_COOKIE, "SESSION=123;Comment=Test Comment;Path=/"); - - assertThat(response.getHeader(SET_COOKIE)).isEqualTo(("SESSION=123; Path=/; Comment=Test Comment")); - - assertNumCookies(1); - assertThat(response.getCookies()[0]).isInstanceOf(MockCookie.class).satisfies(mockCookie -> { - assertThat(mockCookie.getName()).isEqualTo("SESSION"); - assertThat(mockCookie.getPath()).isEqualTo("/"); - assertThat(mockCookie.getComment()).isEqualTo("Test Comment"); - }); - } - @Test void addCookieHeader() { response.addHeader(SET_COOKIE, "SESSION=123; Path=/; Secure; HttpOnly; SameSite=Lax"); @@ -504,26 +486,6 @@ void addCookieHeader() { assertCookieValues("123", "999"); } - @Test - void addCookieHeaderWithComment() { - response.addHeader(SET_COOKIE, "SESSION=123; Path=/; Secure; HttpOnly; SameSite=Lax"); - assertNumCookies(1); - assertPrimarySessionCookie("123"); - - // Adding a 2nd cookie header should result in 2 cookies. - response.addHeader(SET_COOKIE, "SESSION=999; Comment=Test Comment; Path=/; Secure; HttpOnly; SameSite=Lax"); - assertNumCookies(2); - assertPrimarySessionCookie("123"); - assertThat(response.getCookies()[1]).isInstanceOf(MockCookie.class).satisfies(mockCookie -> { - assertThat(mockCookie.getName()).isEqualTo("SESSION"); - assertThat(mockCookie.getValue()).isEqualTo("999"); - assertThat(mockCookie.getComment()).isEqualTo("Test Comment"); - assertThat(mockCookie.getPath()).isEqualTo("/"); - assertThat(mockCookie.getSecure()).isTrue(); - assertThat(mockCookie.isHttpOnly()).isTrue(); - }); - } - /** * @since 5.1.11 */ @@ -605,6 +567,7 @@ private void assertCookieValues(String... expected) { assertThat(response.getCookies()).extracting(Cookie::getValue).containsExactly(expected); } + @SuppressWarnings("removal") private void assertPrimarySessionCookie(String expectedValue) { Cookie cookie = this.response.getCookie("SESSION"); assertThat(cookie).asInstanceOf(type(MockCookie.class)).satisfies(mockCookie -> { diff --git a/spring-test/src/test/java/org/springframework/mock/web/MockHttpSessionTests.java b/spring-test/src/test/java/org/springframework/mock/web/MockHttpSessionTests.java index 602290d5597a..f3f5ba9218bb 100644 --- a/spring-test/src/test/java/org/springframework/mock/web/MockHttpSessionTests.java +++ b/spring-test/src/test/java/org/springframework/mock/web/MockHttpSessionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2022 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. @@ -79,20 +79,6 @@ void getAttributeNamesOnInvalidatedSession() { session::getAttributeNames); } - @Test - void getValueOnInvalidatedSession() { - session.invalidate(); - assertThatIllegalStateException().isThrownBy(() -> - session.getValue("foo")); - } - - @Test - void getValueNamesOnInvalidatedSession() { - session.invalidate(); - assertThatIllegalStateException().isThrownBy( - session::getValueNames); - } - @Test void setAttributeOnInvalidatedSession() { session.invalidate(); @@ -100,13 +86,6 @@ void setAttributeOnInvalidatedSession() { session.setAttribute("name", "value")); } - @Test - void putValueOnInvalidatedSession() { - session.invalidate(); - assertThatIllegalStateException().isThrownBy(() -> - session.putValue("name", "value")); - } - @Test void removeAttributeOnInvalidatedSession() { session.invalidate(); @@ -114,13 +93,6 @@ void removeAttributeOnInvalidatedSession() { session.removeAttribute("name")); } - @Test - void removeValueOnInvalidatedSession() { - session.invalidate(); - assertThatIllegalStateException().isThrownBy(() -> - session.removeValue("name")); - } - @Test void isNewOnInvalidatedSession() { session.invalidate(); diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/result/PrintingResultHandlerTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/result/PrintingResultHandlerTests.java index e7ff36a4cf15..7860292b6a3e 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/result/PrintingResultHandlerTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/result/PrintingResultHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -144,17 +144,16 @@ public void printRequestWithEmptySessionMock() throws Exception { } @Test - @SuppressWarnings("deprecation") + @SuppressWarnings("removal") public void printResponse() throws Exception { Cookie enigmaCookie = new Cookie("enigma", "42"); - enigmaCookie.setComment("This is a comment"); enigmaCookie.setHttpOnly(true); enigmaCookie.setMaxAge(1234); enigmaCookie.setDomain(".example.com"); enigmaCookie.setPath("/crumbs"); enigmaCookie.setSecure(true); - this.response.setStatus(400, "error"); + this.response.setStatus(400); this.response.addHeader("header", "headerValue"); this.response.setContentType("text/plain"); this.response.getWriter().print("content"); @@ -197,7 +196,7 @@ public void printResponse() throws Exception { assertThat(cookie1.endsWith("]")).isTrue(); assertThat(cookie2.startsWith("[" + Cookie.class.getSimpleName())).isTrue(); assertThat(cookie2.contains("name = 'enigma', value = '42', " + - "comment = 'This is a comment', domain = '.example.com', maxAge = 1234, " + + "comment = [null], domain = '.example.com', maxAge = 1234, " + "path = '/crumbs', secure = true, version = 0, httpOnly = true")).isTrue(); assertThat(cookie2.endsWith("]")).isTrue(); } diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java index 41e9693e04dd..5fae16cfa8f0 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -48,6 +48,7 @@ * * @author Marek Hawrylczak * @author Rossen Stoyanchev + * @author Juergen Hoeller * @since 5.0 */ class UndertowServerHttpRequest extends AbstractServerHttpRequest { @@ -82,15 +83,12 @@ public HttpMethod getMethod() { return HttpMethod.valueOf(this.exchange.getRequestMethod().toString()); } - @SuppressWarnings("deprecation") @Override protected MultiValueMap initCookies() { MultiValueMap cookies = new LinkedMultiValueMap<>(); - // getRequestCookies() is deprecated in Undertow 2.2 - for (String name : this.exchange.getRequestCookies().keySet()) { - Cookie cookie = this.exchange.getRequestCookies().get(name); - HttpCookie httpCookie = new HttpCookie(name, cookie.getValue()); - cookies.add(name, httpCookie); + for (Cookie cookie : this.exchange.requestCookies()) { + HttpCookie httpCookie = new HttpCookie(cookie.getName(), cookie.getValue()); + cookies.add(cookie.getName(), httpCookie); } return cookies; } diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpResponse.java b/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpResponse.java index 70a5b9c3211a..6a195245c90a 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpResponse.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/UndertowServerHttpResponse.java @@ -47,6 +47,7 @@ * @author Marek Hawrylczak * @author Rossen Stoyanchev * @author Arjen Poutsma + * @author Juergen Hoeller * @since 5.0 */ class UndertowServerHttpResponse extends AbstractListenerServerHttpResponse implements ZeroCopyHttpOutputMessage { @@ -105,7 +106,6 @@ protected void applyStatusCode() { protected void applyHeaders() { } - @SuppressWarnings("deprecation") @Override protected void applyCookies() { for (String name : getCookies().keySet()) { @@ -123,8 +123,7 @@ protected void applyCookies() { cookie.setSecure(httpCookie.isSecure()); cookie.setHttpOnly(httpCookie.isHttpOnly()); cookie.setSameSiteMode(httpCookie.getSameSite()); - // getResponseCookies() is deprecated in Undertow 2.2 - this.exchange.getResponseCookies().putIfAbsent(name, cookie); + this.exchange.setResponseCookie(cookie); } } } @@ -135,14 +134,10 @@ public Mono writeWith(Path file, long position, long count) { Mono.create(sink -> { try { FileChannel source = FileChannel.open(file, StandardOpenOption.READ); - - TransferBodyListener listener = new TransferBodyListener(source, position, - count, sink); + TransferBodyListener listener = new TransferBodyListener(source, position, count, sink); sink.onDispose(listener::closeSource); - StreamSinkChannel destination = this.exchange.getResponseChannel(); destination.getWriteSetter().set(listener::transfer); - listener.transfer(destination); } catch (IOException ex) { diff --git a/spring-web/src/main/java/org/springframework/web/util/ContentCachingResponseWrapper.java b/spring-web/src/main/java/org/springframework/web/util/ContentCachingResponseWrapper.java index ab13b1c88fb1..dd9a46cab6fc 100644 --- a/spring-web/src/main/java/org/springframework/web/util/ContentCachingResponseWrapper.java +++ b/spring-web/src/main/java/org/springframework/web/util/ContentCachingResponseWrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -78,7 +78,6 @@ public void sendError(int sc) throws IOException { } @Override - @SuppressWarnings("deprecation") public void sendError(int sc, String msg) throws IOException { copyBodyToResponse(false); try { @@ -86,7 +85,7 @@ public void sendError(int sc, String msg) throws IOException { } catch (IllegalStateException ex) { // Possibly on Tomcat when called too late: fall back to silent setStatus - super.setStatus(sc, msg); + super.setStatus(sc); } } 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 b9736f41559e..23a74b54940f 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 @@ -36,6 +36,7 @@ * @author Sam Brannen * @since 5.1 */ +@SuppressWarnings("removal") public class MockCookie extends Cookie { private static final long serialVersionUID = 4312531139502726325L; diff --git a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpServletRequest.java b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpServletRequest.java index 6ccbf589fef0..44174534e426 100644 --- a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpServletRequest.java +++ b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpServletRequest.java @@ -46,6 +46,7 @@ import jakarta.servlet.AsyncContext; import jakarta.servlet.DispatcherType; import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletConnection; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.ServletInputStream; @@ -79,7 +80,7 @@ * is {@link Locale#ENGLISH}. This value can be changed via {@link #addPreferredLocale} * or {@link #setPreferredLocales}. * - *

As of Spring Framework 5.0, this set of mocks is designed on a Servlet 4.0 baseline. + *

As of Spring 6.0, this set of mocks is designed on a Servlet 6.0 baseline. * * @author Juergen Hoeller * @author Rod Johnson @@ -878,12 +879,6 @@ public RequestDispatcher getRequestDispatcher(String path) { return new MockRequestDispatcher(path); } - @Override - @Deprecated - public String getRealPath(String path) { - return this.servletContext.getRealPath(path); - } - public void setRemotePort(int remotePort) { this.remotePort = remotePort; } @@ -970,6 +965,38 @@ public DispatcherType getDispatcherType() { return this.dispatcherType; } + @Override + public String getRequestId() { + return ""; + } + + @Override + public String getProtocolRequestId() { + return ""; + } + + @Override + public ServletConnection getServletConnection() { + return new ServletConnection() { + @Override + public String getConnectionId() { + return MockHttpServletRequest.this.getRequestId(); + } + @Override + public String getProtocol() { + return MockHttpServletRequest.this.getProtocol(); + } + @Override + public String getProtocolConnectionId() { + return MockHttpServletRequest.this.getProtocolRequestId(); + } + @Override + public boolean isSecure() { + return MockHttpServletRequest.this.isSecure(); + } + }; + } + // --------------------------------------------------------------------- // HttpServletRequest interface @@ -1183,7 +1210,7 @@ public String getPathInfo() { @Override @Nullable public String getPathTranslated() { - return (this.pathInfo != null ? getRealPath(this.pathInfo) : null); + return (this.pathInfo != null ? this.servletContext.getRealPath(this.pathInfo) : null); } public void setContextPath(String contextPath) { @@ -1352,12 +1379,6 @@ public boolean isRequestedSessionIdFromURL() { return this.requestedSessionIdFromURL; } - @Override - @Deprecated - public boolean isRequestedSessionIdFromUrl() { - return isRequestedSessionIdFromURL(); - } - @Override public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { throw new UnsupportedOperationException(); diff --git a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpServletResponse.java b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpServletResponse.java index 762bd7a79587..52aab6a61dca 100644 --- a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpServletResponse.java +++ b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpServletResponse.java @@ -53,7 +53,7 @@ /** * Mock implementation of the {@link jakarta.servlet.http.HttpServletResponse} interface. * - *

As of Spring Framework 5.0, this set of mocks is designed on a Servlet 4.0 baseline. + *

As of Spring 6.0, this set of mocks is designed on a Servlet 6.0 baseline. * * @author Juergen Hoeller * @author Rod Johnson @@ -413,6 +413,7 @@ public void addCookie(Cookie cookie) { doAddHeaderValue(HttpHeaders.SET_COOKIE, getCookieHeader(cookie), false); } + @SuppressWarnings("removal") private String getCookieHeader(Cookie cookie) { StringBuilder buf = new StringBuilder(); buf.append(cookie.getName()).append('=').append(cookie.getValue() == null ? "" : cookie.getValue()); @@ -572,18 +573,6 @@ public String encodeRedirectURL(String url) { return encodeURL(url); } - @Override - @Deprecated - public String encodeUrl(String url) { - return encodeURL(url); - } - - @Override - @Deprecated - public String encodeRedirectUrl(String url) { - return encodeRedirectURL(url); - } - @Override public void sendError(int status, String errorMessage) throws IOException { Assert.state(!isCommitted(), "Cannot set error status - response is already committed"); @@ -758,15 +747,6 @@ public void setStatus(int status) { } } - @Override - @Deprecated - public void setStatus(int status, String errorMessage) { - if (!this.isCommitted()) { - this.status = status; - this.errorMessage = errorMessage; - } - } - @Override public int getStatus() { return this.status; diff --git a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpSession.java b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpSession.java index a33c7c9a2236..99742394801b 100644 --- a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpSession.java +++ b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2022 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. @@ -32,12 +32,11 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.StringUtils; /** * Mock implementation of the {@link jakarta.servlet.http.HttpSession} interface. * - *

As of Spring 5.0, this set of mocks is designed on a Servlet 4.0 baseline. + *

As of Spring 6.0, this set of mocks is designed on a Servlet 6.0 baseline. * * @author Juergen Hoeller * @author Rod Johnson @@ -148,11 +147,6 @@ public int getMaxInactiveInterval() { return this.maxInactiveInterval; } - @Override - public jakarta.servlet.http.HttpSessionContext getSessionContext() { - throw new UnsupportedOperationException("getSessionContext"); - } - @Override public Object getAttribute(String name) { assertIsValid(); @@ -160,23 +154,12 @@ public Object getAttribute(String name) { return this.attributes.get(name); } - @Override - public Object getValue(String name) { - return getAttribute(name); - } - @Override public Enumeration getAttributeNames() { assertIsValid(); return Collections.enumeration(new LinkedHashSet<>(this.attributes.keySet())); } - @Override - public String[] getValueNames() { - assertIsValid(); - return StringUtils.toStringArray(this.attributes.keySet()); - } - @Override public void setAttribute(String name, @Nullable Object value) { assertIsValid(); @@ -197,11 +180,6 @@ public void setAttribute(String name, @Nullable Object value) { } } - @Override - public void putValue(String name, Object value) { - setAttribute(name, value); - } - @Override public void removeAttribute(String name) { assertIsValid(); @@ -212,11 +190,6 @@ public void removeAttribute(String name) { } } - @Override - public void removeValue(String name) { - removeAttribute(name); - } - /** * Clear all of this session's attributes. */ diff --git a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockMultipartHttpServletRequest.java b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockMultipartHttpServletRequest.java index 10a4443fc48f..24d2b464b598 100644 --- a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockMultipartHttpServletRequest.java +++ b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockMultipartHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -23,6 +23,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; @@ -42,7 +43,7 @@ * Mock implementation of the * {@link org.springframework.web.multipart.MultipartHttpServletRequest} interface. * - *

As of Spring 5.0, this set of mocks is designed on a Servlet 4.0 baseline. + *

As of Spring 6.0, this set of mocks is designed on a Servlet 6.0 baseline. * *

Useful for testing application controllers that access multipart uploads. * {@link MockMultipartFile} can be used to populate these mock requests with files. @@ -102,12 +103,7 @@ public MultipartFile getFile(String name) { @Override public List getFiles(String name) { List multipartFiles = this.multipartFiles.get(name); - if (multipartFiles != null) { - return multipartFiles; - } - else { - return Collections.emptyList(); - } + return Objects.requireNonNullElse(multipartFiles, Collections.emptyList()); } @Override @@ -142,12 +138,8 @@ public String getMultipartContentType(String paramOrFileName) { @Override public HttpMethod getRequestMethod() { String method = getMethod(); - if (method != null) { - return HttpMethod.valueOf(method); - } - else { - return null; - } + Assert.state(method != null, "Method must not be null"); + return HttpMethod.valueOf(method); } @Override diff --git a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockServletContext.java b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockServletContext.java index b44a3b13283f..78f18933064b 100644 --- a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockServletContext.java +++ b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockServletContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -60,7 +60,7 @@ /** * Mock implementation of the {@link jakarta.servlet.ServletContext} interface. * - *

As of Spring 5.0, this set of mocks is designed on a Servlet 4.0 baseline. + *

As of Spring 6.0, this set of mocks is designed on a Servlet 6.0 baseline. * *

Compatible with Servlet 3.1 but can be configured to expose a specific version * through {@link #setMajorVersion}/{@link #setMinorVersion}; default is 3.1. @@ -430,36 +430,11 @@ public void setDefaultServletName(String defaultServletName) { registerNamedDispatcher(this.defaultServletName, new MockRequestDispatcher(this.defaultServletName)); } - @Deprecated - @Override - @Nullable - public Servlet getServlet(String name) { - return null; - } - - @Override - @Deprecated - public Enumeration getServlets() { - return Collections.enumeration(Collections.emptySet()); - } - - @Override - @Deprecated - public Enumeration getServletNames() { - return Collections.enumeration(Collections.emptySet()); - } - @Override public void log(String message) { logger.info(message); } - @Override - @Deprecated - public void log(Exception ex, String message) { - logger.info(message, ex); - } - @Override public void log(String message, Throwable ex) { logger.info(message, ex); diff --git a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockSessionCookieConfig.java b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockSessionCookieConfig.java index 04f4a8ceabc4..07a76626b25c 100644 --- a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockSessionCookieConfig.java +++ b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockSessionCookieConfig.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2022 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. @@ -16,6 +16,10 @@ package org.springframework.web.testfixture.servlet; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + import jakarta.servlet.SessionCookieConfig; import org.springframework.lang.Nullable; @@ -47,6 +51,8 @@ public class MockSessionCookieConfig implements SessionCookieConfig { private int maxAge = -1; + private Map attributes = new LinkedHashMap<>(); + @Override public void setName(@Nullable String name) { @@ -81,11 +87,13 @@ public String getPath() { return this.path; } + @SuppressWarnings("removal") @Override public void setComment(@Nullable String comment) { this.comment = comment; } + @SuppressWarnings("removal") @Override @Nullable public String getComment() { @@ -122,4 +130,19 @@ public int getMaxAge() { return this.maxAge; } + @Override + public void setAttribute(String name, String value) { + this.attributes.put(name, value); + } + + @Override + public String getAttribute(String name) { + return this.attributes.get(name); + } + + @Override + public Map getAttributes() { + return Collections.unmodifiableMap(this.attributes); + } + } diff --git a/spring-webflux/spring-webflux.gradle b/spring-webflux/spring-webflux.gradle index a8a814863fb3..b7cc4daf2d7c 100644 --- a/spring-webflux/spring-webflux.gradle +++ b/spring-webflux/spring-webflux.gradle @@ -11,6 +11,7 @@ dependencies { optional(project(":spring-context-support")) // for FreeMarker support optional("jakarta.servlet:jakarta.servlet-api") optional("jakarta.websocket:jakarta.websocket-api") + optional("jakarta.websocket:jakarta.websocket-client-api") optional("org.webjars:webjars-locator-core") optional("org.freemarker:freemarker") optional("com.fasterxml.jackson.core:jackson-databind") @@ -25,7 +26,7 @@ dependencies { exclude group: "jakarta.servlet", module: "jakarta.servlet-api" } optional("org.eclipse.jetty.websocket:websocket-jetty-client") - optional("io.undertow:undertow-websockets-jsr-jakarta") + optional("io.undertow:undertow-websockets-jsr") optional("org.jetbrains.kotlin:kotlin-reflect") optional("org.jetbrains.kotlin:kotlin-stdlib") optional("com.google.protobuf:protobuf-java-util") diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java index 12c764c69081..96e0a94e716f 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java @@ -507,12 +507,6 @@ public void setStatus(int sc) { this.status = sc; } - @Override - @Deprecated - public void setStatus(int sc, String sm) { - this.status = sc; - } - @Override public int getStatus() { return this.status; @@ -553,18 +547,6 @@ public String encodeRedirectURL(String url) { throw new UnsupportedOperationException(); } - @Override - @Deprecated - public String encodeUrl(String url) { - throw new UnsupportedOperationException(); - } - - @Override - @Deprecated - public String encodeRedirectUrl(String url) { - throw new UnsupportedOperationException(); - } - @Override public void sendError(int sc, String msg) throws IOException { throw new UnsupportedOperationException(); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/DispatcherServletTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/DispatcherServletTests.java index ad6465cf7afc..595046a04746 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/DispatcherServletTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/DispatcherServletTests.java @@ -98,6 +98,7 @@ public void setup() throws ServletException { complexConfig.addInitParameter("publishContext", "false"); complexConfig.addInitParameter("class", "notWritable"); complexConfig.addInitParameter("unknownParam", "someValue"); + complexConfig.addInitParameter("jakarta.servlet.http.legacyDoHead", "true"); simpleDispatcherServlet = new DispatcherServlet(); simpleDispatcherServlet.setContextClass(SimpleWebApplicationContext.class); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/AbstractServletHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/AbstractServletHandlerMethodTests.java index 4c260f3a8a35..1f7ec54b519f 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/AbstractServletHandlerMethodTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/AbstractServletHandlerMethodTests.java @@ -110,7 +110,9 @@ protected WebApplicationContext createWebApplicationContext(@Nullable WebApplica } }; - servlet.init(new MockServletConfig()); + MockServletConfig config = new MockServletConfig(); + config.addInitParameter("jakarta.servlet.http.legacyDoHead", "true"); + servlet.init(config); return wac; } diff --git a/spring-websocket/spring-websocket.gradle b/spring-websocket/spring-websocket.gradle index 3c9d011b4a8a..333a9a5c98bc 100644 --- a/spring-websocket/spring-websocket.gradle +++ b/spring-websocket/spring-websocket.gradle @@ -8,6 +8,7 @@ dependencies { optional(project(":spring-webmvc")) optional("jakarta.servlet:jakarta.servlet-api") optional("jakarta.websocket:jakarta.websocket-api") + optional("jakarta.websocket:jakarta.websocket-client-api") optional("org.apache.tomcat:tomcat-websocket") { exclude group: "org.apache.tomcat", module: "tomcat-servlet-api" exclude group: "org.apache.tomcat", module: "tomcat-websocket-api" @@ -21,8 +22,8 @@ dependencies { } optional("org.eclipse.jetty.websocket:websocket-jetty-client") optional("org.eclipse.jetty:jetty-client") - optional("io.undertow:undertow-servlet-jakarta") - optional("io.undertow:undertow-websockets-jsr-jakarta") + optional("io.undertow:undertow-servlet") + optional("io.undertow:undertow-websockets-jsr") optional("com.fasterxml.jackson.core:jackson-databind") testImplementation(testFixtures(project(":spring-core"))) testImplementation(testFixtures(project(":spring-web"))) diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java index 3a9f7b0561f5..a02b18b82f7a 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/AbstractTyrusRequestUpgradeStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2022 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. @@ -72,7 +72,7 @@ public abstract class AbstractTyrusRequestUpgradeStrategy extends AbstractStanda private static final Constructor constructor; - private static boolean constructorWithBooleanArgument; + private static final boolean constructorWithBooleanArgument; private static final Method registerMethod; diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/StandardWebSocketUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/StandardWebSocketUpgradeStrategy.java new file mode 100644 index 000000000000..0ac70abe408c --- /dev/null +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/StandardWebSocketUpgradeStrategy.java @@ -0,0 +1,77 @@ +/* + * Copyright 2002-2022 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 + * + * https://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.web.socket.server.standard; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.websocket.Endpoint; +import jakarta.websocket.Extension; + +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.lang.Nullable; +import org.springframework.web.socket.server.HandshakeFailureException; + +/** + * A WebSocket {@code RequestUpgradeStrategy} for the Jakarta WebSocket API 2.1+. + * + *

To modify properties of the underlying {@link jakarta.websocket.server.ServerContainer} + * you can use {@link ServletServerContainerFactoryBean} in XML configuration or, + * when using Java configuration, access the container instance through the + * "jakarta.websocket.server.ServerContainer" ServletContext attribute. + * + * @author Juergen Hoeller + * @since 6.0 + * @see jakarta.websocket.server.ServerContainer#upgradeHttpToWebSocket + */ +public class StandardWebSocketUpgradeStrategy extends AbstractStandardUpgradeStrategy { + + @Override + public String[] getSupportedVersions() { + return new String[] {"13"}; + } + + @Override + public void upgradeInternal(ServerHttpRequest request, ServerHttpResponse response, + @Nullable String selectedProtocol, List selectedExtensions, Endpoint endpoint) + throws HandshakeFailureException { + + HttpServletRequest servletRequest = getHttpServletRequest(request); + HttpServletResponse servletResponse = getHttpServletResponse(response); + + StringBuffer requestUrl = servletRequest.getRequestURL(); + String path = servletRequest.getRequestURI(); // shouldn't matter + Map pathParams = Collections. emptyMap(); + + ServerEndpointRegistration endpointConfig = new ServerEndpointRegistration(path, endpoint); + endpointConfig.setSubprotocols(Collections.singletonList(selectedProtocol)); + endpointConfig.setExtensions(selectedExtensions); + + try { + getContainer(servletRequest).upgradeHttpToWebSocket(servletRequest, servletResponse, endpointConfig, pathParams); + } + catch (Exception ex) { + throw new HandshakeFailureException( + "Servlet request failed to upgrade to WebSocket: " + requestUrl, ex); + } + } + +} diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/TomcatRequestUpgradeStrategy.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/TomcatRequestUpgradeStrategy.java index 20e5f7a435db..2344cf660c38 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/TomcatRequestUpgradeStrategy.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/TomcatRequestUpgradeStrategy.java @@ -42,6 +42,7 @@ * * @author Rossen Stoyanchev * @since 4.0 + * @see WsServerContainer#upgradeHttpToWebSocket */ public class TomcatRequestUpgradeStrategy extends AbstractStandardUpgradeStrategy { diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java index a7a5f4e0d976..ead6f06852e1 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java @@ -47,6 +47,7 @@ import org.springframework.web.socket.server.HandshakeFailureException; import org.springframework.web.socket.server.HandshakeHandler; import org.springframework.web.socket.server.RequestUpgradeStrategy; +import org.springframework.web.socket.server.standard.StandardWebSocketUpgradeStrategy; /** * A base class for {@link HandshakeHandler} implementations, independent of the Servlet API. @@ -149,7 +150,8 @@ else if (websphereWsPresent) { className = "org.springframework.web.socket.server.standard.WebSphereRequestUpgradeStrategy"; } else { - throw new IllegalStateException("No suitable default RequestUpgradeStrategy found"); + // Let's assume Jakarta WebSocket API 2.1+ + return new StandardWebSocketUpgradeStrategy(); } try {