Skip to content

Commit

Permalink
Ensure characterEncoding in MockHttpServletResponse is non-null
Browse files Browse the repository at this point in the history
  • Loading branch information
sbrannen authored and lxbzmy committed Mar 26, 2022
1 parent 25a5bfa commit b3d5840
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 58 deletions.
Expand Up @@ -80,7 +80,6 @@ public class MockHttpServletResponse implements HttpServletResponse {

private boolean writerAccessAllowed = true;

@Nullable
private String characterEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;

/**
Expand Down Expand Up @@ -171,33 +170,37 @@ public boolean isWriterAccessAllowed() {
* Determine whether the character encoding has been explicitly set through
* {@link HttpServletResponse} methods or through a {@code charset} parameter
* on the {@code Content-Type}.
* <p>If {@code false}, {@link #getCharacterEncoding()} will return a default
* encoding value.
* <p>If {@code false}, {@link #getCharacterEncoding()} will return the default
* character encoding.
*/
public boolean isCharset() {
return this.characterEncodingSet;
}

@Override
public void setCharacterEncoding(String characterEncoding) {
setExplicitCharacterEncoding(characterEncoding);
updateContentTypePropertyAndHeader();
}

private void setExplicitCharacterEncoding(String characterEncoding) {
Assert.notNull(characterEncoding, "'characterEncoding' must not be null");
this.characterEncoding = characterEncoding;
this.characterEncodingSet = true;
updateContentTypePropertyAndHeader();
}

private void updateContentTypePropertyAndHeader() {
if (this.contentType != null) {
String value = this.contentType;
if (this.characterEncodingSet && !this.contentType.toLowerCase().contains(CHARSET_PREFIX)) {
value = value + ';' + CHARSET_PREFIX + this.characterEncoding;
if (this.characterEncodingSet && !value.toLowerCase().contains(CHARSET_PREFIX)) {
value += ';' + CHARSET_PREFIX + getCharacterEncoding();
this.contentType = value;
}
doAddHeaderValue(HttpHeaders.CONTENT_TYPE, value, true);
}
}

@Override
@Nullable
public String getCharacterEncoding() {
return this.characterEncoding;
}
Expand All @@ -212,9 +215,7 @@ public ServletOutputStream getOutputStream() {
public PrintWriter getWriter() throws UnsupportedEncodingException {
Assert.state(this.writerAccessAllowed, "Writer access not allowed");
if (this.writer == null) {
Writer targetWriter = (this.characterEncoding != null ?
new OutputStreamWriter(this.content, this.characterEncoding) :
new OutputStreamWriter(this.content));
Writer targetWriter = new OutputStreamWriter(this.content, getCharacterEncoding());
this.writer = new ResponsePrintWriter(targetWriter);
}
return this.writer;
Expand All @@ -228,14 +229,16 @@ public byte[] getContentAsByteArray() {
* Get the content of the response body as a {@code String}, using the charset
* specified for the response by the application, either through
* {@link HttpServletResponse} methods or through a charset parameter on the
* {@code Content-Type}.
* {@code Content-Type}. If no charset has been explicitly defined, the default
* character encoding will be used.
* @return the content as a {@code String}
* @throws UnsupportedEncodingException if the character encoding is not supported
* @see #getContentAsString(Charset)
* @see #setCharacterEncoding(String)
* @see #setContentType(String)
*/
public String getContentAsString() throws UnsupportedEncodingException {
return (this.characterEncoding != null ?
this.content.toString(this.characterEncoding) : this.content.toString());
return this.content.toString(getCharacterEncoding());
}

/**
Expand All @@ -248,11 +251,12 @@ public String getContentAsString() throws UnsupportedEncodingException {
* @throws UnsupportedEncodingException if the character encoding is not supported
* @since 5.2
* @see #getContentAsString()
* @see #setCharacterEncoding(String)
* @see #setContentType(String)
*/
public String getContentAsString(Charset fallbackCharset) throws UnsupportedEncodingException {
return (isCharset() && this.characterEncoding != null ?
this.content.toString(this.characterEncoding) :
this.content.toString(fallbackCharset.name()));
String charsetName = (this.characterEncodingSet ? getCharacterEncoding() : fallbackCharset.name());
return this.content.toString(charsetName);
}

@Override
Expand Down Expand Up @@ -282,16 +286,14 @@ public void setContentType(@Nullable String contentType) {
try {
MediaType mediaType = MediaType.parseMediaType(contentType);
if (mediaType.getCharset() != null) {
this.characterEncoding = mediaType.getCharset().name();
this.characterEncodingSet = true;
setExplicitCharacterEncoding(mediaType.getCharset().name());
}
}
catch (Exception ex) {
// Try to get charset value anyway
int charsetIndex = contentType.toLowerCase().indexOf(CHARSET_PREFIX);
if (charsetIndex != -1) {
this.characterEncoding = contentType.substring(charsetIndex + CHARSET_PREFIX.length());
this.characterEncodingSet = true;
setExplicitCharacterEncoding(contentType.substring(charsetIndex + CHARSET_PREFIX.length()));
}
}
updateContentTypePropertyAndHeader();
Expand Down Expand Up @@ -344,7 +346,7 @@ public boolean isCommitted() {
@Override
public void reset() {
resetBuffer();
this.characterEncoding = null;
this.characterEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
this.characterEncodingSet = false;
this.contentLength = 0;
this.contentType = null;
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 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.
Expand Down Expand Up @@ -248,14 +248,11 @@ protected void printFlashMap(FlashMap flashMap) throws Exception {
* Print the response.
*/
protected void printResponse(MockHttpServletResponse response) throws Exception {
String body = (response.getCharacterEncoding() != null ?
response.getContentAsString() : MISSING_CHARACTER_ENCODING);

this.printer.printValue("Status", response.getStatus());
this.printer.printValue("Error message", response.getErrorMessage());
this.printer.printValue("Headers", getResponseHeaders(response));
this.printer.printValue("Content type", response.getContentType());
this.printer.printValue("Body", body);
this.printer.printValue("Body", response.getContentAsString());
this.printer.printValue("Forwarded URL", response.getForwardedUrl());
this.printer.printValue("Redirected URL", response.getRedirectedUrl());
printCookies(response.getCookies());
Expand Down
Expand Up @@ -551,6 +551,8 @@ private void assertPrimarySessionCookie(String expectedValue) {

@Test // gh-25501
void resetResetsCharset() {
assertThat(response.getContentType()).isNull();
assertThat(response.getCharacterEncoding()).isEqualTo("ISO-8859-1");
assertThat(response.isCharset()).isFalse();
response.setCharacterEncoding("UTF-8");
assertThat(response.isCharset()).isTrue();
Expand All @@ -562,6 +564,8 @@ void resetResetsCharset() {

response.reset();

assertThat(response.getContentType()).isNull();
assertThat(response.getCharacterEncoding()).isEqualTo("ISO-8859-1");
assertThat(response.isCharset()).isFalse();
// Do not invoke setCharacterEncoding() since that sets the charset flag to true.
// response.setCharacterEncoding("UTF-8");
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 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.
Expand Down Expand Up @@ -238,16 +238,6 @@ public void printResponseWithDefaultCharacterEncoding() throws Exception {
assertValue("MockHttpServletResponse", "Body", "text");
}

@Test
public void printResponseWithoutCharacterEncoding() throws Exception {
this.response.setCharacterEncoding(null);
this.response.getWriter().print("text");

this.handler.handle(this.mvcResult);

assertValue("MockHttpServletResponse", "Body", "<no character encoding set>");
}

@Test
public void printHandlerNull() throws Exception {
StubMvcResult mvcResult = new StubMvcResult(this.request, null, null, null, null, null, this.response);
Expand Down
Expand Up @@ -80,7 +80,6 @@ public class MockHttpServletResponse implements HttpServletResponse {

private boolean writerAccessAllowed = true;

@Nullable
private String characterEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;

/**
Expand Down Expand Up @@ -171,33 +170,37 @@ public boolean isWriterAccessAllowed() {
* Determine whether the character encoding has been explicitly set through
* {@link HttpServletResponse} methods or through a {@code charset} parameter
* on the {@code Content-Type}.
* <p>If {@code false}, {@link #getCharacterEncoding()} will return a default
* encoding value.
* <p>If {@code false}, {@link #getCharacterEncoding()} will return the default
* character encoding.
*/
public boolean isCharset() {
return this.characterEncodingSet;
}

@Override
public void setCharacterEncoding(String characterEncoding) {
setExplicitCharacterEncoding(characterEncoding);
updateContentTypePropertyAndHeader();
}

private void setExplicitCharacterEncoding(String characterEncoding) {
Assert.notNull(characterEncoding, "'characterEncoding' must not be null");
this.characterEncoding = characterEncoding;
this.characterEncodingSet = true;
updateContentTypePropertyAndHeader();
}

private void updateContentTypePropertyAndHeader() {
if (this.contentType != null) {
String value = this.contentType;
if (this.characterEncodingSet && !this.contentType.toLowerCase().contains(CHARSET_PREFIX)) {
value = value + ';' + CHARSET_PREFIX + this.characterEncoding;
if (this.characterEncodingSet && !value.toLowerCase().contains(CHARSET_PREFIX)) {
value += ';' + CHARSET_PREFIX + getCharacterEncoding();
this.contentType = value;
}
doAddHeaderValue(HttpHeaders.CONTENT_TYPE, value, true);
}
}

@Override
@Nullable
public String getCharacterEncoding() {
return this.characterEncoding;
}
Expand All @@ -212,9 +215,7 @@ public ServletOutputStream getOutputStream() {
public PrintWriter getWriter() throws UnsupportedEncodingException {
Assert.state(this.writerAccessAllowed, "Writer access not allowed");
if (this.writer == null) {
Writer targetWriter = (this.characterEncoding != null ?
new OutputStreamWriter(this.content, this.characterEncoding) :
new OutputStreamWriter(this.content));
Writer targetWriter = new OutputStreamWriter(this.content, getCharacterEncoding());
this.writer = new ResponsePrintWriter(targetWriter);
}
return this.writer;
Expand All @@ -228,14 +229,16 @@ public byte[] getContentAsByteArray() {
* Get the content of the response body as a {@code String}, using the charset
* specified for the response by the application, either through
* {@link HttpServletResponse} methods or through a charset parameter on the
* {@code Content-Type}.
* {@code Content-Type}. If no charset has been explicitly defined, the default
* character encoding will be used.
* @return the content as a {@code String}
* @throws UnsupportedEncodingException if the character encoding is not supported
* @see #getContentAsString(Charset)
* @see #setCharacterEncoding(String)
* @see #setContentType(String)
*/
public String getContentAsString() throws UnsupportedEncodingException {
return (this.characterEncoding != null ?
this.content.toString(this.characterEncoding) : this.content.toString());
return this.content.toString(getCharacterEncoding());
}

/**
Expand All @@ -248,11 +251,12 @@ public String getContentAsString() throws UnsupportedEncodingException {
* @throws UnsupportedEncodingException if the character encoding is not supported
* @since 5.2
* @see #getContentAsString()
* @see #setCharacterEncoding(String)
* @see #setContentType(String)
*/
public String getContentAsString(Charset fallbackCharset) throws UnsupportedEncodingException {
return (isCharset() && this.characterEncoding != null ?
this.content.toString(this.characterEncoding) :
this.content.toString(fallbackCharset.name()));
String charsetName = (this.characterEncodingSet ? getCharacterEncoding() : fallbackCharset.name());
return this.content.toString(charsetName);
}

@Override
Expand Down Expand Up @@ -282,16 +286,14 @@ public void setContentType(@Nullable String contentType) {
try {
MediaType mediaType = MediaType.parseMediaType(contentType);
if (mediaType.getCharset() != null) {
this.characterEncoding = mediaType.getCharset().name();
this.characterEncodingSet = true;
setExplicitCharacterEncoding(mediaType.getCharset().name());
}
}
catch (Exception ex) {
// Try to get charset value anyway
int charsetIndex = contentType.toLowerCase().indexOf(CHARSET_PREFIX);
if (charsetIndex != -1) {
this.characterEncoding = contentType.substring(charsetIndex + CHARSET_PREFIX.length());
this.characterEncodingSet = true;
setExplicitCharacterEncoding(contentType.substring(charsetIndex + CHARSET_PREFIX.length()));
}
}
updateContentTypePropertyAndHeader();
Expand Down Expand Up @@ -344,7 +346,7 @@ public boolean isCommitted() {
@Override
public void reset() {
resetBuffer();
this.characterEncoding = null;
this.characterEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
this.characterEncodingSet = false;
this.contentLength = 0;
this.contentType = null;
Expand Down

0 comments on commit b3d5840

Please sign in to comment.