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
This commit is contained in:
Juergen Hoeller
2022-11-06 16:08:30 +01:00
parent 4b22a4a0d8
commit 19cf503534
37 changed files with 289 additions and 340 deletions

View File

@@ -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 @@ import org.springframework.util.StringUtils;
*
* @author Marek Hawrylczak
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 5.0
*/
class UndertowServerHttpRequest extends AbstractServerHttpRequest {
@@ -82,15 +83,12 @@ class UndertowServerHttpRequest extends AbstractServerHttpRequest {
return HttpMethod.valueOf(this.exchange.getRequestMethod().toString());
}
@SuppressWarnings("deprecation")
@Override
protected MultiValueMap<String, HttpCookie> initCookies() {
MultiValueMap<String, HttpCookie> 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;
}

View File

@@ -47,6 +47,7 @@ import org.springframework.util.Assert;
* @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 @@ class UndertowServerHttpResponse extends AbstractListenerServerHttpResponse impl
protected void applyHeaders() {
}
@SuppressWarnings("deprecation")
@Override
protected void applyCookies() {
for (String name : getCookies().keySet()) {
@@ -123,8 +123,7 @@ class UndertowServerHttpResponse extends AbstractListenerServerHttpResponse impl
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 @@ class UndertowServerHttpResponse extends AbstractListenerServerHttpResponse impl
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) {

View File

@@ -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 class ContentCachingResponseWrapper extends HttpServletResponseWrapper {
}
@Override
@SuppressWarnings("deprecation")
public void sendError(int sc, String msg) throws IOException {
copyBodyToResponse(false);
try {
@@ -86,7 +85,7 @@ public class ContentCachingResponseWrapper extends HttpServletResponseWrapper {
}
catch (IllegalStateException ex) {
// Possibly on Tomcat when called too late: fall back to silent setStatus
super.setStatus(sc, msg);
super.setStatus(sc);
}
}

View File

@@ -36,6 +36,7 @@ import org.springframework.util.StringUtils;
* @author Sam Brannen
* @since 5.1
*/
@SuppressWarnings("removal")
public class MockCookie extends Cookie {
private static final long serialVersionUID = 4312531139502726325L;

View File

@@ -46,6 +46,7 @@ import java.util.stream.Collectors;
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 @@ import org.springframework.web.util.UrlPathHelper;
* is {@link Locale#ENGLISH}. This value can be changed via {@link #addPreferredLocale}
* or {@link #setPreferredLocales}.
*
* <p>As of Spring Framework 5.0, this set of mocks is designed on a Servlet 4.0 baseline.
* <p>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 class MockHttpServletRequest implements HttpServletRequest {
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 class MockHttpServletRequest implements HttpServletRequest {
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 class MockHttpServletRequest implements HttpServletRequest {
@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 class MockHttpServletRequest implements HttpServletRequest {
return this.requestedSessionIdFromURL;
}
@Override
@Deprecated
public boolean isRequestedSessionIdFromUrl() {
return isRequestedSessionIdFromURL();
}
@Override
public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {
throw new UnsupportedOperationException();

View File

@@ -53,7 +53,7 @@ import org.springframework.web.util.WebUtils;
/**
* Mock implementation of the {@link jakarta.servlet.http.HttpServletResponse} interface.
*
* <p>As of Spring Framework 5.0, this set of mocks is designed on a Servlet 4.0 baseline.
* <p>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 class MockHttpServletResponse implements HttpServletResponse {
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 class MockHttpServletResponse implements HttpServletResponse {
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 class MockHttpServletResponse implements HttpServletResponse {
}
}
@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;

View File

@@ -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 jakarta.servlet.http.HttpSessionBindingListener;
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.
*
* <p>As of Spring 5.0, this set of mocks is designed on a Servlet 4.0 baseline.
* <p>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 class MockHttpSession implements HttpSession {
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 class MockHttpSession implements HttpSession {
return this.attributes.get(name);
}
@Override
public Object getValue(String name) {
return getAttribute(name);
}
@Override
public Enumeration<String> 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 class MockHttpSession implements HttpSession {
}
}
@Override
public void putValue(String name, Object value) {
setAttribute(name, value);
}
@Override
public void removeAttribute(String name) {
assertIsValid();
@@ -212,11 +190,6 @@ public class MockHttpSession implements HttpSession {
}
}
@Override
public void removeValue(String name) {
removeAttribute(name);
}
/**
* Clear all of this session's attributes.
*/

View File

@@ -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.Enumeration;
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 @@ import org.springframework.web.multipart.MultipartHttpServletRequest;
* Mock implementation of the
* {@link org.springframework.web.multipart.MultipartHttpServletRequest} interface.
*
* <p>As of Spring 5.0, this set of mocks is designed on a Servlet 4.0 baseline.
* <p>As of Spring 6.0, this set of mocks is designed on a Servlet 6.0 baseline.
*
* <p>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 class MockMultipartHttpServletRequest extends MockHttpServletRequest impl
@Override
public List<MultipartFile> getFiles(String name) {
List<MultipartFile> 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 class MockMultipartHttpServletRequest extends MockHttpServletRequest impl
@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

View File

@@ -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 @@ import org.springframework.web.util.WebUtils;
/**
* Mock implementation of the {@link jakarta.servlet.ServletContext} interface.
*
* <p>As of Spring 5.0, this set of mocks is designed on a Servlet 4.0 baseline.
* <p>As of Spring 6.0, this set of mocks is designed on a Servlet 6.0 baseline.
*
* <p>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 class MockServletContext implements ServletContext {
registerNamedDispatcher(this.defaultServletName, new MockRequestDispatcher(this.defaultServletName));
}
@Deprecated
@Override
@Nullable
public Servlet getServlet(String name) {
return null;
}
@Override
@Deprecated
public Enumeration<Servlet> getServlets() {
return Collections.enumeration(Collections.emptySet());
}
@Override
@Deprecated
public Enumeration<String> 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);

View File

@@ -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<String, String> attributes = new LinkedHashMap<>();
@Override
public void setName(@Nullable String name) {
@@ -81,11 +87,13 @@ public class MockSessionCookieConfig implements SessionCookieConfig {
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 class MockSessionCookieConfig implements SessionCookieConfig {
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<String, String> getAttributes() {
return Collections.unmodifiableMap(this.attributes);
}
}