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:
Artem Bilan
2013-05-27 18:36:56 +03:00
committed by Gary Russell
parent 9285867465
commit 4609b861d1
6 changed files with 305 additions and 7 deletions

View File

@@ -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;
}
}