Skip to content

Commit cb5d7f6

Browse files
artembilangaryrussell
authored andcommitted
Fix If-Unmodified-Since header mapping
https://build.spring.io/browse/INT-MASTERSPRING40-664 https://build.spring.io/browse/INT-FATS5IC-833 Fix `DefaultHttpHeaderMapper` to populate an `If-Unmodified-Since` request header with the same formatter as it is in the `HttpHeaders` in Spring Web **Cherry-pick to 5.1.x & 5.0.x**
1 parent 23a73fa commit cb5d7f6

File tree

3 files changed

+54
-46
lines changed

3 files changed

+54
-46
lines changed

spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ private HttpEntity<?> createHttpEntityFromPayload(Message<?> message, HttpMethod
370370
// payload is already an HttpEntity, just return it as-is
371371
return (HttpEntity<?>) payload;
372372
}
373-
HttpHeaders httpHeaders = this.mapHeaders(message);
373+
HttpHeaders httpHeaders = mapHeaders(message);
374374
if (!shouldIncludeRequestBody(httpMethod)) {
375375
return new HttpEntity<>(httpHeaders);
376376
}

spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java

+52-44
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@ public class DefaultHttpHeaderMapper implements HeaderMapper<HttpHeaders>, BeanF
262262
// Copy of 'org.springframework.http.HttpHeaders#GMT'
263263
private static final ZoneId GMT = ZoneId.of("GMT");
264264

