mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Revisit MockHttpServletResponse for Servlet 6.1
This commit revisits the behavior of our `MockHttpServletResponse` implementation with the javadoc clarifications applied in Servlet 6.1. Prior to this change, adding or setting an HTTP response header with a `null` name or value would not have the expected behavior: * a `null` name should have no effect instead of throwing exceptions * a `null` value when setting a header effectively removes the entry from the response headers Also, this commit ensures that `IllegalStateException` are thrown if `getWriter` is called after a previous `getOutputStream` (and vice versa). Closes gh-34467
This commit is contained in:
+33
-16
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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.
|
||||
@@ -97,7 +97,7 @@ public class MockHttpServletResponse implements HttpServletResponse {
|
||||
|
||||
private final ByteArrayOutputStream content = new ByteArrayOutputStream(1024);
|
||||
|
||||
private final ServletOutputStream outputStream = new ResponseServletOutputStream(this.content);
|
||||
private @Nullable ServletOutputStream outputStream;
|
||||
|
||||
private @Nullable PrintWriter writer;
|
||||
|
||||
@@ -258,12 +258,17 @@ public class MockHttpServletResponse implements HttpServletResponse {
|
||||
@Override
|
||||
public ServletOutputStream getOutputStream() {
|
||||
Assert.state(this.outputStreamAccessAllowed, "OutputStream access not allowed");
|
||||
Assert.state(this.writer == null, "getWriter() has already been called");
|
||||
if (this.outputStream == null) {
|
||||
this.outputStream = new ResponseServletOutputStream(this.content);
|
||||
}
|
||||
return this.outputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrintWriter getWriter() throws UnsupportedEncodingException {
|
||||
Assert.state(this.writerAccessAllowed, "Writer access not allowed");
|
||||
Assert.state(this.outputStream == null, "getOutputStream() has already been called");
|
||||
if (this.writer == null) {
|
||||
Writer targetWriter = new OutputStreamWriter(this.content, getCharacterEncoding());
|
||||
this.writer = new ResponsePrintWriter(targetWriter);
|
||||
@@ -365,6 +370,9 @@ public class MockHttpServletResponse implements HttpServletResponse {
|
||||
}
|
||||
updateContentTypePropertyAndHeader();
|
||||
}
|
||||
else {
|
||||
this.headers.remove(HttpHeaders.CONTENT_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -421,6 +429,8 @@ public class MockHttpServletResponse implements HttpServletResponse {
|
||||
this.headers.clear();
|
||||
this.status = HttpServletResponse.SC_OK;
|
||||
this.errorMessage = null;
|
||||
this.writer = null;
|
||||
this.outputStream = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -680,17 +690,12 @@ public class MockHttpServletResponse implements HttpServletResponse {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeader(String name, @Nullable String value) {
|
||||
if (value == null) {
|
||||
this.headers.remove(name);
|
||||
}
|
||||
else {
|
||||
setHeaderValue(name, value);
|
||||
}
|
||||
public void setHeader(@Nullable String name, @Nullable String value) {
|
||||
setHeaderValue(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHeader(String name, @Nullable String value) {
|
||||
public void addHeader(@Nullable String name, @Nullable String value) {
|
||||
addHeaderValue(name, value);
|
||||
}
|
||||
|
||||
@@ -704,8 +709,8 @@ public class MockHttpServletResponse implements HttpServletResponse {
|
||||
addHeaderValue(name, value);
|
||||
}
|
||||
|
||||
private void setHeaderValue(String name, @Nullable Object value) {
|
||||
if (value == null) {
|
||||
private void setHeaderValue(@Nullable String name, @Nullable Object value) {
|
||||
if (name == null) {
|
||||
return;
|
||||
}
|
||||
boolean replaceHeader = true;
|
||||
@@ -715,8 +720,8 @@ public class MockHttpServletResponse implements HttpServletResponse {
|
||||
doAddHeaderValue(name, value, replaceHeader);
|
||||
}
|
||||
|
||||
private void addHeaderValue(String name, @Nullable Object value) {
|
||||
if (value == null) {
|
||||
private void addHeaderValue(@Nullable String name, @Nullable Object value) {
|
||||
if (name == null) {
|
||||
return;
|
||||
}
|
||||
boolean replaceHeader = false;
|
||||
@@ -726,7 +731,19 @@ public class MockHttpServletResponse implements HttpServletResponse {
|
||||
doAddHeaderValue(name, value, replaceHeader);
|
||||
}
|
||||
|
||||
private boolean setSpecialHeader(String name, Object value, boolean replaceHeader) {
|
||||
private boolean setSpecialHeader(String name, @Nullable Object value, boolean replaceHeader) {
|
||||
if (value == null) {
|
||||
if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
|
||||
setContentType(null);
|
||||
}
|
||||
else if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
|
||||
this.contentLength = 0;
|
||||
}
|
||||
else {
|
||||
this.headers.remove(name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
|
||||
setContentType(value.toString());
|
||||
return true;
|
||||
@@ -763,7 +780,7 @@ public class MockHttpServletResponse implements HttpServletResponse {
|
||||
}
|
||||
}
|
||||
|
||||
private void doAddHeaderValue(String name, Object value, boolean replace) {
|
||||
private void doAddHeaderValue(String name, @Nullable Object value, boolean replace) {
|
||||
Assert.notNull(value, "Header value must not be null");
|
||||
HeaderValueHolder header = this.headers.computeIfAbsent(name, key -> new HeaderValueHolder());
|
||||
if (replace) {
|
||||
|
||||
Reference in New Issue
Block a user