header = this.headers.get(name);
+ if (!CollectionUtils.isEmpty(header) && header.size() == 1) {
+ Object value = header.get(0);
+ if (value instanceof Number) {
+ return ((Number) value).intValue();
}
- catch (IllegalArgumentException ex) {
- // Invalid Accept-Language format -> just store plain header
+ else if (value instanceof String) {
+ return Integer.parseInt((String) value);
+ }
+ else if (value != null) {
+ throw new NumberFormatException("Value for header '" + name + "' is not a Number: " + value);
+ }
+ else {
+ return -1;
}
- doAddHeaderValue(name, value, true);
}
else {
- doAddHeaderValue(name, value, false);
+ return -1;
}
}
- private void doAddHeaderValue(String name, @Nullable Object value, boolean replace) {
- HeaderValueHolder header = this.headers.get(name);
- Assert.notNull(value, "Header value must not be null");
- if (header == null || replace) {
- header = new HeaderValueHolder();
- this.headers.put(name, header);
- }
- if (value instanceof Collection) {
- header.addValues((Collection>) value);
- }
- else if (value.getClass().isArray()) {
- header.addValueArray(value);
- }
- else {
- header.addValue(value);
- }
- }
-
- /**
- * Return the long timestamp for the date header with the given {@code name}.
- *
- * If the internal value representation is a String, this method will try to
- * parse it as a date using the supported date formats:
- *
- * - "EEE, dd MMM yyyy HH:mm:ss zzz"
- * - "EEE, dd-MMM-yy HH:mm:ss zzz"
- * - "EEE MMM dd HH:mm:ss yyyy"
- *
- *
- * @param name the header name
- * @see Section
- * 7.1.1.1 of RFC 7231
- */
@Override
public long getDateHeader(String name) {
- HeaderValueHolder header = this.headers.get(name);
- Object value = (header != null ? header.getValue() : null);
- if (value instanceof Date) {
- return ((Date) value).getTime();
- }
- else if (value instanceof Number) {
- return ((Number) value).longValue();
- }
- else if (value instanceof String) {
- return parseDateHeader(name, (String) value);
- }
- else if (value != null) {
- throw new IllegalArgumentException(
- "Value for header '" + name + "' is not a Date, Number, or String: " + value);
+ List header = this.headers.get(name);
+ if (!CollectionUtils.isEmpty(header) && header.size() == 1) {
+ Object value = header.get(0);
+ if (value instanceof Date) {
+ return ((Date) value).getTime();
+ }
+ else if (value instanceof Number) {
+ return ((Number) value).longValue();
+ }
+ else if (value instanceof String) {
+ return parseDateHeader(name, (String) value);
+ }
+ else if (value != null) {
+ throw new IllegalArgumentException(
+ "Value for header '" + name + "' is not a Date, Number, or String: " + value);
+ }
+ else {
+ return -1L;
+ }
}
else {
- return -1L;
+ return -1;
}
}
@@ -873,42 +791,6 @@ public class ProxyHttpServletRequest implements HttpServletRequest {
throw new IllegalArgumentException("Cannot parse date value '" + value + "' for '" + name + "' header");
}
- @Override
- @Nullable
- public String getHeader(String name) {
- HeaderValueHolder header = this.headers.get(name);
- return (header != null ? header.getStringValue() : null);
- }
-
- @Override
- public Enumeration getHeaders(String name) {
- HeaderValueHolder header = this.headers.get(name);
- return Collections.enumeration(header != null ? header.getStringValues() : new LinkedList<>());
- }
-
- @Override
- public Enumeration getHeaderNames() {
- return Collections.enumeration(this.headers.keySet());
- }
-
- @Override
- public int getIntHeader(String name) {
- HeaderValueHolder header = this.headers.get(name);
- Object value = (header != null ? header.getValue() : null);
- if (value instanceof Number) {
- return ((Number) value).intValue();
- }
- else if (value instanceof String) {
- return Integer.parseInt((String) value);
- }
- else if (value != null) {
- throw new NumberFormatException("Value for header '" + name + "' is not a Number: " + value);
- }
- else {
- return -1;
- }
- }
-
public void setMethod(@Nullable String method) {
this.method = method;
}
diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletResponse.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletResponse.java
index 3531a8330..e57251e5a 100644
--- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletResponse.java
+++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ProxyHttpServletResponse.java
@@ -22,16 +22,12 @@ import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.text.DateFormat;
-import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
-import java.util.Map;
-import java.util.TimeZone;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
@@ -41,86 +37,43 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
-import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.web.util.WebUtils;
+/**
+ *
+ * @author Oleg Zhurakousky
+ *
+ */
public class ProxyHttpServletResponse implements HttpServletResponse {
- private static final String CHARSET_PREFIX = "charset=";
-
private static final String DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
- private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
-
- // ---------------------------------------------------------------------
- // ServletResponse properties
- // ---------------------------------------------------------------------
-
- private boolean outputStreamAccessAllowed = true;
-
private String defaultCharacterEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
private String characterEncoding = this.defaultCharacterEncoding;
- /**
- * {@code true} if the character encoding has been explicitly set through
- * {@link HttpServletResponse} methods or through a {@code charset} parameter on
- * the {@code Content-Type}.
- */
- private boolean characterEncodingSet = false;
-
private final ByteArrayOutputStream content = new ByteArrayOutputStream(1024);
private final ServletOutputStream outputStream = new ResponseServletOutputStream();
- private long contentLength = 0;
-
private String contentType;
private int bufferSize = 4096;
- private boolean committed;
-
private Locale locale = Locale.getDefault();
- // ---------------------------------------------------------------------
- // HttpServletResponse properties
- // ---------------------------------------------------------------------
-
private final List cookies = new ArrayList<>();
- private final Map headers = new LinkedCaseInsensitiveMap<>();
+ private final HttpHeaders headers = new HttpHeaders();
private int status = HttpServletResponse.SC_OK;
@Nullable
private String errorMessage;
- // ---------------------------------------------------------------------
- // ServletResponse interface
- // ---------------------------------------------------------------------
-
@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;
- }
-
- private void updateContentTypePropertyAndHeader() {
- if (this.contentType != null) {
- String value = this.contentType;
- if (this.characterEncodingSet && !value.toLowerCase().contains(CHARSET_PREFIX)) {
- value += ';' + CHARSET_PREFIX + getCharacterEncoding();
- this.contentType = value;
- }
- doAddHeaderValue(HttpHeaders.CONTENT_TYPE, value, true);
- }
}
@Override
@@ -130,7 +83,6 @@ public class ProxyHttpServletResponse implements HttpServletResponse {
@Override
public ServletOutputStream getOutputStream() {
- Assert.state(this.outputStreamAccessAllowed, "OutputStream access not allowed");
return this.outputStream;
}
@@ -162,24 +114,8 @@ public class ProxyHttpServletResponse implements HttpServletResponse {
return this.content.toString(getCharacterEncoding());
}
- /**
- * Get the content of the response body as a {@code String}, using the provided
- * {@code fallbackCharset} if no charset has been explicitly defined and
- * otherwise 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}.
- *
- * @return the content as a {@code String}
- * @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 {
- String charsetName = (this.characterEncodingSet ? getCharacterEncoding() : fallbackCharset.name());
- return this.content.toString(charsetName);
+ return this.content.toString(getCharacterEncoding());
}
@Override
@@ -224,21 +160,15 @@ public class ProxyHttpServletResponse implements HttpServletResponse {
this.content.reset();
}
- public void setCommitted(boolean committed) {
- this.committed = committed;
- }
-
@Override
public boolean isCommitted() {
- return this.committed;
+ return true;
}
@Override
public void reset() {
resetBuffer();
this.characterEncoding = this.defaultCharacterEncoding;
- this.characterEncodingSet = false;
- this.contentLength = 0;
this.contentType = null;
this.locale = Locale.getDefault();
this.cookies.clear();
@@ -249,16 +179,11 @@ public class ProxyHttpServletResponse implements HttpServletResponse {
@Override
public void setLocale(@Nullable Locale locale) {
- // Although the Javadoc for javax.servlet.ServletResponse.setLocale(Locale) does
- // not
- // state how a null value for the supplied Locale should be handled, both Tomcat
- // and
- // Jetty simply ignore a null value. So we do the same here.
if (locale == null) {
return;
}
this.locale = locale;
- doAddHeaderValue(HttpHeaders.CONTENT_LANGUAGE, locale.toLanguageTag(), true);
+ this.headers.add(HttpHeaders.CONTENT_LANGUAGE, locale.toLanguageTag());
}
@Override
@@ -314,8 +239,7 @@ public class ProxyHttpServletResponse implements HttpServletResponse {
@Override
@Nullable
public String getHeader(String name) {
- HeaderValueHolder header = this.headers.get(name);
- return (header != null ? header.getStringValue() : null);
+ return this.headers.containsKey(name) ? this.headers.get(name).toString() : null;
}
/**
@@ -331,13 +255,7 @@ public class ProxyHttpServletResponse implements HttpServletResponse {
*/
@Override
public List getHeaders(String name) {
- HeaderValueHolder header = this.headers.get(name);
- if (header != null) {
- return header.getStringValues();
- }
- else {
- return Collections.emptyList();
- }
+ return this.headers.get(name);
}
/**
@@ -350,24 +268,7 @@ public class ProxyHttpServletResponse implements HttpServletResponse {
*/
@Nullable
public Object getHeaderValue(String name) {
- HeaderValueHolder header = this.headers.get(name);
- return (header != null ? header.getValue() : null);
- }
-
- /**
- * Return all values for the given header as a List of value objects.
- *
- * @param name the name of the header
- * @return the associated header values, or an empty List if none
- */
- public List