265+
// Copy of 'org.springframework.http.HttpHeaders#DATE_FORMATTER'
266+
protected static final DateTimeFormatter DATE_FORMATTER =
267+
DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US).withZone(GMT);
268+
265269
// Copy of 'org.springframework.http.HttpHeaders#DATE_FORMATS'
266270
protected static final DateTimeFormatter[] DATE_FORMATS = new DateTimeFormatter[] {
267271
DateTimeFormatter.RFC_1123_DATE_TIME,
@@ -413,27 +417,28 @@ public void setUserDefinedHeaderPrefix(String userDefinedHeaderPrefix) {
413417
@Override
414418
public void fromHeaders(MessageHeaders headers, HttpHeaders target) {
415419
if (this.logger.isDebugEnabled()) {
416-
this.logger.debug(MessageFormat.format("outboundHeaderNames={0}",
417-
CollectionUtils.arrayToList(this.outboundHeaderNames)));
420+
this.logger.debug("outboundHeaderNames=" + Arrays.toString(this.outboundHeaderNames));
418421
}
419422
for (Entry<String, Object> entry : headers.entrySet()) {
420423
String name = entry.getKey();
421424
String lowerName = name.toLowerCase();
422-
if (this.shouldMapOutboundHeader(lowerName)) {
425+
if (shouldMapOutboundHeader(lowerName)) {
423426
Object value = entry.getValue();
424427
if (value != null) {
425428
if (!HTTP_REQUEST_HEADER_NAMES_LOWER.contains(lowerName) &&
426429
!HTTP_RESPONSE_HEADER_NAMES_LOWER.contains(lowerName) &&
427430
!MessageHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) {
428431
// prefix the user-defined header names if not already prefixed
429432

430-
name = StringUtils.startsWithIgnoreCase(name, this.userDefinedHeaderPrefix) ? name :
431-
this.userDefinedHeaderPrefix + name;
433+
name =
434+
StringUtils.startsWithIgnoreCase(name, this.userDefinedHeaderPrefix)
435+
? name
436+
: this.userDefinedHeaderPrefix + name;
432437
}
433438
if (this.logger.isDebugEnabled()) {
434439
this.logger.debug(MessageFormat.format("setting headerName=[{0}], value={1}", name, value));
435440
}
436-
this.setHttpHeader(target, name, value);
441+
setHttpHeader(target, name, value);
437442
}
438443
}
439444
}
@@ -450,36 +455,36 @@ public Map<String, Object> toHeaders(HttpHeaders source) {
450455
this.logger.debug(MessageFormat.format("inboundHeaderNames={0}",
451456
CollectionUtils.arrayToList(this.inboundHeaderNames)));
452457
}
453-
Map<String, Object> target = new HashMap<String, Object>();
458+
Map<String, Object> target = new HashMap<>();
454459
Set<String> headerNames = source.keySet();
455460
for (String name : headerNames) {
456461
String lowerName = name.toLowerCase();
457-
if (this.shouldMapInboundHeader(lowerName)) {
462+
if (shouldMapInboundHeader(lowerName)) {
458463
if (!HTTP_REQUEST_HEADER_NAMES_LOWER.contains(lowerName)
459464
&& !HTTP_RESPONSE_HEADER_NAMES_LOWER.contains(lowerName)) {
460465
String prefixedName = StringUtils.startsWithIgnoreCase(name, this.userDefinedHeaderPrefix)
461466
? name
462467
: this.userDefinedHeaderPrefix + name;
463468
Object value = source.containsKey(prefixedName)
464-
? this.getHttpHeader(source, prefixedName)
465-
: this.getHttpHeader(source, name);
469+
? getHttpHeader(source, prefixedName)
470+
: getHttpHeader(source, name);
466471
if (value != null) {
467472
if (this.logger.isDebugEnabled()) {
468473
this.logger.debug(MessageFormat.format("setting headerName=[{0}], value={1}", name, value));
469474
}
470-
this.setMessageHeader(target, name, value);
475+
setMessageHeader(target, name, value);
471476
}
472477
}
473478
else {
474-
Object value = this.getHttpHeader(source, name);
479+
Object value = getHttpHeader(source, name);
475480
if (value != null) {
476481
if (this.logger.isDebugEnabled()) {
477482
this.logger.debug(MessageFormat.format("setting headerName=[{0}], value={1}", name, value));
478483
}
479484
if (CONTENT_TYPE.equalsIgnoreCase(name)) {
480485
name = MessageHeaders.CONTENT_TYPE;
481486
}
482-
this.setMessageHeader(target, name, value);
487+
setMessageHeader(target, name, value);
483488
}
484489
}
485490
}
@@ -511,9 +516,10 @@ private boolean shouldMapOutboundHeader(String headerName) {
511516
* When using the default response header name list, suppress the
512517
* mapping of exclusions for specific headers.
513518
*/
514-
if (this.containsElementIgnoreCase(this.excludedInboundStandardResponseHeaderNames, headerName)) {
519+
if (containsElementIgnoreCase(this.excludedInboundStandardResponseHeaderNames, headerName)) {
515520
if (this.logger.isDebugEnabled()) {
516-
this.logger.debug(MessageFormat.format("headerName=[{0}] WILL NOT be mapped (excluded)", headerName));
521+
this.logger
522+
.debug(MessageFormat.format("headerName=[{0}] WILL NOT be mapped (excluded)", headerName));
517523
}
518524
return false;
519525
}
@@ -524,18 +530,19 @@ else if (this.isDefaultOutboundMapper) {
524530
* When using the default request header name list, suppress the
525531
* mapping of exclusions for specific headers.
526532
*/
527-
if (this.containsElementIgnoreCase(this.excludedOutboundStandardRequestHeaderNames, headerName)) {
533+
if (containsElementIgnoreCase(this.excludedOutboundStandardRequestHeaderNames, headerName)) {
528534
if (this.logger.isDebugEnabled()) {
529-
this.logger.debug(MessageFormat.format("headerName=[{0}] WILL NOT be mapped (excluded)", headerName));
535+
this.logger
536+
.debug(MessageFormat.format("headerName=[{0}] WILL NOT be mapped (excluded)", headerName));
530537
}
531538
return false;
532539
}
533540
}
534-
return this.shouldMapHeader(headerName, outboundHeaderNamesLower);
541+
return shouldMapHeader(headerName, outboundHeaderNamesLower);
535542
}
536543

537544
protected final boolean shouldMapInboundHeader(String headerName) {
538-
return this.shouldMapHeader(headerName, this.inboundHeaderNamesLower);
545+
return shouldMapHeader(headerName, this.inboundHeaderNamesLower);
539546
}
540547

541548
/**
@@ -582,7 +589,7 @@ private void setHttpHeader(HttpHeaders target, String name, Object value) {
582589
if (value instanceof Collection<?>) {
583590
Collection<?> values = (Collection<?>) value;
584591
if (!CollectionUtils.isEmpty(values)) {
585-
List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
592+
List<MediaType> acceptableMediaTypes = new ArrayList<>();
586593
for (Object type : values) {
587594
if (type instanceof MediaType) {
588595
acceptableMediaTypes.add((MediaType) type);
@@ -604,7 +611,7 @@ else if (value instanceof MediaType) {
604611
target.setAccept(Collections.singletonList((MediaType) value));
605612
}
606613
else if (value instanceof String[]) {
607-
List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
614+
List<MediaType> acceptableMediaTypes = new ArrayList<>();
608615
for (String next : (String[]) value) {
609616
acceptableMediaTypes.add(MediaType.parseMediaType(next));
610617
}
@@ -623,7 +630,7 @@ else if (ACCEPT_CHARSET.equalsIgnoreCase(name)) {
623630
if (value instanceof Collection<?>) {
624631
Collection<?> values = (Collection<?>) value;
625632
if (!CollectionUtils.isEmpty(values)) {
626-
List<Charset> acceptableCharsets = new ArrayList<Charset>();
633+
List<Charset> acceptableCharsets = new ArrayList<>();
627634
for (Object charset : values) {
628635
if (charset instanceof Charset) {
629636
acceptableCharsets.add((Charset) charset);
@@ -642,7 +649,7 @@ else if (charset instanceof String) {
642649
}
643650
}
644651
else if (value instanceof Charset[] || value instanceof String[]) {
645-
List<Charset> acceptableCharsets = new ArrayList<Charset>();
652+
List<Charset> acceptableCharsets = new ArrayList<>();
646653
Object[] values = ObjectUtils.toObjectArray(value);
647654
for (Object charset : values) {
648655
if (charset instanceof Charset) {
@@ -659,7 +666,7 @@ else if (value instanceof Charset) {
659666
}
660667
else if (value instanceof String) {
661668
String[] charsets = StringUtils.commaDelimitedListToStringArray((String) value);
662-
List<Charset> acceptableCharsets = new ArrayList<Charset>();
669+
List<Charset> acceptableCharsets = new ArrayList<>();
663670
for (String charset : charsets) {
664671
acceptableCharsets.add(Charset.forName(charset.trim()));
665672
}
@@ -675,7 +682,7 @@ else if (ALLOW.equalsIgnoreCase(name)) {
675682
if (value instanceof Collection<?>) {
676683
Collection<?> values = (Collection<?>) value;
677684
if (!CollectionUtils.isEmpty(values)) {
678-
Set<HttpMethod> allowedMethods = new HashSet<HttpMethod>();
685+
Set<HttpMethod> allowedMethods = new HashSet<>();
679686
for (Object method : values) {
680687
if (method instanceof HttpMethod) {
681688
allowedMethods.add((HttpMethod) method);
@@ -698,14 +705,14 @@ else if (method instanceof String) {
698705
target.setAllow(Collections.singleton((HttpMethod) value));
699706
}
700707
else if (value instanceof HttpMethod[]) {
701-
Set<HttpMethod> allowedMethods = new HashSet<HttpMethod>();
708+
Set<HttpMethod> allowedMethods = new HashSet<>();
702709
Collections.addAll(allowedMethods, (HttpMethod[]) value);
703710
target.setAllow(allowedMethods);
704711
}
705712
else if (value instanceof String || value instanceof String[]) {
706713
String[] values = (value instanceof String[]) ? (String[]) value
707714
: StringUtils.commaDelimitedListToStringArray((String) value);
708-
Set<HttpMethod> allowedMethods = new HashSet<HttpMethod>();
715+
Set<HttpMethod> allowedMethods = new HashSet<>();
709716
for (String next : values) {
710717
allowedMethods.add(HttpMethod.valueOf(next.trim()));
711718
}
@@ -766,7 +773,7 @@ else if (value instanceof String) {
766773
target.setDate(Long.parseLong((String) value));
767774
}
768775
catch (@SuppressWarnings(UNUSED) NumberFormatException e) {
769-
target.setDate(this.getFirstDate((String) value, DATE));
776+
target.setDate(getFirstDate((String) value, DATE));
770777
}
771778
}
772779
else {
@@ -797,7 +804,7 @@ else if (value instanceof String) {
797804
target.setExpires(Long.parseLong((String) value));
798805
}
799806
catch (@SuppressWarnings(UNUSED) NumberFormatException e) {
800-
target.setExpires(this.getFirstDate((String) value, EXPIRES));
807+
target.setExpires(getFirstDate((String) value, EXPIRES));
801808
}
802809
}
803810
else {
@@ -818,7 +825,7 @@ else if (value instanceof String) {
818825
target.setIfModifiedSince(Long.parseLong((String) value));
819826
}
820827
catch (@SuppressWarnings(UNUSED) NumberFormatException e) {
821-
target.setIfModifiedSince(this.getFirstDate((String) value, IF_MODIFIED_SINCE));
828+
target.setIfModifiedSince(getFirstDate((String) value, IF_MODIFIED_SINCE));
822829
}
823830
}
824831
else {
@@ -829,20 +836,20 @@ else if (value instanceof String) {
829836
}
830837
}
831838
else if (IF_UNMODIFIED_SINCE.equalsIgnoreCase(name)) {
832-
String ifUnmodifiedSinceValue = null;
839+
String ifUnmodifiedSinceValue;
833840
if (value instanceof Date) {
834-
ifUnmodifiedSinceValue = this.formatDate(((Date) value).getTime());
841+
ifUnmodifiedSinceValue = formatDate(((Date) value).getTime());
835842
}
836843
else if (value instanceof Number) {
837-
ifUnmodifiedSinceValue = this.formatDate(((Number) value).longValue());
844+
ifUnmodifiedSinceValue = formatDate(((Number) value).longValue());
838845
}
839846
else if (value instanceof String) {
840847
try {
841-
ifUnmodifiedSinceValue = this.formatDate(Long.parseLong((String) value));
848+
ifUnmodifiedSinceValue = formatDate(Long.parseLong((String) value));
842849
}
843850
catch (@SuppressWarnings(UNUSED) NumberFormatException e) {
844-
long longValue = this.getFirstDate((String) value, IF_UNMODIFIED_SINCE);
845-
ifUnmodifiedSinceValue = this.formatDate(longValue);
851+
long longValue = getFirstDate((String) value, IF_UNMODIFIED_SINCE);
852+
ifUnmodifiedSinceValue = formatDate(longValue);
846853
}
847854
}
848855
else {
@@ -864,7 +871,7 @@ else if (value instanceof String[]) {
864871
else if (value instanceof Collection) {
865872
Collection<?> values = (Collection<?>) value;
866873
if (!CollectionUtils.isEmpty(values)) {
867-
List<String> ifNoneMatchList = new ArrayList<String>();
874+
List<String> ifNoneMatchList = new ArrayList<>();
868875
for (Object next : values) {
869876
if (next instanceof String) {
870877
ifNoneMatchList.add((String) next);
@@ -891,7 +898,7 @@ else if (value instanceof String) {
891898
target.setLastModified(Long.parseLong((String) value));
892899
}
893900
catch (@SuppressWarnings(UNUSED) NumberFormatException e) {
894-
target.setLastModified(this.getFirstDate((String) value, LAST_MODIFIED));
901+
target.setLastModified(getFirstDate((String) value, LAST_MODIFIED));
895902
}
896903
}
897904
else {
@@ -939,12 +946,12 @@ else if (value instanceof String[]) {
939946
}
940947
else if (value instanceof Iterable<?>) {
941948
for (Object next : (Iterable<?>) value) {
942-
String convertedValue = null;
949+
String convertedValue;
943950
if (next instanceof String) {
944951
convertedValue = (String) next;
945952
}
946953
else {
947-
convertedValue = this.convertToString(value);
954+
convertedValue = convertToString(value);
948955
}
949956
if (StringUtils.hasText(convertedValue)) {
950957
target.add(name, convertedValue);
@@ -957,7 +964,7 @@ else if (value instanceof Iterable<?>) {
957964
}
958965
}
959966
else {
960-
String convertedValue = this.convertToString(value);
967+
String convertedValue = convertToString(value);
961968
if (StringUtils.hasText(convertedValue)) {
962969
target.set(name, convertedValue);
963970
}
@@ -1018,7 +1025,7 @@ else if (IF_MODIFIED_SINCE.equalsIgnoreCase(name)) {
10181025
}
10191026
else if (IF_UNMODIFIED_SINCE.equalsIgnoreCase(name)) {
10201027
String unmodifiedSince = source.getFirst(IF_UNMODIFIED_SINCE);
1021-
return unmodifiedSince != null ? this.getFirstDate(unmodifiedSince, IF_UNMODIFIED_SINCE) : null;
1028+
return unmodifiedSince != null ? getFirstDate(unmodifiedSince, IF_UNMODIFIED_SINCE) : null;
10221029
}
10231030
else if (LAST_MODIFIED.equalsIgnoreCase(name)) {
10241031
long lastModified = source.getLastModified();
@@ -1066,6 +1073,7 @@ protected String convertToString(Object value) {
10661073
if (this.conversionService != null &&
10671074
this.conversionService.canConvert(TypeDescriptor.forObject(value),
10681075
TypeDescriptor.valueOf(String.class))) {
1076+
10691077
return this.conversionService.convert(value, String.class);
10701078
}
10711079
return null;
@@ -1089,10 +1097,10 @@ protected long getFirstDate(String headerValue, String headerName) {
10891097
+ "' header");
10901098
}
10911099

1092-
protected String formatDate(long date) {
1100+
protected static String formatDate(long date) {
10931101
Instant instant = Instant.ofEpochMilli(date);
10941102
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, GMT);
1095-
return DATE_FORMATS[0].format(zonedDateTime);
1103+
return DATE_FORMATTER.format(zonedDateTime);
10961104
}
10971105

10981106
/**

spring-integration-http/src/test/java/org/springframework/integration/http/HttpProxyScenarioTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ public void testHttpMultipartProxyScenario() throws Exception {
190190
MultiValueMap<String, String> responseHeaders = new LinkedMultiValueMap<>(httpHeaders);
191191
responseHeaders.set("Connection", "close");
192192
responseHeaders.set("Content-Type", "text/plain");
193-
return new ResponseEntity<Object>(responseHeaders, HttpStatus.OK);
193+
return new ResponseEntity<>(responseHeaders, HttpStatus.OK);
194194
}).when(template).exchange(Mockito.any(URI.class), Mockito.any(HttpMethod.class),
195195
Mockito.any(HttpEntity.class), (Class<?>) isNull());
196196

0 commit comments

Comments
 (0)