INT-2995: Fix HttpHeaders inconsistency
Previously, 'If-Modified-Since' and 'If-Unmodified-Since' HTTP headers were incorrectly processed within from/to HTTP headers mapping, because Spring-Web `HttpHeaders` has a confusing method name, see: https://jira.springsource.org/browse/SPR-10600 * fix 'If-Modified-Since' and 'If-Unmodified-Since' processing independently from `HttpHeaders` * add fallback to formatted string for date ware HTTP headers * add Spring Integration HTTP proxy scenario test JIRA: https://jira.springsource.org/browse/INT-2995
This commit is contained in:
committed by
Gary Russell
parent
9285867465
commit
4609b861d1
@@ -19,7 +19,10 @@ package org.springframework.integration.http.support;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.DateFormat;
|
||||
import java.text.MessageFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -29,9 +32,12 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
@@ -240,6 +246,14 @@ public class DefaultHttpHeaderMapper implements HeaderMapper<HttpHeaders>, BeanF
|
||||
|
||||
public static final String HTTP_RESPONSE_HEADER_NAME_PATTERN = "HTTP_RESPONSE_HEADERS";
|
||||
|
||||
// Copy of 'org.springframework.http.HttpHeaders#DATE_FORMATS'
|
||||
private static final String[] DATE_FORMATS = new String[] {
|
||||
"EEE, dd MMM yyyy HH:mm:ss zzz",
|
||||
"EEE, dd-MMM-yy HH:mm:ss zzz",
|
||||
"EEE MMM dd HH:mm:ss yyyy"
|
||||
};
|
||||
|
||||
private static TimeZone GMT = TimeZone.getTimeZone("GMT");
|
||||
|
||||
private volatile String[] outboundHeaderNames = new String[0];
|
||||
|
||||
@@ -637,7 +651,12 @@ public class DefaultHttpHeaderMapper implements HeaderMapper<HttpHeaders>, BeanF
|
||||
target.setDate(((Number) value).longValue());
|
||||
}
|
||||
else if (value instanceof String) {
|
||||
target.setDate(Long.parseLong((String) value));
|
||||
try {
|
||||
target.setDate(Long.parseLong((String) value));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
target.setDate(this.getFirstDate((String) value, DATE));
|
||||
}
|
||||
}
|
||||
else {
|
||||
Class<?> clazz = (value != null) ? value.getClass() : null;
|
||||
@@ -663,7 +682,12 @@ public class DefaultHttpHeaderMapper implements HeaderMapper<HttpHeaders>, BeanF
|
||||
target.setExpires(((Number) value).longValue());
|
||||
}
|
||||
else if (value instanceof String) {
|
||||
target.setExpires(Long.parseLong((String) value));
|
||||
try {
|
||||
target.setExpires(Long.parseLong((String) value));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
target.setExpires(this.getFirstDate((String) value, EXPIRES));
|
||||
}
|
||||
}
|
||||
else {
|
||||
Class<?> clazz = (value != null) ? value.getClass() : null;
|
||||
@@ -679,7 +703,12 @@ public class DefaultHttpHeaderMapper implements HeaderMapper<HttpHeaders>, BeanF
|
||||
target.setIfModifiedSince(((Number) value).longValue());
|
||||
}
|
||||
else if (value instanceof String) {
|
||||
target.setIfModifiedSince(Long.parseLong((String) value));
|
||||
try {
|
||||
target.setIfModifiedSince(Long.parseLong((String) value));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
target.setIfModifiedSince(this.getFirstDate((String) value, IF_MODIFIED_SINCE));
|
||||
}
|
||||
}
|
||||
else {
|
||||
Class<?> clazz = (value != null) ? value.getClass() : null;
|
||||
@@ -687,6 +716,30 @@ public class DefaultHttpHeaderMapper implements HeaderMapper<HttpHeaders>, BeanF
|
||||
"Expected Date, Number, or String value for 'If-Modified-Since' header value, but received: " + clazz);
|
||||
}
|
||||
}
|
||||
else if (IF_UNMODIFIED_SINCE.equalsIgnoreCase(name)) {
|
||||
String ifUnmodifiedSinceValue = null;
|
||||
if (value instanceof Date) {
|
||||
ifUnmodifiedSinceValue = this.formatDate(((Date) value).getTime());
|
||||
}
|
||||
else if (value instanceof Number) {
|
||||
ifUnmodifiedSinceValue = this.formatDate(((Number) value).longValue());
|
||||
}
|
||||
else if (value instanceof String) {
|
||||
try {
|
||||
ifUnmodifiedSinceValue = this.formatDate(Long.parseLong((String) value));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
long longValue = this.getFirstDate((String) value, IF_UNMODIFIED_SINCE);
|
||||
ifUnmodifiedSinceValue = this.formatDate(longValue);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Class<?> clazz = (value != null) ? value.getClass() : null;
|
||||
throw new IllegalArgumentException(
|
||||
"Expected Date, Number, or String value for 'If-Unmodified-Since' header value, but received: " + clazz);
|
||||
}
|
||||
target.set(IF_UNMODIFIED_SINCE, ifUnmodifiedSinceValue);
|
||||
}
|
||||
else if (IF_NONE_MATCH.equalsIgnoreCase(name)) {
|
||||
if (value instanceof String) {
|
||||
target.setIfNoneMatch((String) value);
|
||||
@@ -721,7 +774,12 @@ public class DefaultHttpHeaderMapper implements HeaderMapper<HttpHeaders>, BeanF
|
||||
target.setLastModified(((Number) value).longValue());
|
||||
}
|
||||
else if (value instanceof String) {
|
||||
target.setLastModified(Long.parseLong((String) value));
|
||||
try {
|
||||
target.setLastModified(Long.parseLong((String) value));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
target.setLastModified(this.getFirstDate((String) value, LAST_MODIFIED));
|
||||
}
|
||||
}
|
||||
else {
|
||||
Class<?> clazz = (value != null) ? value.getClass() : null;
|
||||
@@ -842,9 +900,13 @@ public class DefaultHttpHeaderMapper implements HeaderMapper<HttpHeaders>, BeanF
|
||||
else if (IF_NONE_MATCH.equalsIgnoreCase(name)) {
|
||||
return source.getIfNoneMatch();
|
||||
}
|
||||
else if (IF_MODIFIED_SINCE.equalsIgnoreCase(name)) {
|
||||
long modifiedSince = source.getIfNotModifiedSince();
|
||||
return (modifiedSince > -1) ? modifiedSince : null;
|
||||
}
|
||||
else if (IF_UNMODIFIED_SINCE.equalsIgnoreCase(name)) {
|
||||
long unmodifiedSince = source.getIfNotModifiedSince();
|
||||
return (unmodifiedSince > -1) ? unmodifiedSince : null;
|
||||
String unmodifiedSince = source.getFirst(IF_UNMODIFIED_SINCE);
|
||||
return unmodifiedSince != null ? this.getFirstDate(unmodifiedSince, IF_UNMODIFIED_SINCE) : null;
|
||||
}
|
||||
else if (LAST_MODIFIED.equalsIgnoreCase(name)) {
|
||||
long lastModified = source.getLastModified();
|
||||
@@ -896,6 +958,27 @@ public class DefaultHttpHeaderMapper implements HeaderMapper<HttpHeaders>, BeanF
|
||||
return null;
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
|
||||
private long getFirstDate(String headerValue, String headerName) {
|
||||
for (String dateFormat : DATE_FORMATS) {
|
||||
DateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US);
|
||||
simpleDateFormat.setTimeZone(GMT);
|
||||
try {
|
||||
return simpleDateFormat.parse(headerValue).getTime();
|
||||
}
|
||||
catch (ParseException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot parse date value '" + headerValue +"' for '" + headerName + "' header");
|
||||
}
|
||||
|
||||
private String formatDate(long date) {
|
||||
DateFormat dateFormat = new SimpleDateFormat(DATE_FORMATS[0], Locale.US);
|
||||
dateFormat.setTimeZone(GMT);
|
||||
return dateFormat.format(new Date(date));
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for creating a basic outbound mapper instance.
|
||||
@@ -922,4 +1005,5 @@ public class DefaultHttpHeaderMapper implements HeaderMapper<HttpHeaders>, BeanF
|
||||
mapper.setExcludedInboundStandardResponseHeaderNames(HTTP_RESPONSE_HEADER_NAMES_INBOUND_EXCLUSIONS);
|
||||
return mapper;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user