diff --git a/spring-web/src/main/java/org/springframework/http/ContentDisposition.java b/spring-web/src/main/java/org/springframework/http/ContentDisposition.java index 119d0a29f2..f436b3f565 100644 --- a/spring-web/src/main/java/org/springframework/http/ContentDisposition.java +++ b/spring-web/src/main/java/org/springframework/http/ContentDisposition.java @@ -233,9 +233,9 @@ public final class ContentDisposition { result = 31 * result + ObjectUtils.nullSafeHashCode(this.filename); result = 31 * result + ObjectUtils.nullSafeHashCode(this.charset); result = 31 * result + ObjectUtils.nullSafeHashCode(this.size); - result = 31 * result + (this.creationDate != null ? this.creationDate.hashCode() : 0); - result = 31 * result + (this.modificationDate != null ? this.modificationDate.hashCode() : 0); - result = 31 * result + (this.readDate != null ? this.readDate.hashCode() : 0); + result = 31 * result + ObjectUtils.nullSafeHashCode(this.creationDate); + result = 31 * result + ObjectUtils.nullSafeHashCode(this.modificationDate); + result = 31 * result + ObjectUtils.nullSafeHashCode(this.readDate); return result; } @@ -360,7 +360,7 @@ public final class ContentDisposition { if (idx1 != -1 && idx2 != -1) { charset = Charset.forName(value.substring(0, idx1).trim()); Assert.isTrue(UTF_8.equals(charset) || ISO_8859_1.equals(charset), - "Charset should be UTF-8 or ISO-8859-1"); + "Charset must be UTF-8 or ISO-8859-1"); filename = decodeFilename(value.substring(idx2 + 1), charset); } else { @@ -532,10 +532,11 @@ public final class ContentDisposition { * @see RFC 5987 */ private static String encodeFilename(String input, Charset charset) { - Assert.notNull(input, "'input' is required"); - Assert.notNull(charset, "'charset' is required"); + Assert.notNull(input, "'input' must not be null"); + Assert.notNull(charset, "'charset' must not be null"); Assert.isTrue(!StandardCharsets.US_ASCII.equals(charset), "ASCII does not require encoding"); Assert.isTrue(UTF_8.equals(charset) || ISO_8859_1.equals(charset), "Only UTF-8 and ISO-8859-1 are supported"); + byte[] source = input.getBytes(charset); int len = source.length; StringBuilder sb = new StringBuilder(len << 1); diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/JettyHttpHandlerAdapter.java b/spring-web/src/main/java/org/springframework/http/server/reactive/JettyHttpHandlerAdapter.java index 89a3150683..041f8fd383 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/JettyHttpHandlerAdapter.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/JettyHttpHandlerAdapter.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. @@ -65,12 +65,12 @@ public class JettyHttpHandlerAdapter extends ServletHttpHandlerAdapter { protected ServletServerHttpRequest createRequest(HttpServletRequest request, AsyncContext context) throws IOException, URISyntaxException { - // TODO: need to compile against Jetty 10 to use HttpFields (class->interface) if (jetty10Present) { + // No HttpFields optimization on Jetty 10 due to binary incompatibility return super.createRequest(request, context); } - Assert.notNull(getServletPath(), "Servlet path is not initialized"); + Assert.state(getServletPath() != null, "Servlet path is not initialized"); return new JettyServerHttpRequest( request, context, getServletPath(), getDataBufferFactory(), getBufferSize()); } @@ -79,15 +79,14 @@ public class JettyHttpHandlerAdapter extends ServletHttpHandlerAdapter { protected ServletServerHttpResponse createResponse(HttpServletResponse response, AsyncContext context, ServletServerHttpRequest request) throws IOException { - // TODO: need to compile against Jetty 10 to use HttpFields (class->interface) if (jetty10Present) { + // No HttpFields optimization on Jetty 10 due to binary incompatibility return new BaseJettyServerHttpResponse( response, context, getDataBufferFactory(), getBufferSize(), request); } - else { - return new JettyServerHttpResponse( - response, context, getDataBufferFactory(), getBufferSize(), request); - } + + return new JettyServerHttpResponse( + response, context, getDataBufferFactory(), getBufferSize(), request); } @@ -120,8 +119,6 @@ public class JettyHttpHandlerAdapter extends ServletHttpHandlerAdapter { "] to org.eclipse.jetty.server.Request"); } } - - } diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ServletHttpHandlerAdapter.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ServletHttpHandlerAdapter.java index c38837c7ed..0cedc4dd1b 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ServletHttpHandlerAdapter.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ServletHttpHandlerAdapter.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. @@ -170,7 +170,7 @@ public class ServletHttpHandlerAdapter implements Servlet { AsyncListener requestListener; String logPrefix; try { - httpRequest = createRequest(((HttpServletRequest) request), asyncContext); + httpRequest = createRequest((HttpServletRequest) request, asyncContext); requestListener = httpRequest.getAsyncListener(); logPrefix = httpRequest.getLogPrefix(); } @@ -183,8 +183,10 @@ public class ServletHttpHandlerAdapter implements Servlet { return; } - ServerHttpResponse httpResponse = createResponse(((HttpServletResponse) response), asyncContext, httpRequest); - AsyncListener responseListener = ((ServletServerHttpResponse) httpResponse).getAsyncListener(); + ServletServerHttpResponse wrappedResponse = + createResponse((HttpServletResponse) response, asyncContext, httpRequest); + ServerHttpResponse httpResponse = wrappedResponse; + AsyncListener responseListener = wrappedResponse.getAsyncListener(); if (httpRequest.getMethod() == HttpMethod.HEAD) { httpResponse = new HttpHeadResponseDecorator(httpResponse); } @@ -201,7 +203,7 @@ public class ServletHttpHandlerAdapter implements Servlet { protected ServletServerHttpRequest createRequest(HttpServletRequest request, AsyncContext context) throws IOException, URISyntaxException { - Assert.notNull(this.servletPath, "Servlet path is not initialized"); + Assert.state(this.servletPath != null, "Servlet path is not initialized"); return new ServletServerHttpRequest( request, context, this.servletPath, getDataBufferFactory(), getBufferSize()); } @@ -264,9 +266,7 @@ public class ServletHttpHandlerAdapter implements Servlet { private final String logPrefix; - - public HttpHandlerAsyncListener( - AsyncListener requestAsyncListener, AsyncListener responseAsyncListener, + public HttpHandlerAsyncListener(AsyncListener requestAsyncListener, AsyncListener responseAsyncListener, Runnable handlerDisposeTask, AtomicBoolean completionFlag, String logPrefix) { this.requestAsyncListener = requestAsyncListener; @@ -276,7 +276,6 @@ public class ServletHttpHandlerAdapter implements Servlet { this.logPrefix = logPrefix; } - @Override public void onTimeout(AsyncEvent event) { // Should never happen since we call asyncContext.setTimeout(-1) @@ -362,9 +361,7 @@ public class ServletHttpHandlerAdapter implements Servlet { @Nullable private volatile Subscription subscription; - public HandlerResultSubscriber( - AsyncContext asyncContext, AtomicBoolean completionFlag, String logPrefix) { - + public HandlerResultSubscriber(AsyncContext asyncContext, AtomicBoolean completionFlag, String logPrefix) { this.asyncContext = asyncContext; this.completionFlag = completionFlag; this.logPrefix = logPrefix; diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/TomcatHttpHandlerAdapter.java b/spring-web/src/main/java/org/springframework/http/server/reactive/TomcatHttpHandlerAdapter.java index b8c78fdbf4..ed67bc57ac 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/TomcatHttpHandlerAdapter.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/TomcatHttpHandlerAdapter.java @@ -58,7 +58,6 @@ import org.springframework.util.ReflectionUtils; */ public class TomcatHttpHandlerAdapter extends ServletHttpHandlerAdapter { - public TomcatHttpHandlerAdapter(HttpHandler httpHandler) { super(httpHandler); } @@ -68,7 +67,7 @@ public class TomcatHttpHandlerAdapter extends ServletHttpHandlerAdapter { protected ServletServerHttpRequest createRequest(HttpServletRequest request, AsyncContext asyncContext) throws IOException, URISyntaxException { - Assert.notNull(getServletPath(), "Servlet path is not initialized"); + Assert.state(getServletPath() != null, "Servlet path is not initialized"); return new TomcatServerHttpRequest( request, asyncContext, getServletPath(), getDataBufferFactory(), getBufferSize()); } diff --git a/spring-web/src/test/java/org/springframework/http/ContentDispositionTests.java b/spring-web/src/test/java/org/springframework/http/ContentDispositionTests.java index 690eda7f55..af8cf0aee7 100644 --- a/spring-web/src/test/java/org/springframework/http/ContentDispositionTests.java +++ b/spring-web/src/test/java/org/springframework/http/ContentDispositionTests.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. @@ -25,20 +25,22 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.springframework.http.ContentDisposition.parse; /** - * Unit tests for {@link ContentDisposition} + * Unit tests for {@link ContentDisposition}. + * * @author Sebastien Deleuze * @author Rossen Stoyanchev */ class ContentDispositionTests { - private static DateTimeFormatter formatter = DateTimeFormatter.RFC_1123_DATE_TIME; + private static final DateTimeFormatter formatter = DateTimeFormatter.RFC_1123_DATE_TIME; - @Test @SuppressWarnings("deprecation") - void parse() { + @Test + void parseFilenameQuoted() { assertThat(parse("form-data; name=\"foo\"; filename=\"foo.txt\"; size=123")) .isEqualTo(ContentDisposition.formData() .name("foo") @@ -72,7 +74,7 @@ class ContentDispositionTests { .build()); } - @Test // gh-24112 + @Test // gh-24112 void parseEncodedFilenameWithPaddedCharset() { assertThat(parse("attachment; filename*= UTF-8''some-file.zip")) .isEqualTo(ContentDisposition.attachment() @@ -80,13 +82,13 @@ class ContentDispositionTests { .build()); } - @Test // gh-26463 + @Test // gh-26463 void parseBase64EncodedFilename() { String input = "attachment; filename=\"=?UTF-8?B?5pel5pys6KqeLmNzdg==?=\""; assertThat(parse(input).getFilename()).isEqualTo("日本語.csv"); } - @Test // gh-26463 + @Test // gh-26463 void parseBase64EncodedShiftJISFilename() { String input = "attachment; filename=\"=?SHIFT_JIS?B?k/qWe4zqLmNzdg==?=\""; assertThat(parse(input).getFilename()).isEqualTo("日本語.csv"); @@ -116,7 +118,7 @@ class ContentDispositionTests { .isThrownBy(() -> parse("form-data; name=\"name\"; filename*=UTF-8''%A.txt")); } - @Test // gh-23077 + @Test // gh-23077 @SuppressWarnings("deprecation") void parseWithEscapedQuote() { BiConsumer tester = (description, filename) -> @@ -137,8 +139,8 @@ class ContentDispositionTests { "The Twilight Zone \\\\\\\\"); } - @Test @SuppressWarnings("deprecation") + @Test void parseWithExtraSemicolons() { assertThat(parse("form-data; name=\"foo\";; ; filename=\"foo.txt\"; size=123")) .isEqualTo(ContentDisposition.formData() @@ -148,8 +150,8 @@ class ContentDispositionTests { .build()); } - @Test @SuppressWarnings("deprecation") + @Test void parseDates() { ZonedDateTime creationTime = ZonedDateTime.parse("Mon, 12 Feb 2007 10:15:30 -0500", formatter); ZonedDateTime modificationTime = ZonedDateTime.parse("Tue, 13 Feb 2007 10:15:30 -0500", formatter); @@ -167,8 +169,8 @@ class ContentDispositionTests { .build()); } - @Test @SuppressWarnings("deprecation") + @Test void parseIgnoresInvalidDates() { ZonedDateTime readTime = ZonedDateTime.parse("Wed, 14 Feb 2007 10:15:30 -0500", formatter); @@ -197,13 +199,8 @@ class ContentDispositionTests { assertThatIllegalArgumentException().isThrownBy(() -> parse("foo;bar")); } - private static ContentDisposition parse(String input) { - return ContentDisposition.parse(input); - } - - - @Test @SuppressWarnings("deprecation") + @Test void format() { assertThat( ContentDisposition.formData() @@ -235,14 +232,11 @@ class ContentDispositionTests { .isEqualTo("form-data; name=\"name\"; filename=\"test.txt\""); } - @Test // gh-24220 + @Test // gh-24220 void formatWithFilenameWithQuotes() { - BiConsumer tester = (input, output) -> { - assertThat(ContentDisposition.formData().filename(input).build().toString()) .isEqualTo("form-data; filename=\"" + output + "\""); - assertThat(ContentDisposition.formData().filename(input, StandardCharsets.US_ASCII).build().toString()) .isEqualTo("form-data; filename=\"" + output + "\""); };