From 4261f34b633eaa21fc2791d356466f232637276d Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 9 Dec 2015 12:26:44 +0100 Subject: [PATCH] Consistent and lenient HttpMethod resolution across all web modules Issue: SPR-13776 --- .../web/MockMultipartHttpServletRequest.java | 4 +- .../org/springframework/http/HttpHeaders.java | 31 ++++++++------ .../org/springframework/http/HttpMethod.java | 40 ++++++++++++++++++- .../org/springframework/http/HttpRequest.java | 11 ++--- .../springframework/http/RequestEntity.java | 3 -- .../HttpComponentsAsyncClientHttpRequest.java | 2 +- .../HttpComponentsClientHttpRequest.java | 2 +- ...pComponentsStreamingClientHttpRequest.java | 2 +- ...SimpleBufferingAsyncClientHttpRequest.java | 8 ++-- .../SimpleBufferingClientHttpRequest.java | 6 +-- ...SimpleStreamingAsyncClientHttpRequest.java | 4 +- .../SimpleStreamingClientHttpRequest.java | 4 +- .../http/server/ServletServerHttpRequest.java | 37 ++++++++--------- ...ttpRequestMethodNotSupportedException.java | 16 +++++--- .../web/bind/annotation/RequestMethod.java | 14 +++---- .../context/request/ServletWebRequest.java | 2 +- .../web/cors/CorsConfiguration.java | 7 +++- .../springframework/web/cors/CorsUtils.java | 4 +- .../web/filter/ShallowEtagHeaderFilter.java | 2 +- .../AbstractMultipartHttpServletRequest.java | 4 +- .../util/ContentCachingRequestWrapper.java | 6 +-- .../test/MockMultipartHttpServletRequest.java | 4 +- .../web/servlet/DispatcherServlet.java | 5 +-- .../web/servlet/FrameworkServlet.java | 7 ++-- ...essageConverterMethodArgumentResolver.java | 10 ++--- .../annotation/HttpEntityMethodProcessor.java | 9 ++--- .../support/AbstractHandshakeHandler.java | 6 +-- .../sockjs/support/AbstractSockJsService.java | 4 +- .../TransportHandlingSockJsService.java | 4 +- 29 files changed, 149 insertions(+), 109 deletions(-) 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 ac0cb75e64..7988b17b32 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-2013 the original author or authors. + * 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. @@ -128,7 +128,7 @@ public class MockMultipartHttpServletRequest extends MockHttpServletRequest impl @Override public HttpMethod getRequestMethod() { - return HttpMethod.valueOf(getMethod()); + return HttpMethod.resolve(getMethod()); } @Override diff --git a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java index 006a631faf..886b06a633 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -470,7 +470,7 @@ public class HttpHeaders implements MultiValueMap, Serializable } /** - * Returns the value of the {@code Access-Control-Allow-Methods} response header. + * Return the value of the {@code Access-Control-Allow-Methods} response header. */ public List getAccessControlAllowMethods() { List result = new ArrayList(); @@ -478,7 +478,10 @@ public class HttpHeaders implements MultiValueMap, Serializable if (value != null) { String[] tokens = value.split(",\\s*"); for (String token : tokens) { - result.add(HttpMethod.valueOf(token)); + HttpMethod resolved = HttpMethod.resolve(token); + if (resolved != null) { + result.add(resolved); + } } } return result; @@ -492,7 +495,7 @@ public class HttpHeaders implements MultiValueMap, Serializable } /** - * Returns the value of the {@code Access-Control-Allow-Origin} response header. + * Return the value of the {@code Access-Control-Allow-Origin} response header. */ public String getAccessControlAllowOrigin() { return getFirst(ACCESS_CONTROL_ALLOW_ORIGIN); @@ -550,11 +553,10 @@ public class HttpHeaders implements MultiValueMap, Serializable } /** - * Returns the value of the {@code Access-Control-Request-Method} request header. + * Return 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); + return HttpMethod.resolve(getFirst(ACCESS_CONTROL_REQUEST_METHOD)); } /** @@ -615,12 +617,15 @@ public class HttpHeaders implements MultiValueMap, Serializable public Set getAllow() { String value = getFirst(ALLOW); if (!StringUtils.isEmpty(value)) { - List allowedMethod = new ArrayList(5); + List result = new LinkedList(); String[] tokens = value.split(",\\s*"); for (String token : tokens) { - allowedMethod.add(HttpMethod.valueOf(token)); + HttpMethod resolved = HttpMethod.resolve(token); + if (resolved != null) { + result.add(resolved); + } } - return EnumSet.copyOf(allowedMethod); + return EnumSet.copyOf(result); } else { return EnumSet.noneOf(HttpMethod.class); @@ -635,7 +640,7 @@ public class HttpHeaders implements MultiValueMap, Serializable } /** - * Returns the value of the {@code Cache-Control} header. + * Return the value of the {@code Cache-Control} header. */ public String getCacheControl() { return getFirst(CACHE_CONTROL); @@ -656,7 +661,7 @@ public class HttpHeaders implements MultiValueMap, Serializable } /** - * Returns the value of the {@code Connection} header. + * Return the value of the {@code Connection} header. */ public List getConnection() { return getFirstValueAsList(CONNECTION); @@ -920,7 +925,7 @@ public class HttpHeaders implements MultiValueMap, Serializable } /** - * Returns the value of the {@code Range} header. + * Return the value of the {@code Range} header. *

Returns an empty list when the range is unknown. */ public List getRange() { @@ -936,7 +941,7 @@ public class HttpHeaders implements MultiValueMap, Serializable } /** - * Returns the value of the {@code Upgrade} header. + * Return the value of the {@code Upgrade} header. */ public String getUpgrade() { return getFirst(UPGRADE); diff --git a/spring-web/src/main/java/org/springframework/http/HttpMethod.java b/spring-web/src/main/java/org/springframework/http/HttpMethod.java index f006c703cc..87173fd381 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpMethod.java +++ b/spring-web/src/main/java/org/springframework/http/HttpMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * 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. @@ -16,16 +16,52 @@ package org.springframework.http; +import java.util.HashMap; +import java.util.Map; + /** * Java 5 enumeration of HTTP request methods. Intended for use * with {@link org.springframework.http.client.ClientHttpRequest} * and {@link org.springframework.web.client.RestTemplate}. * * @author Arjen Poutsma + * @author Juergen Hoeller * @since 3.0 */ public enum HttpMethod { - GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + + + private static final Map mappings = new HashMap(8); + + static { + for (HttpMethod httpMethod : values()) { + mappings.put(httpMethod.name(), httpMethod); + } + } + + + /** + * Resolve the given method value to an {@code HttpMethod}. + * @param method the method value as a String + * @return the corresponding {@code HttpMethod}, or {@code null} if not found + * @since 4.2.4 + */ + public static HttpMethod resolve(String method) { + return (method != null ? mappings.get(method) : null); + } + + + /** + * Determine whether this {@code HttpMethod} matches the given + * method value. + * @param method the method value as a String + * @return {@code true} if it matches, {@code false} otherwise + * @since 4.2.4 + */ + public boolean matches(String method) { + return name().equals(method); + } } diff --git a/spring-web/src/main/java/org/springframework/http/HttpRequest.java b/spring-web/src/main/java/org/springframework/http/HttpRequest.java index b84df266de..2f670a37e7 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/HttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * 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. @@ -19,8 +19,8 @@ package org.springframework.http; import java.net.URI; /** - * Represents an HTTP request message, consisting of {@linkplain #getMethod() method} - * and {@linkplain #getURI() uri}. + * Represents an HTTP request message, consisting of + * {@linkplain #getMethod() method} and {@linkplain #getURI() uri}. * * @author Arjen Poutsma * @since 3.1 @@ -29,13 +29,14 @@ public interface HttpRequest extends HttpMessage { /** * Return the HTTP method of the request. - * @return the HTTP method as an HttpMethod enum value + * @return the HTTP method as an HttpMethod enum value, or {@code null} + * if not resolvable (e.g. in case of a non-standard HTTP method) */ HttpMethod getMethod(); /** * Return the URI of the request. - * @return the URI of the request + * @return the URI of the request (never {@code null}) */ URI getURI(); diff --git a/spring-web/src/main/java/org/springframework/http/RequestEntity.java b/spring-web/src/main/java/org/springframework/http/RequestEntity.java index e7e4b5e763..7e9ab88b2f 100644 --- a/spring-web/src/main/java/org/springframework/http/RequestEntity.java +++ b/spring-web/src/main/java/org/springframework/http/RequestEntity.java @@ -20,7 +20,6 @@ import java.net.URI; import java.nio.charset.Charset; import java.util.Arrays; -import org.springframework.util.Assert; import org.springframework.util.MultiValueMap; import org.springframework.util.ObjectUtils; @@ -104,8 +103,6 @@ public class RequestEntity extends HttpEntity { */ public RequestEntity(T body, MultiValueMap headers, HttpMethod method, URI url) { super(body, headers); - Assert.notNull(method, "'method' is required"); - Assert.notNull(url, "'url' is required"); this.method = method; this.url = url; } diff --git a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsAsyncClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsAsyncClientHttpRequest.java index 4606c07a35..e062ba8db4 100644 --- a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsAsyncClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsAsyncClientHttpRequest.java @@ -68,7 +68,7 @@ final class HttpComponentsAsyncClientHttpRequest extends AbstractBufferingAsyncC @Override public HttpMethod getMethod() { - return HttpMethod.valueOf(this.httpRequest.getMethod()); + return HttpMethod.resolve(this.httpRequest.getMethod()); } @Override diff --git a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequest.java index 996a17e07e..0bf8d9fb04 100644 --- a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsClientHttpRequest.java @@ -66,7 +66,7 @@ final class HttpComponentsClientHttpRequest extends AbstractBufferingClientHttpR @Override public HttpMethod getMethod() { - return HttpMethod.valueOf(this.httpRequest.getMethod()); + return HttpMethod.resolve(this.httpRequest.getMethod()); } @Override diff --git a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsStreamingClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsStreamingClientHttpRequest.java index 856a9ea684..68756d7166 100644 --- a/spring-web/src/main/java/org/springframework/http/client/HttpComponentsStreamingClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/HttpComponentsStreamingClientHttpRequest.java @@ -65,7 +65,7 @@ final class HttpComponentsStreamingClientHttpRequest extends AbstractClientHttpR @Override public HttpMethod getMethod() { - return HttpMethod.valueOf(this.httpRequest.getMethod()); + return HttpMethod.resolve(this.httpRequest.getMethod()); } @Override diff --git a/spring-web/src/main/java/org/springframework/http/client/SimpleBufferingAsyncClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/SimpleBufferingAsyncClientHttpRequest.java index 336cf4f239..015a2e4225 100644 --- a/spring-web/src/main/java/org/springframework/http/client/SimpleBufferingAsyncClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/SimpleBufferingAsyncClientHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * 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. @@ -57,7 +57,7 @@ final class SimpleBufferingAsyncClientHttpRequest extends AbstractBufferingAsync @Override public HttpMethod getMethod() { - return HttpMethod.valueOf(this.connection.getRequestMethod()); + return HttpMethod.resolve(this.connection.getRequestMethod()); } @Override @@ -78,8 +78,8 @@ final class SimpleBufferingAsyncClientHttpRequest extends AbstractBufferingAsync @Override public ClientHttpResponse call() throws Exception { SimpleBufferingClientHttpRequest.addHeaders(connection, headers); - // JDK < 1.8 doesn't support getOutputStream with HTTP DELETE - if (HttpMethod.DELETE.equals(getMethod()) && bufferedOutput.length == 0) { + // JDK <1.8 doesn't support getOutputStream with HTTP DELETE + if (HttpMethod.DELETE == getMethod() && bufferedOutput.length == 0) { connection.setDoOutput(false); } if (connection.getDoOutput() && outputStreaming) { diff --git a/spring-web/src/main/java/org/springframework/http/client/SimpleBufferingClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/SimpleBufferingClientHttpRequest.java index c16f984888..24195bebdf 100644 --- a/spring-web/src/main/java/org/springframework/http/client/SimpleBufferingClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/SimpleBufferingClientHttpRequest.java @@ -52,7 +52,7 @@ final class SimpleBufferingClientHttpRequest extends AbstractBufferingClientHttp @Override public HttpMethod getMethod() { - return HttpMethod.valueOf(this.connection.getRequestMethod()); + return HttpMethod.resolve(this.connection.getRequestMethod()); } @Override @@ -69,8 +69,8 @@ final class SimpleBufferingClientHttpRequest extends AbstractBufferingClientHttp protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException { addHeaders(this.connection, headers); - // JDK < 1.8 doesn't support getOutputStream with HTTP DELETE - if (HttpMethod.DELETE.equals(getMethod()) && bufferedOutput.length == 0) { + // JDK <1.8 doesn't support getOutputStream with HTTP DELETE + if (HttpMethod.DELETE == getMethod() && bufferedOutput.length == 0) { this.connection.setDoOutput(false); } diff --git a/spring-web/src/main/java/org/springframework/http/client/SimpleStreamingAsyncClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/SimpleStreamingAsyncClientHttpRequest.java index cf6f59690b..0417eef09c 100644 --- a/spring-web/src/main/java/org/springframework/http/client/SimpleStreamingAsyncClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/SimpleStreamingAsyncClientHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * 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. @@ -63,7 +63,7 @@ final class SimpleStreamingAsyncClientHttpRequest extends AbstractAsyncClientHtt @Override public HttpMethod getMethod() { - return HttpMethod.valueOf(this.connection.getRequestMethod()); + return HttpMethod.resolve(this.connection.getRequestMethod()); } @Override diff --git a/spring-web/src/main/java/org/springframework/http/client/SimpleStreamingClientHttpRequest.java b/spring-web/src/main/java/org/springframework/http/client/SimpleStreamingClientHttpRequest.java index 504f567a61..5e871d007c 100644 --- a/spring-web/src/main/java/org/springframework/http/client/SimpleStreamingClientHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/client/SimpleStreamingClientHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * 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. @@ -53,7 +53,7 @@ final class SimpleStreamingClientHttpRequest extends AbstractClientHttpRequest { public HttpMethod getMethod() { - return HttpMethod.valueOf(this.connection.getRequestMethod()); + return HttpMethod.resolve(this.connection.getRequestMethod()); } @Override diff --git a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java index 489dfa294f..fbbe1446d6 100644 --- a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * 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. @@ -55,8 +55,6 @@ public class ServletServerHttpRequest implements ServerHttpRequest { protected static final String FORM_CHARSET = "UTF-8"; - private static final String METHOD_POST = "POST"; - private final HttpServletRequest servletRequest; @@ -84,7 +82,7 @@ public class ServletServerHttpRequest implements ServerHttpRequest { @Override public HttpMethod getMethod() { - return HttpMethod.valueOf(this.servletRequest.getMethod()); + return HttpMethod.resolve(this.servletRequest.getMethod()); } @Override @@ -166,9 +164,22 @@ public class ServletServerHttpRequest implements ServerHttpRequest { } } - private boolean isFormPost(HttpServletRequest request) { - return (request.getContentType() != null && request.getContentType().contains(FORM_CONTENT_TYPE) && - METHOD_POST.equalsIgnoreCase(request.getMethod())); + @Override + public ServerHttpAsyncRequestControl getAsyncRequestControl(ServerHttpResponse response) { + if (this.asyncRequestControl == null) { + Assert.isInstanceOf(ServletServerHttpResponse.class, response); + ServletServerHttpResponse servletServerResponse = (ServletServerHttpResponse) response; + this.asyncRequestControl = new ServletServerHttpAsyncRequestControl(this, servletServerResponse); + } + return this.asyncRequestControl; + } + + + + private static boolean isFormPost(HttpServletRequest request) { + String contentType = request.getContentType(); + return (contentType != null && contentType.contains(FORM_CONTENT_TYPE) && + HttpMethod.POST.matches(request.getMethod())); } /** @@ -177,7 +188,7 @@ public class ServletServerHttpRequest implements ServerHttpRequest { * from the body, which can fail if any other code has used ServletRequest * to access a parameter thus causing the input stream to be "consumed". */ - private InputStream getBodyFromServletRequestParameters(HttpServletRequest request) throws IOException { + private static InputStream getBodyFromServletRequestParameters(HttpServletRequest request) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(1024); Writer writer = new OutputStreamWriter(bos, FORM_CHARSET); @@ -205,14 +216,4 @@ public class ServletServerHttpRequest implements ServerHttpRequest { return new ByteArrayInputStream(bos.toByteArray()); } - @Override - public ServerHttpAsyncRequestControl getAsyncRequestControl(ServerHttpResponse response) { - if (this.asyncRequestControl == null) { - Assert.isInstanceOf(ServletServerHttpResponse.class, response); - ServletServerHttpResponse servletServerResponse = (ServletServerHttpResponse) response; - this.asyncRequestControl = new ServletServerHttpAsyncRequestControl(this, servletServerResponse); - } - return this.asyncRequestControl; - } - } diff --git a/spring-web/src/main/java/org/springframework/web/HttpRequestMethodNotSupportedException.java b/spring-web/src/main/java/org/springframework/web/HttpRequestMethodNotSupportedException.java index 44096bb583..13caeda9bb 100644 --- a/spring-web/src/main/java/org/springframework/web/HttpRequestMethodNotSupportedException.java +++ b/spring-web/src/main/java/org/springframework/web/HttpRequestMethodNotSupportedException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * 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. @@ -17,8 +17,9 @@ package org.springframework.web; import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; +import java.util.EnumSet; +import java.util.LinkedList; +import java.util.List; import java.util.Set; import javax.servlet.ServletException; @@ -105,11 +106,14 @@ public class HttpRequestMethodNotSupportedException extends ServletException { * Return the actually supported HTTP methods, if known, as {@link HttpMethod} instances. */ public Set getSupportedHttpMethods() { - Set supportedMethods = new LinkedHashSet(); + List supportedMethods = new LinkedList(); for (String value : this.supportedMethods) { - supportedMethods.add(HttpMethod.valueOf(value)); + HttpMethod resolved = HttpMethod.resolve(value); + if (resolved != null) { + supportedMethods.add(resolved); + } } - return Collections.unmodifiableSet(supportedMethods); + return EnumSet.copyOf(supportedMethods); } } diff --git a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMethod.java b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMethod.java index 814687ca65..80bd4b1c01 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMethod.java +++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * 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. @@ -17,16 +17,14 @@ package org.springframework.web.bind.annotation; /** - * Java 5 enumeration of HTTP request methods. Intended for use - * with the {@link RequestMapping#method()} attribute of the - * {@link RequestMapping} annotation. + * Java 5 enumeration of HTTP request methods. Intended for use with the + * {@link RequestMapping#method()} attribute of the {@link RequestMapping} annotation. * *

Note that, by default, {@link org.springframework.web.servlet.DispatcherServlet} * supports GET, HEAD, POST, PUT, PATCH and DELETE only. DispatcherServlet will - * process TRACE and OPTIONS with the default HttpServlet behavior unless - * explicitly told to dispatch those request types as well: Check out - * the "dispatchOptionsRequest" and "dispatchTraceRequest" properties, - * switching them to "true" if necessary. + * process TRACE and OPTIONS with the default HttpServlet behavior unless explicitly + * told to dispatch those request types as well: Check out the "dispatchOptionsRequest" + * and "dispatchTraceRequest" properties, switching them to "true" if necessary. * * @author Juergen Hoeller * @since 2.5 diff --git a/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java b/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java index 381e72bcc3..6cc8e05272 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java @@ -106,7 +106,7 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ * @since 4.0.2 */ public HttpMethod getHttpMethod() { - return HttpMethod.valueOf(getRequest().getMethod()); + return HttpMethod.resolve(getRequest().getMethod()); } @Override diff --git a/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java b/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java index 4cc231e50c..76daa33bbc 100644 --- a/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java +++ b/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java @@ -344,10 +344,13 @@ public class CorsConfiguration { List result = new ArrayList(allowedMethods.size()); boolean allowed = false; for (String method : allowedMethods) { - if (requestMethod.name().equals(method)) { + if (requestMethod.matches(method)) { allowed = true; } - result.add(HttpMethod.valueOf(method)); + HttpMethod resolved = HttpMethod.resolve(method); + if (resolved != null) { + result.add(resolved); + } } return (allowed ? result : null); } diff --git a/spring-web/src/main/java/org/springframework/web/cors/CorsUtils.java b/spring-web/src/main/java/org/springframework/web/cors/CorsUtils.java index d85ddd4cdf..c46f6956cf 100644 --- a/spring-web/src/main/java/org/springframework/web/cors/CorsUtils.java +++ b/spring-web/src/main/java/org/springframework/web/cors/CorsUtils.java @@ -28,7 +28,7 @@ import org.springframework.http.HttpMethod; * @author Sebastien Deleuze * @since 4.2 */ -public class CorsUtils { +public abstract class CorsUtils { /** * Returns {@code true} if the request is a valid CORS one. @@ -41,7 +41,7 @@ public class CorsUtils { * Returns {@code true} if the request is a valid CORS pre-flight one. */ public static boolean isPreFlightRequest(HttpServletRequest request) { - return (isCorsRequest(request) && request.getMethod().equals(HttpMethod.OPTIONS.name()) && + return (isCorsRequest(request) && HttpMethod.OPTIONS.matches(request.getMethod()) && request.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD) != null); } diff --git a/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java b/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java index 211cb0ebb3..b3fbe83826 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/ShallowEtagHeaderFilter.java @@ -144,7 +144,7 @@ public class ShallowEtagHeaderFilter extends OncePerRequestFilter { protected boolean isEligibleForEtag(HttpServletRequest request, HttpServletResponse response, int responseStatusCode, InputStream inputStream) { - if (responseStatusCode >= 200 && responseStatusCode < 300 && HttpMethod.GET.name().equals(request.getMethod())) { + if (responseStatusCode >= 200 && responseStatusCode < 300 && HttpMethod.GET.matches(request.getMethod())) { String cacheControl = null; if (servlet3Present) { cacheControl = response.getHeader(HEADER_CACHE_CONTROL); diff --git a/spring-web/src/main/java/org/springframework/web/multipart/support/AbstractMultipartHttpServletRequest.java b/spring-web/src/main/java/org/springframework/web/multipart/support/AbstractMultipartHttpServletRequest.java index decbe3859f..4e423e4496 100644 --- a/spring-web/src/main/java/org/springframework/web/multipart/support/AbstractMultipartHttpServletRequest.java +++ b/spring-web/src/main/java/org/springframework/web/multipart/support/AbstractMultipartHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * 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. @@ -61,7 +61,7 @@ public abstract class AbstractMultipartHttpServletRequest extends HttpServletReq @Override public HttpMethod getRequestMethod() { - return HttpMethod.valueOf(getRequest().getMethod()); + return HttpMethod.resolve(getRequest().getMethod()); } @Override diff --git a/spring-web/src/main/java/org/springframework/web/util/ContentCachingRequestWrapper.java b/spring-web/src/main/java/org/springframework/web/util/ContentCachingRequestWrapper.java index cfca663ad6..9b9a97cffe 100644 --- a/spring-web/src/main/java/org/springframework/web/util/ContentCachingRequestWrapper.java +++ b/spring-web/src/main/java/org/springframework/web/util/ContentCachingRequestWrapper.java @@ -30,6 +30,8 @@ import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; +import org.springframework.http.HttpMethod; + /** * {@link javax.servlet.http.HttpServletRequest} wrapper that caches all content read from * the {@linkplain #getInputStream() input stream} and {@linkplain #getReader() reader}, @@ -46,8 +48,6 @@ public class ContentCachingRequestWrapper extends HttpServletRequestWrapper { private static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded"; - private static final String METHOD_POST = "POST"; - private final ByteArrayOutputStream cachedContent; @@ -125,7 +125,7 @@ public class ContentCachingRequestWrapper extends HttpServletRequestWrapper { private boolean isFormPost() { String contentType = getContentType(); return (contentType != null && contentType.contains(FORM_CONTENT_TYPE) && - METHOD_POST.equalsIgnoreCase(getMethod())); + HttpMethod.POST.matches(getMethod())); } private void writeRequestParametersToCachedContent() { diff --git a/spring-web/src/test/java/org/springframework/mock/web/test/MockMultipartHttpServletRequest.java b/spring-web/src/test/java/org/springframework/mock/web/test/MockMultipartHttpServletRequest.java index a0c87ea3ac..7f218695a5 100644 --- a/spring-web/src/test/java/org/springframework/mock/web/test/MockMultipartHttpServletRequest.java +++ b/spring-web/src/test/java/org/springframework/mock/web/test/MockMultipartHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * 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. @@ -128,7 +128,7 @@ public class MockMultipartHttpServletRequest extends MockHttpServletRequest impl @Override public HttpMethod getRequestMethod() { - return HttpMethod.valueOf(getMethod()); + return HttpMethod.resolve(getMethod()); } @Override diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java index 706b3ab6a2..0143fdc868 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/DispatcherServlet.java @@ -1137,9 +1137,8 @@ public class DispatcherServlet extends FrameworkServlet { "] in DispatcherServlet with name '" + getServletName() + "'"); } if (this.throwExceptionIfNoHandlerFound) { - ServletServerHttpRequest sshr = new ServletServerHttpRequest(request); - throw new NoHandlerFoundException( - sshr.getMethod().name(), sshr.getServletRequest().getRequestURI(), sshr.getHeaders()); + throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request), + new ServletServerHttpRequest(request).getHeaders()); } else { response.sendError(HttpServletResponse.SC_NOT_FOUND); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java index 478495d5c1..e3d859fc0a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java @@ -42,6 +42,7 @@ import org.springframework.context.i18n.SimpleLocaleContext; import org.springframework.core.GenericTypeResolver; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.http.HttpMethod; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -831,15 +832,13 @@ public abstract class FrameworkServlet extends HttpServletBean implements Applic /** - * Override the parent class implementation in order to intercept PATCH - * requests. + * Override the parent class implementation in order to intercept PATCH requests. */ @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - String method = request.getMethod(); - if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) { + if (HttpMethod.PATCH.matches(request.getMethod())) { processRequest(request, response); } else { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java index d8399ebc3d..75c78a9105 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java @@ -22,8 +22,8 @@ import java.io.PushbackInputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; +import java.util.EnumSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -63,8 +63,8 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; */ public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver { - private static final List SUPPORTED_METHODS = - Arrays.asList(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH); + private static final Set SUPPORTED_METHODS = + EnumSet.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH); private static final Object NO_VALUE = new Object(); @@ -228,8 +228,8 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements } if (body == NO_VALUE) { - if (!SUPPORTED_METHODS.contains(httpMethod) - || (noContentType && inputMessage.getBody() == null)) { + if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) || + (noContentType && inputMessage.getBody() == null)) { return null; } throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java index 630a8c123e..7741b1c4db 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java @@ -19,7 +19,6 @@ package org.springframework.web.servlet.mvc.method.annotation; import java.io.IOException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.net.URI; import java.util.List; import org.springframework.core.MethodParameter; @@ -122,9 +121,8 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro Object body = readWithMessageConverters(webRequest, parameter, paramType); if (RequestEntity.class == parameter.getParameterType()) { - URI url = inputMessage.getURI(); - HttpMethod httpMethod = inputMessage.getMethod(); - return new RequestEntity(body, inputMessage.getHeaders(), httpMethod, url); + return new RequestEntity(body, inputMessage.getHeaders(), + inputMessage.getMethod(), inputMessage.getURI()); } else { return new HttpEntity(body, inputMessage.getHeaders()); @@ -172,8 +170,7 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro Object body = responseEntity.getBody(); if (responseEntity instanceof ResponseEntity) { outputMessage.setStatusCode(((ResponseEntity) responseEntity).getStatusCode()); - if (inputMessage.getMethod().equals(HttpMethod.GET) && - isResourceNotModified(inputMessage, outputMessage)) { + if (HttpMethod.GET == inputMessage.getMethod() && isResourceNotModified(inputMessage, outputMessage)) { outputMessage.setStatusCode(HttpStatus.NOT_MODIFIED); // Ensure headers are flushed, no body should be written. outputMessage.flush(); 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 0b8b99afaf..d76ed66d22 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 @@ -231,7 +231,7 @@ public abstract class AbstractHandshakeHandler implements HandshakeHandler, Life logger.trace("Processing request " + request.getURI() + " with headers=" + headers); } try { - if (!HttpMethod.GET.equals(request.getMethod())) { + if (HttpMethod.GET != request.getMethod()) { response.setStatusCode(HttpStatus.METHOD_NOT_ALLOWED); response.getHeaders().setAllow(Collections.singleton(HttpMethod.GET)); if (logger.isErrorEnabled()) { @@ -320,8 +320,8 @@ public abstract class AbstractHandshakeHandler implements HandshakeHandler, Life ". Supported versions: " + Arrays.toString(getSupportedVersions())); } response.setStatusCode(HttpStatus.UPGRADE_REQUIRED); - response.getHeaders().put(WebSocketHttpHeaders.SEC_WEBSOCKET_VERSION, - Arrays.asList(StringUtils.arrayToCommaDelimitedString(getSupportedVersions()))); + response.getHeaders().set(WebSocketHttpHeaders.SEC_WEBSOCKET_VERSION, + StringUtils.arrayToCommaDelimitedString(getSupportedVersions())); } /** diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java index 9c00716740..761671febc 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java @@ -544,7 +544,7 @@ public abstract class AbstractSockJsService implements SockJsService, CorsConfig @Override public void handle(ServerHttpRequest request, ServerHttpResponse response) throws IOException { - if (HttpMethod.GET.equals(request.getMethod())) { + if (HttpMethod.GET == request.getMethod()) { addNoCacheHeaders(response); if (checkOrigin(request, response)) { response.getHeaders().setContentType(new MediaType("application", "json", UTF8_CHARSET)); @@ -554,7 +554,7 @@ public abstract class AbstractSockJsService implements SockJsService, CorsConfig } } - else if (HttpMethod.OPTIONS.equals(request.getMethod())) { + else if (HttpMethod.OPTIONS == request.getMethod()) { if (checkOrigin(request, response)) { addCacheHeaders(response); response.setStatusCode(HttpStatus.NO_CONTENT); diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java index 811c0861b6..81d10a0b85 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportHandlingSockJsService.java @@ -247,8 +247,8 @@ public class TransportHandlingSockJsService extends AbstractSockJsService implem try { HttpMethod supportedMethod = transportType.getHttpMethod(); - if (!supportedMethod.equals(request.getMethod())) { - if (HttpMethod.OPTIONS.equals(request.getMethod()) && transportType.supportsCors()) { + if (supportedMethod != request.getMethod()) { + if (HttpMethod.OPTIONS == request.getMethod() && transportType.supportsCors()) { if (checkOrigin(request, response, HttpMethod.OPTIONS, supportedMethod)) { response.setStatusCode(HttpStatus.NO_CONTENT); addCacheHeaders(response);