mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:39:29 +00:00
Consistent adaptation of HTTP headers on Servlet responses
Closes gh-36345
This commit is contained in:
+2
-3
@@ -187,10 +187,10 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
|
|||||||
if (contentType != null && contentType.getCharset() == null) {
|
if (contentType != null && contentType.getCharset() == null) {
|
||||||
String requestEncoding = this.servletRequest.getCharacterEncoding();
|
String requestEncoding = this.servletRequest.getCharacterEncoding();
|
||||||
if (StringUtils.hasLength(requestEncoding)) {
|
if (StringUtils.hasLength(requestEncoding)) {
|
||||||
Charset charSet = Charset.forName(requestEncoding);
|
Charset charset = Charset.forName(requestEncoding);
|
||||||
Map<String, String> params = new LinkedCaseInsensitiveMap<>();
|
Map<String, String> params = new LinkedCaseInsensitiveMap<>();
|
||||||
params.putAll(contentType.getParameters());
|
params.putAll(contentType.getParameters());
|
||||||
params.put("charset", charSet.toString());
|
params.put("charset", charset.toString());
|
||||||
MediaType mediaType = new MediaType(contentType.getType(), contentType.getSubtype(), params);
|
MediaType mediaType = new MediaType(contentType.getType(), contentType.getSubtype(), params);
|
||||||
this.headers.setContentType(mediaType);
|
this.headers.setContentType(mediaType);
|
||||||
}
|
}
|
||||||
@@ -207,7 +207,6 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.headers;
|
return this.headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
-7
@@ -18,6 +18,7 @@ package org.springframework.http.server;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -118,19 +119,31 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
|
|||||||
this.servletResponse.addHeader(headerName, headerValue);
|
this.servletResponse.addHeader(headerName, headerValue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// HttpServletResponse exposes some headers as properties: we should include those if not already present
|
// HttpServletResponse exposes some headers as properties: we should include those if not already present
|
||||||
MediaType contentTypeHeader = this.headers.getContentType();
|
if (this.servletResponse.getContentType() == null && this.headers.containsKey(HttpHeaders.CONTENT_TYPE)) {
|
||||||
if (this.servletResponse.getContentType() == null && contentTypeHeader != null) {
|
this.servletResponse.setContentType(this.headers.getFirst(HttpHeaders.CONTENT_TYPE));
|
||||||
this.servletResponse.setContentType(contentTypeHeader.toString());
|
|
||||||
}
|
}
|
||||||
if (this.servletResponse.getCharacterEncoding() == null && contentTypeHeader != null &&
|
if (this.servletResponse.getCharacterEncoding() == null && this.headers.containsKey(HttpHeaders.CONTENT_TYPE)) {
|
||||||
contentTypeHeader.getCharset() != null) {
|
try {
|
||||||
this.servletResponse.setCharacterEncoding(contentTypeHeader.getCharset().name());
|
// Lazy parsing into MediaType
|
||||||
|
MediaType contentType = this.headers.getContentType();
|
||||||
|
if (contentType != null) {
|
||||||
|
Charset charset = contentType.getCharset();
|
||||||
|
if (charset != null) {
|
||||||
|
this.servletResponse.setCharacterEncoding(charset.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
// Leave character encoding unspecified
|
||||||
|
}
|
||||||
}
|
}
|
||||||
long contentLength = getHeaders().getContentLength();
|
long contentLength = this.headers.getContentLength();
|
||||||
if (contentLength != -1) {
|
if (contentLength != -1) {
|
||||||
this.servletResponse.setContentLengthLong(contentLength);
|
this.servletResponse.setContentLengthLong(contentLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.headersWritten = true;
|
this.headersWritten = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-21
@@ -50,7 +50,9 @@ import org.springframework.util.ReflectionUtils;
|
|||||||
*/
|
*/
|
||||||
class ServletServerHttpResponse extends AbstractListenerServerHttpResponse {
|
class ServletServerHttpResponse extends AbstractListenerServerHttpResponse {
|
||||||
|
|
||||||
private static final boolean IS_SERVLET61 = ReflectionUtils.findField(HttpServletResponse.class, "SC_PERMANENT_REDIRECT") != null;
|
private static final boolean SERVLET61 =
|
||||||
|
(ReflectionUtils.findField(HttpServletResponse.class, "SC_PERMANENT_REDIRECT") != null);
|
||||||
|
|
||||||
|
|
||||||
private final HttpServletResponse response;
|
private final HttpServletResponse response;
|
||||||
|
|
||||||
@@ -138,31 +140,35 @@ class ServletServerHttpResponse extends AbstractListenerServerHttpResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void adaptHeaders(boolean removeAdaptedHeaders) {
|
protected void adaptHeaders(boolean removeAdaptedHeaders) {
|
||||||
MediaType contentType = null;
|
HttpHeaders headers = getHeaders();
|
||||||
try {
|
|
||||||
contentType = getHeaders().getContentType();
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
String rawContentType = getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
|
|
||||||
this.response.setContentType(rawContentType);
|
|
||||||
}
|
|
||||||
if (this.response.getContentType() == null && contentType != null) {
|
|
||||||
this.response.setContentType(contentType.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
Charset charset = (contentType != null ? contentType.getCharset() : null);
|
// HttpServletResponse exposes some headers as properties: we should include those if not already present
|
||||||
if (this.response.getCharacterEncoding() == null && charset != null) {
|
if (this.response.getContentType() == null && headers.containsKey(HttpHeaders.CONTENT_TYPE)) {
|
||||||
this.response.setCharacterEncoding(charset.name());
|
this.response.setContentType(headers.getFirst(HttpHeaders.CONTENT_TYPE));
|
||||||
}
|
}
|
||||||
|
if (this.response.getCharacterEncoding() == null && headers.containsKey(HttpHeaders.CONTENT_TYPE)) {
|
||||||
long contentLength = getHeaders().getContentLength();
|
try {
|
||||||
|
// Lazy parsing into MediaType
|
||||||
|
MediaType contentType = headers.getContentType();
|
||||||
|
if (contentType != null) {
|
||||||
|
Charset charset = contentType.getCharset();
|
||||||
|
if (charset != null) {
|
||||||
|
this.response.setCharacterEncoding(charset.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
// Leave character encoding unspecified
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long contentLength = headers.getContentLength();
|
||||||
if (contentLength != -1) {
|
if (contentLength != -1) {
|
||||||
this.response.setContentLengthLong(contentLength);
|
this.response.setContentLengthLong(contentLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeAdaptedHeaders) {
|
if (removeAdaptedHeaders) {
|
||||||
getHeaders().remove(HttpHeaders.CONTENT_TYPE);
|
headers.remove(HttpHeaders.CONTENT_TYPE);
|
||||||
getHeaders().remove(HttpHeaders.CONTENT_LENGTH);
|
headers.remove(HttpHeaders.CONTENT_LENGTH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +192,7 @@ class ServletServerHttpResponse extends AbstractListenerServerHttpResponse {
|
|||||||
cookie.setSecure(httpCookie.isSecure());
|
cookie.setSecure(httpCookie.isSecure());
|
||||||
cookie.setHttpOnly(httpCookie.isHttpOnly());
|
cookie.setHttpOnly(httpCookie.isHttpOnly());
|
||||||
if (httpCookie.isPartitioned()) {
|
if (httpCookie.isPartitioned()) {
|
||||||
if (IS_SERVLET61) {
|
if (SERVLET61) {
|
||||||
cookie.setAttribute("Partitioned", "");
|
cookie.setAttribute("Partitioned", "");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -373,7 +379,6 @@ class ServletServerHttpResponse extends AbstractListenerServerHttpResponse {
|
|||||||
|
|
||||||
private class ResponseBodyProcessor extends AbstractListenerWriteProcessor<DataBuffer> {
|
private class ResponseBodyProcessor extends AbstractListenerWriteProcessor<DataBuffer> {
|
||||||
|
|
||||||
|
|
||||||
public ResponseBodyProcessor() {
|
public ResponseBodyProcessor() {
|
||||||
super(request.getLogPrefix());
|
super(request.getLogPrefix());
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-6
@@ -17,6 +17,7 @@
|
|||||||
package org.springframework.web.servlet.function;
|
package org.springframework.web.servlet.function;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpStatusCode;
|
import org.springframework.http.HttpStatusCode;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
@@ -51,6 +53,7 @@ abstract class AbstractServerResponse extends ErrorHandlingServerResponse {
|
|||||||
|
|
||||||
private final MultiValueMap<String, Cookie> cookies;
|
private final MultiValueMap<String, Cookie> cookies;
|
||||||
|
|
||||||
|
|
||||||
protected AbstractServerResponse(
|
protected AbstractServerResponse(
|
||||||
HttpStatusCode statusCode, HttpHeaders headers, MultiValueMap<String, Cookie> cookies) {
|
HttpStatusCode statusCode, HttpHeaders headers, MultiValueMap<String, Cookie> cookies) {
|
||||||
|
|
||||||
@@ -60,6 +63,7 @@ abstract class AbstractServerResponse extends ErrorHandlingServerResponse {
|
|||||||
CollectionUtils.unmodifiableMultiValueMap(new LinkedMultiValueMap<>(cookies));
|
CollectionUtils.unmodifiableMultiValueMap(new LinkedMultiValueMap<>(cookies));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final HttpStatusCode statusCode() {
|
public final HttpStatusCode statusCode() {
|
||||||
return this.statusCode;
|
return this.statusCode;
|
||||||
@@ -118,14 +122,29 @@ abstract class AbstractServerResponse extends ErrorHandlingServerResponse {
|
|||||||
servletResponse.addHeader(headerName, headerValue);
|
servletResponse.addHeader(headerName, headerValue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// HttpServletResponse exposes some headers as properties: we should include those if not already present
|
// HttpServletResponse exposes some headers as properties: we should include those if not already present
|
||||||
if (servletResponse.getContentType() == null && this.headers.getContentType() != null) {
|
if (servletResponse.getContentType() == null && this.headers.containsKey(HttpHeaders.CONTENT_TYPE)) {
|
||||||
servletResponse.setContentType(this.headers.getContentType().toString());
|
servletResponse.setContentType(this.headers.getFirst(HttpHeaders.CONTENT_TYPE));
|
||||||
}
|
}
|
||||||
if (servletResponse.getCharacterEncoding() == null &&
|
if (servletResponse.getCharacterEncoding() == null && this.headers.containsKey(HttpHeaders.CONTENT_TYPE)) {
|
||||||
this.headers.getContentType() != null &&
|
try {
|
||||||
this.headers.getContentType().getCharset() != null) {
|
// Lazy parsing into MediaType
|
||||||
servletResponse.setCharacterEncoding(this.headers.getContentType().getCharset().name());
|
MediaType contentType = this.headers.getContentType();
|
||||||
|
if (contentType != null) {
|
||||||
|
Charset charset = contentType.getCharset();
|
||||||
|
if (charset != null) {
|
||||||
|
servletResponse.setCharacterEncoding(charset.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
// Leave character encoding unspecified
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long contentLength = this.headers.getContentLength();
|
||||||
|
if (contentLength != -1) {
|
||||||
|
servletResponse.setContentLengthLong(contentLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
@@ -459,6 +459,11 @@ public class ResponseBodyEmitterReturnValueHandler implements HandlerMethodRetur
|
|||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Override - on Servlet 6.1
|
||||||
|
public void setCharacterEncoding(Charset encoding) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setContentLength(int len) {
|
public void setContentLength(int len) {
|
||||||
// ignore
|
// ignore
|
||||||
|
|||||||
Reference in New Issue
Block a user