diff options
Diffstat (limited to 'spring-web/src/main')
29 files changed, 284 insertions, 236 deletions
diff --git a/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java index 42a91192..d63d2870 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/AbstractHttpMessageConverter.java @@ -200,7 +200,7 @@ public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConv /** * Add default headers to the output message. * <p>This implementation delegates to {@link #getDefaultContentType(Object)} if a content - * type was not provided, calls {@link #getContentLength}, and sets the corresponding headers + * type was not provided, calls {@link #getContentLength}, and sets the corresponding headers. * @since 4.2 */ protected void addDefaultHeaders(HttpHeaders headers, T t, MediaType contentType) throws IOException{ diff --git a/spring-web/src/main/java/org/springframework/http/converter/ByteArrayHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/ByteArrayHttpMessageConverter.java index 87c003d7..12c95e14 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/ByteArrayHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/ByteArrayHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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,15 +32,19 @@ import org.springframework.util.StreamUtils; * overridden by setting the {@link #setSupportedMediaTypes supportedMediaTypes} property. * * @author Arjen Poutsma + * @author Juergen Hoeller * @since 3.0 */ public class ByteArrayHttpMessageConverter extends AbstractHttpMessageConverter<byte[]> { - /** Creates a new instance of the {@code ByteArrayHttpMessageConverter}. */ + /** + * Create a new instance of the {@code ByteArrayHttpMessageConverter}. + */ public ByteArrayHttpMessageConverter() { super(new MediaType("application", "octet-stream"), MediaType.ALL); } + @Override public boolean supports(Class<?> clazz) { return byte[].class == clazz; diff --git a/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java index 44491be0..fa2d0e4a 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -255,7 +255,7 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue Charset charset; if (contentType != null) { outputMessage.getHeaders().setContentType(contentType); - charset = contentType.getCharSet() != null ? contentType.getCharSet() : this.charset; + charset = (contentType.getCharSet() != null ? contentType.getCharSet() : this.charset); } else { outputMessage.getHeaders().setContentType(MediaType.APPLICATION_FORM_URLENCODED); diff --git a/spring-web/src/main/java/org/springframework/http/converter/ObjectToStringHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/ObjectToStringHttpMessageConverter.java index c1ef5c26..5fab4fec 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/ObjectToStringHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/ObjectToStringHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -29,11 +29,12 @@ import org.springframework.util.Assert; * An {@code HttpMessageConverter} that uses {@link StringHttpMessageConverter} * for reading and writing content and a {@link ConversionService} for converting * the String content to and from the target object type. - * <p> - * By default, this converter supports the media type {@code text/plain} only. - * This can be overridden by setting the - * {@link #setSupportedMediaTypes supportedMediaTypes} property. - * Example of usage: + * + * <p>By default, this converter supports the media type {@code text/plain} only. + * This can be overridden through the {@link #setSupportedMediaTypes supportedMediaTypes} + * property. + * + * <p>A usage example: * * <pre class="code"> * <bean class="org.springframework.http.converter.ObjectToStringHttpMessageConverter"> @@ -49,17 +50,15 @@ import org.springframework.util.Assert; */ public class ObjectToStringHttpMessageConverter extends AbstractHttpMessageConverter<Object> { - private ConversionService conversionService; + private final ConversionService conversionService; - private StringHttpMessageConverter stringHttpMessageConverter; + private final StringHttpMessageConverter stringHttpMessageConverter; /** * A constructor accepting a {@code ConversionService} to use to convert the - * (String) message body to/from the target class type. This constructor - * uses {@link StringHttpMessageConverter#DEFAULT_CHARSET} as the default - * charset. - * + * (String) message body to/from the target class type. This constructor uses + * {@link StringHttpMessageConverter#DEFAULT_CHARSET} as the default charset. * @param conversionService the conversion service */ public ObjectToStringHttpMessageConverter(ConversionService conversionService) { @@ -67,20 +66,19 @@ public class ObjectToStringHttpMessageConverter extends AbstractHttpMessageConve } /** - * A constructor accepting a {@code ConversionService} as well as a default - * charset. - * + * A constructor accepting a {@code ConversionService} as well as a default charset. * @param conversionService the conversion service * @param defaultCharset the default charset */ public ObjectToStringHttpMessageConverter(ConversionService conversionService, Charset defaultCharset) { super(new MediaType("text", "plain", defaultCharset)); - Assert.notNull(conversionService, "conversionService is required"); + Assert.notNull(conversionService, "ConversionService is required"); this.conversionService = conversionService; this.stringHttpMessageConverter = new StringHttpMessageConverter(defaultCharset); } + /** * Indicates whether the {@code Accept-Charset} should be written to any outgoing request. * <p>Default is {@code true}. @@ -89,6 +87,7 @@ public class ObjectToStringHttpMessageConverter extends AbstractHttpMessageConve this.stringHttpMessageConverter.setWriteAcceptCharset(writeAcceptCharset); } + @Override public boolean canRead(Class<?> clazz, MediaType mediaType) { return this.conversionService.canConvert(String.class, clazz) && canRead(mediaType); @@ -106,15 +105,15 @@ public class ObjectToStringHttpMessageConverter extends AbstractHttpMessageConve } @Override - protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException { + protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException { String value = this.stringHttpMessageConverter.readInternal(String.class, inputMessage); return this.conversionService.convert(value, clazz); } @Override protected void writeInternal(Object obj, HttpOutputMessage outputMessage) throws IOException { - String s = this.conversionService.convert(obj, String.class); - this.stringHttpMessageConverter.writeInternal(s, outputMessage); + String value = this.conversionService.convert(obj, String.class); + this.stringHttpMessageConverter.writeInternal(value, outputMessage); } @Override diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/SpringHandlerInstantiator.java b/spring-web/src/main/java/org/springframework/http/converter/json/SpringHandlerInstantiator.java index ef7446ae..f208c32a 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/SpringHandlerInstantiator.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/SpringHandlerInstantiator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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,14 +32,15 @@ import org.springframework.context.ApplicationContext; import org.springframework.util.Assert; /** - * Eventually get Jackson handler ({@link JsonSerializer}, {@link JsonDeserializer}, - * {@link KeyDeserializer}, {@link TypeResolverBuilder}, {@link TypeIdResolver}) beans by - * type from Spring {@link ApplicationContext}. If no bean is found, the default behavior - * happen (calling no-argument constructor via reflection). + * Allows for creating Jackson ({@link JsonSerializer}, {@link JsonDeserializer}, + * {@link KeyDeserializer}, {@link TypeResolverBuilder}, {@link TypeIdResolver}) + * beans with autowiring against a Spring {@link ApplicationContext}. * - * @since 4.1.3 * @author Sebastien Deleuze + * @author Juergen Hoeller + * @since 4.1.3 * @see Jackson2ObjectMapperBuilder#handlerInstantiator(HandlerInstantiator) + * @see ApplicationContext#getAutowireCapableBeanFactory() * @see HandlerInstantiator */ public class SpringHandlerInstantiator extends HandlerInstantiator { @@ -56,33 +57,30 @@ public class SpringHandlerInstantiator extends HandlerInstantiator { this.beanFactory = beanFactory; } + @Override - public JsonSerializer<?> serializerInstance(SerializationConfig config, - Annotated annotated, Class<?> keyDeserClass) { - return (JsonSerializer<?>) this.beanFactory.createBean(keyDeserClass); + public JsonDeserializer<?> deserializerInstance(DeserializationConfig config, Annotated annotated, Class<?> implClass) { + return (JsonDeserializer<?>) this.beanFactory.createBean(implClass); } @Override - public JsonDeserializer<?> deserializerInstance(DeserializationConfig config, - Annotated annotated, Class<?> deserClass) { - return (JsonDeserializer<?>) this.beanFactory.createBean(deserClass); + public KeyDeserializer keyDeserializerInstance(DeserializationConfig config, Annotated annotated, Class<?> implClass) { + return (KeyDeserializer) this.beanFactory.createBean(implClass); } @Override - public KeyDeserializer keyDeserializerInstance(DeserializationConfig config, - Annotated annotated, Class<?> serClass) { - return (KeyDeserializer) this.beanFactory.createBean(serClass); + public JsonSerializer<?> serializerInstance(SerializationConfig config, Annotated annotated, Class<?> implClass) { + return (JsonSerializer<?>) this.beanFactory.createBean(implClass); } @Override - public TypeResolverBuilder<?> typeResolverBuilderInstance(MapperConfig<?> config, - Annotated annotated, Class<?> resolverClass) { - return (TypeResolverBuilder<?>) this.beanFactory.createBean(resolverClass); + public TypeResolverBuilder<?> typeResolverBuilderInstance(MapperConfig<?> config, Annotated annotated, Class<?> implClass) { + return (TypeResolverBuilder<?>) this.beanFactory.createBean(implClass); } @Override - public TypeIdResolver typeIdResolverInstance(MapperConfig<?> config, - Annotated annotated, Class<?> resolverClass) { - return (TypeIdResolver) this.beanFactory.createBean(resolverClass); + public TypeIdResolver typeIdResolverInstance(MapperConfig<?> config, Annotated annotated, Class<?> implClass) { + return (TypeIdResolver) this.beanFactory.createBean(implClass); } + } diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/package-info.java b/spring-web/src/main/java/org/springframework/http/converter/json/package-info.java index c786a6d2..886aa944 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/package-info.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/package-info.java @@ -1,4 +1,4 @@ /** - * Provides an HttpMessageConverter implementations for handling JSON. + * Provides HttpMessageConverter implementations for handling JSON. */ package org.springframework.http.converter.json; diff --git a/spring-web/src/main/java/org/springframework/http/converter/protobuf/package-info.java b/spring-web/src/main/java/org/springframework/http/converter/protobuf/package-info.java new file mode 100644 index 00000000..3c73a272 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/http/converter/protobuf/package-info.java @@ -0,0 +1,5 @@ +/** + * Provides an HttpMessageConverter implementation for handling + * <a href="https://developers.google.com/protocol-buffers/">Google Protocol Buffers</a>. + */ +package org.springframework.http.converter.protobuf; diff --git a/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java b/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java index d93e6ff1..2edcbb8f 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java +++ b/spring-web/src/main/java/org/springframework/http/converter/support/AllEncompassingFormHttpMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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 @@ public class AllEncompassingFormHttpMessageConverter extends FormHttpMessageConv public AllEncompassingFormHttpMessageConverter() { addPartConverter(new SourceHttpMessageConverter<Source>()); - if (jaxb2Present && !jackson2Present) { + if (jaxb2Present && !jackson2XmlPresent) { addPartConverter(new Jaxb2RootElementHttpMessageConverter()); } diff --git a/spring-web/src/main/java/org/springframework/http/converter/support/package-info.java b/spring-web/src/main/java/org/springframework/http/converter/support/package-info.java new file mode 100644 index 00000000..eaaf8204 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/http/converter/support/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides a comprehensive HttpMessageConverter variant for form handling. + */ +package org.springframework.http.converter.support; diff --git a/spring-web/src/main/java/org/springframework/http/converter/xml/package-info.java b/spring-web/src/main/java/org/springframework/http/converter/xml/package-info.java index ef2c3c95..a889b351 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/xml/package-info.java +++ b/spring-web/src/main/java/org/springframework/http/converter/xml/package-info.java @@ -1,4 +1,4 @@ /** - * Provides an HttpMessageConverter implementations for handling XML. + * Provides HttpMessageConverter implementations for handling XML. */ package org.springframework.http.converter.xml; 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 6fda91d2..4a8f342a 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 @@ -37,6 +37,7 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; +import org.springframework.http.InvalidMediaTypeException; import org.springframework.http.MediaType; import org.springframework.util.Assert; import org.springframework.util.LinkedCaseInsensitiveMap; @@ -104,6 +105,7 @@ public class ServletServerHttpRequest implements ServerHttpRequest { public HttpHeaders getHeaders() { if (this.headers == null) { this.headers = new HttpHeaders(); + for (Enumeration<?> headerNames = this.servletRequest.getHeaderNames(); headerNames.hasMoreElements();) { String headerName = (String) headerNames.nextElement(); for (Enumeration<?> headerValues = this.servletRequest.getHeaders(headerName); @@ -112,26 +114,33 @@ public class ServletServerHttpRequest implements ServerHttpRequest { this.headers.add(headerName, headerValue); } } + // HttpServletRequest exposes some headers as properties: we should include those if not already present - MediaType contentType = this.headers.getContentType(); - if (contentType == null) { - String requestContentType = this.servletRequest.getContentType(); - if (StringUtils.hasLength(requestContentType)) { - contentType = MediaType.parseMediaType(requestContentType); - this.headers.setContentType(contentType); + try { + MediaType contentType = this.headers.getContentType(); + if (contentType == null) { + String requestContentType = this.servletRequest.getContentType(); + if (StringUtils.hasLength(requestContentType)) { + contentType = MediaType.parseMediaType(requestContentType); + this.headers.setContentType(contentType); + } } - } - if (contentType != null && contentType.getCharSet() == null) { - String requestEncoding = this.servletRequest.getCharacterEncoding(); - if (StringUtils.hasLength(requestEncoding)) { - Charset charSet = Charset.forName(requestEncoding); - Map<String, String> params = new LinkedCaseInsensitiveMap<String>(); - params.putAll(contentType.getParameters()); - params.put("charset", charSet.toString()); - MediaType newContentType = new MediaType(contentType.getType(), contentType.getSubtype(), params); - this.headers.setContentType(newContentType); + if (contentType != null && contentType.getCharSet() == null) { + String requestEncoding = this.servletRequest.getCharacterEncoding(); + if (StringUtils.hasLength(requestEncoding)) { + Charset charSet = Charset.forName(requestEncoding); + Map<String, String> params = new LinkedCaseInsensitiveMap<String>(); + params.putAll(contentType.getParameters()); + params.put("charset", charSet.toString()); + MediaType newContentType = new MediaType(contentType.getType(), contentType.getSubtype(), params); + this.headers.setContentType(newContentType); + } } } + catch (InvalidMediaTypeException ex) { + // Ignore: simply not exposing an invalid content type in HttpHeaders... + } + if (this.headers.getContentLength() < 0) { int requestContentLength = this.servletRequest.getContentLength(); if (requestContentLength != -1) { @@ -139,6 +148,7 @@ public class ServletServerHttpRequest implements ServerHttpRequest { } } } + return this.headers; } diff --git a/spring-web/src/main/java/org/springframework/web/accept/AbstractMappingContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/AbstractMappingContentNegotiationStrategy.java index 0568b904..ee83d31f 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/AbstractMappingContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/AbstractMappingContentNegotiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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,11 +43,9 @@ import org.springframework.web.context.request.NativeWebRequest; * @author Rossen Stoyanchev * @since 3.2 */ -public abstract class AbstractMappingContentNegotiationStrategy - extends MappingMediaTypeFileExtensionResolver +public abstract class AbstractMappingContentNegotiationStrategy extends MappingMediaTypeFileExtensionResolver implements ContentNegotiationStrategy { - /** * Create an instance with the given map of file extensions and media types. */ @@ -86,6 +84,7 @@ public abstract class AbstractMappingContentNegotiationStrategy return Collections.emptyList(); } + /** * Extract a key from the request to use to look up media types. * @return the lookup key or {@code null}. diff --git a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManager.java b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManager.java index 4450ad10..3f6efab3 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManager.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -41,18 +41,14 @@ import org.springframework.web.context.request.NativeWebRequest; * @author Rossen Stoyanchev * @since 3.2 */ -public class ContentNegotiationManager implements ContentNegotiationStrategy, - MediaTypeFileExtensionResolver { +public class ContentNegotiationManager implements ContentNegotiationStrategy, MediaTypeFileExtensionResolver { - private static final List<MediaType> MEDIA_TYPE_ALL = - Collections.<MediaType>singletonList(MediaType.ALL); + private static final List<MediaType> MEDIA_TYPE_ALL = Collections.<MediaType>singletonList(MediaType.ALL); - private final List<ContentNegotiationStrategy> strategies = - new ArrayList<ContentNegotiationStrategy>(); + private final List<ContentNegotiationStrategy> strategies = new ArrayList<ContentNegotiationStrategy>(); - private final Set<MediaTypeFileExtensionResolver> resolvers = - new LinkedHashSet<MediaTypeFileExtensionResolver>(); + private final Set<MediaTypeFileExtensionResolver> resolvers = new LinkedHashSet<MediaTypeFileExtensionResolver>(); /** diff --git a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java index b035b079..dfcd8499 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -40,34 +40,34 @@ import org.springframework.web.context.ServletContextAware; * * <table> * <tr> - * <th>Property Setter</th> - * <th>Underlying Strategy</th> - * <th>Default Setting</th> + * <th>Property Setter</th> + * <th>Underlying Strategy</th> + * <th>Default Setting</th> * </tr> * <tr> - * <td>{@link #setFavorPathExtension}</td> - * <td>{@link PathExtensionContentNegotiationStrategy Path Extension strategy}</td> - * <td>On</td> + * <td>{@link #setFavorPathExtension}</td> + * <td>{@link PathExtensionContentNegotiationStrategy Path Extension strategy}</td> + * <td>On</td> * </tr> * <tr> - * <td>{@link #setFavorParameter favorParameter}</td> - * <td>{@link ParameterContentNegotiationStrategy Parameter strategy}</td> - * <td>Off</td> + * <td>{@link #setFavorParameter favorParameter}</td> + * <td>{@link ParameterContentNegotiationStrategy Parameter strategy}</td> + * <td>Off</td> * </tr> * <tr> - * <td>{@link #setIgnoreAcceptHeader ignoreAcceptHeader}</td> - * <td>{@link HeaderContentNegotiationStrategy Header strategy}</td> - * <td>On</td> + * <td>{@link #setIgnoreAcceptHeader ignoreAcceptHeader}</td> + * <td>{@link HeaderContentNegotiationStrategy Header strategy}</td> + * <td>On</td> * </tr> * <tr> - * <td>{@link #setDefaultContentType defaultContentType}</td> - * <td>{@link FixedContentNegotiationStrategy Fixed content strategy}</td> - * <td>Not set</td> + * <td>{@link #setDefaultContentType defaultContentType}</td> + * <td>{@link FixedContentNegotiationStrategy Fixed content strategy}</td> + * <td>Not set</td> * </tr> * <tr> - * <td>{@link #setDefaultContentTypeStrategy defaultContentTypeStrategy}</td> - * <td>{@link ContentNegotiationStrategy}</td> - * <td>Not set</td> + * <td>{@link #setDefaultContentTypeStrategy defaultContentTypeStrategy}</td> + * <td>{@link ContentNegotiationStrategy}</td> + * <td>Not set</td> * </tr> * </table> * diff --git a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationStrategy.java index 127f90ce..586485a7 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -33,10 +33,8 @@ public interface ContentNegotiationStrategy { /** * Resolve the given request to a list of media types. The returned list is * ordered by specificity first and by quality parameter second. - * * @param webRequest the current request - * @return the requested media types or an empty list, never {@code null} - * + * @return the requested media types or an empty list (never {@code null}) * @throws HttpMediaTypeNotAcceptableException if the requested media * types cannot be parsed */ diff --git a/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java index 547ccef7..50478683 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/FixedContentNegotiationStrategy.java @@ -33,8 +33,7 @@ import org.springframework.web.context.request.NativeWebRequest; */ public class FixedContentNegotiationStrategy implements ContentNegotiationStrategy { - private static final Log logger = LogFactory.getLog( - FixedContentNegotiationStrategy.class); + private static final Log logger = LogFactory.getLog(FixedContentNegotiationStrategy.class); private final List<MediaType> contentType; @@ -50,7 +49,7 @@ public class FixedContentNegotiationStrategy implements ContentNegotiationStrate @Override public List<MediaType> resolveMediaTypes(NativeWebRequest request) { if (logger.isDebugEnabled()) { - logger.debug("Requested media types is " + this.contentType + "."); + logger.debug("Requested media types: " + this.contentType); } return this.contentType; } diff --git a/spring-web/src/main/java/org/springframework/web/accept/HeaderContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/HeaderContentNegotiationStrategy.java index bcefd727..2642853d 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/HeaderContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/HeaderContentNegotiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -34,11 +34,9 @@ import org.springframework.web.context.request.NativeWebRequest; */ public class HeaderContentNegotiationStrategy implements ContentNegotiationStrategy { - /** * {@inheritDoc} - * @throws HttpMediaTypeNotAcceptableException if the 'Accept' header - * cannot be parsed. + * @throws HttpMediaTypeNotAcceptableException if the 'Accept' header cannot be parsed */ @Override public List<MediaType> resolveMediaTypes(NativeWebRequest request) diff --git a/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java b/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java index 6c2c8c7d..49c38d6d 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java +++ b/spring-web/src/main/java/org/springframework/web/accept/MappingMediaTypeFileExtensionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -35,7 +35,7 @@ import org.springframework.util.MultiValueMap; * lookups between file extensions and MediaTypes in both directions. * * <p>Initially created with a map of file extensions and media types. - * Subsequently sub-classes can use {@link #addMapping} to add more mappings. + * Subsequently subclasses can use {@link #addMapping} to add more mappings. * * @author Rossen Stoyanchev * @since 3.2 diff --git a/spring-web/src/main/java/org/springframework/web/accept/MediaTypeFileExtensionResolver.java b/spring-web/src/main/java/org/springframework/web/accept/MediaTypeFileExtensionResolver.java index c063e5c5..49a7fcd9 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/MediaTypeFileExtensionResolver.java +++ b/spring-web/src/main/java/org/springframework/web/accept/MediaTypeFileExtensionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -31,15 +31,14 @@ public interface MediaTypeFileExtensionResolver { /** * Resolve the given media type to a list of path extensions. - * * @param mediaType the media type to resolve - * @return a list of extensions or an empty list, never {@code null} + * @return a list of extensions or an empty list (never {@code null}) */ List<String> resolveFileExtensions(MediaType mediaType); /** * Return all registered file extensions. - * @return a list of extensions or an empty list, never {@code null} + * @return a list of extensions or an empty list (never {@code null}) */ List<String> getAllFileExtensions(); diff --git a/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java index a2520d27..f6a9cb4e 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ParameterContentNegotiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -27,18 +27,15 @@ import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.context.request.NativeWebRequest; /** - * A {@code ContentNegotiationStrategy} that resolves a query parameter to a - * key to be used to look up a media type. The default parameter name is - * {@code format}. - *s + * A {@code ContentNegotiationStrategy} that resolves a query parameter to a key + * to be used to look up a media type. The default parameter name is {@code format}. + * * @author Rossen Stoyanchev * @since 3.2 */ -public class ParameterContentNegotiationStrategy - extends AbstractMappingContentNegotiationStrategy { +public class ParameterContentNegotiationStrategy extends AbstractMappingContentNegotiationStrategy { - private static final Log logger = LogFactory.getLog( - ParameterContentNegotiationStrategy.class); + private static final Log logger = LogFactory.getLog(ParameterContentNegotiationStrategy.class); private String parameterName = "format"; @@ -56,7 +53,7 @@ public class ParameterContentNegotiationStrategy * <p>By default this is set to {@code "format"}. */ public void setParameterName(String parameterName) { - Assert.notNull(parameterName, "parameterName is required"); + Assert.notNull(parameterName, "'parameterName' is required"); this.parameterName = parameterName; } @@ -64,6 +61,7 @@ public class ParameterContentNegotiationStrategy return this.parameterName; } + @Override protected String getMediaTypeKey(NativeWebRequest request) { return request.getParameter(getParameterName()); @@ -72,8 +70,8 @@ public class ParameterContentNegotiationStrategy @Override protected void handleMatch(String mediaTypeKey, MediaType mediaType) { if (logger.isDebugEnabled()) { - logger.debug("Requested media type is '" + mediaType + - "' based on '" + getParameterName() + "'='" + mediaTypeKey + "'."); + logger.debug("Requested media type: '" + mediaType + "' based on '" + + getParameterName() + "'='" + mediaTypeKey + "'"); } } diff --git a/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java index 9b19c271..9f0c2672 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -56,8 +56,7 @@ public class PathExtensionContentNegotiationStrategy private static final Log logger = LogFactory.getLog(PathExtensionContentNegotiationStrategy.class); - private static final boolean JAF_PRESENT = ClassUtils.isPresent( - "javax.activation.FileTypeMap", + private static final boolean JAF_PRESENT = ClassUtils.isPresent("javax.activation.FileTypeMap", PathExtensionContentNegotiationStrategy.class.getClassLoader()); private static final UrlPathHelper PATH_HELPER = new UrlPathHelper(); diff --git a/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java index 20338710..615568b4 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.web.accept; import java.util.Map; @@ -31,8 +32,7 @@ import org.springframework.web.context.request.NativeWebRequest; * @author Rossen Stoyanchev * @since 3.2 */ -public class ServletPathExtensionContentNegotiationStrategy - extends PathExtensionContentNegotiationStrategy { +public class ServletPathExtensionContentNegotiationStrategy extends PathExtensionContentNegotiationStrategy { private final ServletContext servletContext; @@ -40,12 +40,12 @@ public class ServletPathExtensionContentNegotiationStrategy /** * Create an instance with the given extension-to-MediaType lookup. */ - public ServletPathExtensionContentNegotiationStrategy(ServletContext context, - Map<String, MediaType> mediaTypes) { + public ServletPathExtensionContentNegotiationStrategy( + ServletContext servletContext, Map<String, MediaType> mediaTypes) { super(mediaTypes); - Assert.notNull(context, "ServletContext is required!"); - this.servletContext = context; + Assert.notNull(servletContext, "ServletContext is required"); + this.servletContext = servletContext; } /** diff --git a/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java b/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java index ae75fbe4..26838d39 100644 --- a/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java +++ b/spring-web/src/main/java/org/springframework/web/context/support/WebApplicationContextUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -288,7 +288,7 @@ public abstract class WebApplicationContextUtils { public static void initServletPropertySources( MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) { - Assert.notNull(propertySources, "propertySources must not be null"); + Assert.notNull(propertySources, "'propertySources' must not be null"); if (servletContext != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) && propertySources.get(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) { propertySources.replace(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, diff --git a/spring-web/src/main/java/org/springframework/web/cors/CorsConfigurationSource.java b/spring-web/src/main/java/org/springframework/web/cors/CorsConfigurationSource.java index 2fc6d9ce..48166caa 100644 --- a/spring-web/src/main/java/org/springframework/web/cors/CorsConfigurationSource.java +++ b/spring-web/src/main/java/org/springframework/web/cors/CorsConfigurationSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -29,6 +29,7 @@ public interface CorsConfigurationSource { /** * Return a {@link CorsConfiguration} based on the incoming request. + * @return the associated {@link CorsConfiguration}, or {@code null} if none */ CorsConfiguration getCorsConfiguration(HttpServletRequest request); diff --git a/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java b/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java index 607b8bc0..4062bf6d 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/AbstractRequestLoggingFilter.java @@ -46,7 +46,7 @@ import org.springframework.web.util.WebUtils; * * <p>Prefixes and suffixes for the before and after messages can be configured using the * {@code beforeMessagePrefix}, {@code afterMessagePrefix}, {@code beforeMessageSuffix} and - * {@code afterMessageSuffix} properties, + * {@code afterMessageSuffix} properties. * * @author Rob Harrop * @author Juergen Hoeller @@ -124,7 +124,6 @@ public abstract class AbstractRequestLoggingFilter extends OncePerRequestFilter * <p>Should be configured using an {@code <init-param>} for parameter name * "includePayload" in the filter definition in {@code web.xml}. */ - public void setIncludePayload(boolean includePayload) { this.includePayload = includePayload; } @@ -254,12 +253,14 @@ public abstract class AbstractRequestLoggingFilter extends OncePerRequestFilter StringBuilder msg = new StringBuilder(); msg.append(prefix); msg.append("uri=").append(request.getRequestURI()); + if (isIncludeQueryString()) { String queryString = request.getQueryString(); if (queryString != null) { msg.append('?').append(queryString); } } + if (isIncludeClientInfo()) { String client = request.getRemoteAddr(); if (StringUtils.hasLength(client)) { @@ -274,6 +275,7 @@ public abstract class AbstractRequestLoggingFilter extends OncePerRequestFilter msg.append(";user=").append(user); } } + if (isIncludePayload()) { ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class); @@ -292,6 +294,7 @@ public abstract class AbstractRequestLoggingFilter extends OncePerRequestFilter } } } + msg.append(suffix); return msg.toString(); } diff --git a/spring-web/src/main/java/org/springframework/web/filter/CorsFilter.java b/spring-web/src/main/java/org/springframework/web/filter/CorsFilter.java index 8079532e..812deb0a 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/CorsFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/CorsFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -31,10 +31,10 @@ import org.springframework.web.cors.DefaultCorsProcessor; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; /** - * {@link javax.servlet.Filter} that handles CORS preflight requests and intercepts CORS - * simple and actual requests thanks to a {@link CorsProcessor} implementation - * ({@link DefaultCorsProcessor} by default) in order to add the relevant CORS response - * headers (like {@code Access-Control-Allow-Origin}) using the provided + * {@link javax.servlet.Filter} that handles CORS preflight requests and intercepts + * CORS simple and actual requests thanks to a {@link CorsProcessor} implementation + * ({@link DefaultCorsProcessor} by default) in order to add the relevant CORS + * response headers (like {@code Access-Control-Allow-Origin}) using the provided * {@link CorsConfigurationSource} (for example an {@link UrlBasedCorsConfigurationSource} * instance. * @@ -52,20 +52,22 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; */ public class CorsFilter extends OncePerRequestFilter { - private CorsProcessor processor = new DefaultCorsProcessor(); - private final CorsConfigurationSource configSource; + private CorsProcessor processor = new DefaultCorsProcessor(); + /** - * Constructor accepting a {@link CorsConfigurationSource} used by the filter to find - * the {@link CorsConfiguration} to use for each incoming request. + * Constructor accepting a {@link CorsConfigurationSource} used by the filter + * to find the {@link CorsConfiguration} to use for each incoming request. * @see UrlBasedCorsConfigurationSource */ public CorsFilter(CorsConfigurationSource configSource) { + Assert.notNull(configSource, "CorsConfigurationSource must not be null"); this.configSource = configSource; } + /** * Configure a custom {@link CorsProcessor} to use to apply the matched * {@link CorsConfiguration} for a request. @@ -76,6 +78,7 @@ public class CorsFilter extends OncePerRequestFilter { this.processor = processor; } + @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { @@ -89,6 +92,7 @@ public class CorsFilter extends OncePerRequestFilter { } } } + filterChain.doFilter(request, response); } diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolver.java b/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolver.java index aba8999a..c481b152 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolver.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMethodArgumentResolver.java @@ -54,8 +54,8 @@ import org.springframework.web.util.WebUtils; * abstraction, and arguments of type {@code javax.servlet.http.Part} in conjunction * with Servlet 3.0 multipart requests. This resolver can also be created in default * resolution mode in which simple types (int, long, etc.) not annotated with - * @{@link RequestParam} are also treated as request parameters with the - * parameter name derived from the argument name. + * {@link RequestParam @RequestParam} are also treated as request parameters with + * the parameter name derived from the argument name. * * <p>If the method parameter type is {@link Map}, the name specified in the * annotation is used to resolve the request parameter String value. The value is diff --git a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java index 31c0162e..74d0105e 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriComponentsBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -24,6 +24,7 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpRequest; import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; @@ -130,7 +131,7 @@ public class UriComponentsBuilder implements Cloneable { this.userInfo = other.userInfo; this.host = other.host; this.port = other.port; - this.pathBuilder = (CompositePathComponentBuilder) other.pathBuilder.clone(); + this.pathBuilder = other.pathBuilder.cloneBuilder(); this.queryParams.putAll(other.queryParams); this.fragment = other.fragment; } @@ -271,72 +272,17 @@ public class UriComponentsBuilder implements Cloneable { /** * Create a new {@code UriComponents} object from the URI associated with * the given HttpRequest while also overlaying with values from the headers - * "Forwarded" (<a href="http://tools.ietf.org/html/rfc7239">RFC 7239</a>, or - * "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if "Forwarded" is - * not found. + * "Forwarded" (<a href="http://tools.ietf.org/html/rfc7239">RFC 7239</a>, + * or "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if + * "Forwarded" is not found. * @param request the source request * @return the URI components of the URI * @since 4.1.5 */ public static UriComponentsBuilder fromHttpRequest(HttpRequest request) { - URI uri = request.getURI(); - UriComponentsBuilder builder = UriComponentsBuilder.fromUri(uri); - - String scheme = uri.getScheme(); - String host = uri.getHost(); - int port = uri.getPort(); - - String forwardedHeader = request.getHeaders().getFirst("Forwarded"); - if (StringUtils.hasText(forwardedHeader)) { - String forwardedToUse = StringUtils.commaDelimitedListToStringArray(forwardedHeader)[0]; - Matcher m = FORWARDED_HOST_PATTERN.matcher(forwardedToUse); - if (m.find()) { - host = m.group(1).trim(); - } - m = FORWARDED_PROTO_PATTERN.matcher(forwardedToUse); - if (m.find()) { - scheme = m.group(1).trim(); - } - } - else { - String hostHeader = request.getHeaders().getFirst("X-Forwarded-Host"); - if (StringUtils.hasText(hostHeader)) { - String[] hosts = StringUtils.commaDelimitedListToStringArray(hostHeader); - String hostToUse = hosts[0]; - if (hostToUse.contains(":")) { - String[] hostAndPort = StringUtils.split(hostToUse, ":"); - host = hostAndPort[0]; - port = Integer.parseInt(hostAndPort[1]); - } - else { - host = hostToUse; - port = -1; - } - } - - String portHeader = request.getHeaders().getFirst("X-Forwarded-Port"); - if (StringUtils.hasText(portHeader)) { - String[] ports = StringUtils.commaDelimitedListToStringArray(portHeader); - port = Integer.parseInt(ports[0]); - } - - String protocolHeader = request.getHeaders().getFirst("X-Forwarded-Proto"); - if (StringUtils.hasText(protocolHeader)) { - String[] protocols = StringUtils.commaDelimitedListToStringArray(protocolHeader); - scheme = protocols[0]; - } - } - - builder.scheme(scheme); - builder.host(host); - builder.port(null); - if (scheme.equals("http") && port != 80 || scheme.equals("https") && port != 443) { - builder.port(port); - } - return builder; + return fromUri(request.getURI()).adaptFromForwardedHeaders(request.getHeaders()); } - /** * Create an instance by parsing the "Origin" header of an HTTP request. * @see <a href="https://tools.ietf.org/html/rfc6454">RFC 6454</a> @@ -463,18 +409,6 @@ public class UriComponentsBuilder implements Cloneable { return this; } - private void resetHierarchicalComponents() { - this.userInfo = null; - this.host = null; - this.port = null; - this.pathBuilder = new CompositePathComponentBuilder(); - this.queryParams.clear(); - } - - private void resetSchemeSpecificPart() { - this.ssp = null; - } - /** * Set the URI scheme. The given scheme may contain URI template variables, * and may also be {@code null} to clear the scheme of this builder. @@ -724,17 +658,103 @@ public class UriComponentsBuilder implements Cloneable { return this; } + /** + * Adapt this builder's scheme+host+port from the given headers, specifically + * "Forwarded" (<a href="http://tools.ietf.org/html/rfc7239">RFC 7239</a>, + * or "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" if + * "Forwarded" is not found. + * @param headers the HTTP headers to consider + * @return this UriComponentsBuilder + * @since 4.2.7 + */ + UriComponentsBuilder adaptFromForwardedHeaders(HttpHeaders headers) { + String forwardedHeader = headers.getFirst("Forwarded"); + if (StringUtils.hasText(forwardedHeader)) { + String forwardedToUse = StringUtils.commaDelimitedListToStringArray(forwardedHeader)[0]; + Matcher matcher = FORWARDED_HOST_PATTERN.matcher(forwardedToUse); + if (matcher.find()) { + host(matcher.group(1).trim()); + } + matcher = FORWARDED_PROTO_PATTERN.matcher(forwardedToUse); + if (matcher.find()) { + scheme(matcher.group(1).trim()); + } + } + else { + String hostHeader = headers.getFirst("X-Forwarded-Host"); + if (StringUtils.hasText(hostHeader)) { + String[] hosts = StringUtils.commaDelimitedListToStringArray(hostHeader); + String hostToUse = hosts[0]; + if (hostToUse.contains(":")) { + String[] hostAndPort = StringUtils.split(hostToUse, ":"); + host(hostAndPort[0]); + port(Integer.parseInt(hostAndPort[1])); + } + else { + host(hostToUse); + port(null); + } + } + + String portHeader = headers.getFirst("X-Forwarded-Port"); + if (StringUtils.hasText(portHeader)) { + String[] ports = StringUtils.commaDelimitedListToStringArray(portHeader); + port(Integer.parseInt(ports[0])); + } + + String protocolHeader = headers.getFirst("X-Forwarded-Proto"); + if (StringUtils.hasText(protocolHeader)) { + String[] protocols = StringUtils.commaDelimitedListToStringArray(protocolHeader); + scheme(protocols[0]); + } + } + + if ((this.scheme.equals("http") && "80".equals(this.port)) || + (this.scheme.equals("https") && "443".equals(this.port))) { + this.port = null; + } + + return this; + } + + private void resetHierarchicalComponents() { + this.userInfo = null; + this.host = null; + this.port = null; + this.pathBuilder = new CompositePathComponentBuilder(); + this.queryParams.clear(); + } + + private void resetSchemeSpecificPart() { + this.ssp = null; + } + + + /** + * Public declaration of Object's {@code clone()} method. + * Delegates to {@link #cloneBuilder()}. + * @see Object#clone() + */ @Override public Object clone() { + return cloneBuilder(); + } + + /** + * Clone this {@code UriComponentsBuilder}. + * @return the cloned {@code UriComponentsBuilder} object + * @since 4.2.7 + */ + public UriComponentsBuilder cloneBuilder() { return new UriComponentsBuilder(this); } - private interface PathComponentBuilder extends Cloneable { + private interface PathComponentBuilder { PathComponent build(); - Object clone(); + PathComponentBuilder cloneBuilder(); } @@ -810,10 +830,10 @@ public class UriComponentsBuilder implements Cloneable { } @Override - public Object clone() { + public CompositePathComponentBuilder cloneBuilder() { CompositePathComponentBuilder compositeBuilder = new CompositePathComponentBuilder(); for (PathComponentBuilder builder : this.builders) { - compositeBuilder.builders.add((PathComponentBuilder) builder.clone()); + compositeBuilder.builders.add(builder.cloneBuilder()); } return compositeBuilder; } @@ -852,7 +872,7 @@ public class UriComponentsBuilder implements Cloneable { } @Override - public Object clone() { + public FullPathComponentBuilder cloneBuilder() { FullPathComponentBuilder builder = new FullPathComponentBuilder(); builder.append(this.path.toString()); return builder; @@ -879,7 +899,7 @@ public class UriComponentsBuilder implements Cloneable { } @Override - public Object clone() { + public PathSegmentComponentBuilder cloneBuilder() { PathSegmentComponentBuilder builder = new PathSegmentComponentBuilder(); builder.pathSegments.addAll(this.pathSegments); return builder; diff --git a/spring-web/src/main/java/org/springframework/web/util/WebUtils.java b/spring-web/src/main/java/org/springframework/web/util/WebUtils.java index 76c5dd04..6d8062fb 100644 --- a/spring-web/src/main/java/org/springframework/web/util/WebUtils.java +++ b/spring-web/src/main/java/org/springframework/web/util/WebUtils.java @@ -34,6 +34,7 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.http.HttpRequest; +import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; @@ -811,18 +812,31 @@ public abstract class WebUtils { if (origin == null) { return true; } - UriComponents actualUrl = UriComponentsBuilder.fromHttpRequest(request).build(); + UriComponentsBuilder urlBuilder; + if (request instanceof ServletServerHttpRequest) { + // Build more efficiently if we can: we only need scheme, host, port for origin comparison + HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest(); + urlBuilder = new UriComponentsBuilder(). + scheme(servletRequest.getScheme()). + host(servletRequest.getServerName()). + port(servletRequest.getServerPort()). + adaptFromForwardedHeaders(request.getHeaders()); + } + else { + urlBuilder = UriComponentsBuilder.fromHttpRequest(request); + } + UriComponents actualUrl = urlBuilder.build(); UriComponents originUrl = UriComponentsBuilder.fromOriginHeader(origin).build(); return (actualUrl.getHost().equals(originUrl.getHost()) && getPort(actualUrl) == getPort(originUrl)); } - private static int getPort(UriComponents component) { - int port = component.getPort(); + private static int getPort(UriComponents uri) { + int port = uri.getPort(); if (port == -1) { - if ("http".equals(component.getScheme()) || "ws".equals(component.getScheme())) { + if ("http".equals(uri.getScheme()) || "ws".equals(uri.getScheme())) { port = 80; } - else if ("https".equals(component.getScheme()) || "wss".equals(component.getScheme())) { + else if ("https".equals(uri.getScheme()) || "wss".equals(uri.getScheme())) { port = 443; } } |