[INT-2995] If-Modified-Since is formatted date string but DefaultHttpHeaderMapper is handling it as long value and causes NumberFormatException Created: 16/Apr/13  Updated: 05/Sep/13  Resolved: 30/Aug/13

Status: Closed
Project: Spring Integration
Component/s: HTTP Support
Affects Version/s: 2.2.3
Fix Version/s: 3.0 M3

Type: Bug Priority: Major
Reporter: Shinki Hong Assignee: Artem Bilan
Resolution: Complete Votes: 0
Labels: PullRequest
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Relate
relates to SPR-10600 Rename HttpHeaders#getIfNotModifiedSi... Closed

 Description   

'if-Modified-Since' in HTTP header is supposed to have something like below.

If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT

But DefaultHttpHeaderMapper is just trying to cast it to long value and it causes NumberFormatException.

Caused by: java.lang.NumberFormatException: For input string: "Mon, 25 Mar 2013 19:02:15 GMT"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48) ~[na:1.6.0_34]
	at java.lang.Long.parseLong(Long.java:410) ~[na:1.6.0_34]
	at java.lang.Long.parseLong(Long.java:468) ~[na:1.6.0_34]
	at org.springframework.integration.http.support.DefaultHttpHeaderMapper.setHttpHeader(DefaultHttpHeaderMapper.java:681) ~[spring-integration-http-2.2.3.RELEASE.jar:na]
	at org.springframework.integration.http.support.DefaultHttpHeaderMapper.fromHeaders(DefaultHttpHeaderMapper.java:333) ~[spring-integration-http-2.2.3.RELEASE.jar:na]
	at org.springframework.integration.http.support.DefaultHttpHeaderMapper.fromHeaders(DefaultHttpHeaderMapper.java:63) ~[spring-integration-http-2.2.3.RELEASE.jar:na]


 Comments   
Comment by Artem Bilan [ 16/Apr/13 ]

Shinki, it's invalid issue. Take a look into Servlet spec, e.g. JavaDoc for HttpServletRequest#getDateHeader.
It says that this header should be returned as long of milliseconds. But from other side it has to be presented in the headers as some specific Date format: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html.
So, to achieve robustness and flexibility the framework suggests to make the work for you. Your task just supply Date or millisecods: org.springframework.http.HttpHeaders#setIfModifiedSince

WDYT?

Comment by Gary Russell [ 16/Apr/13 ]

As described by Artem, this header must be specified as a Long, in milliseconds since 1/1/1970 00:00:00 GMT.

You can use a SimpleDateFormat to parse the date and convert it to the appropriate long value.

Comment by Shinki Hong [ 16/Apr/13 ]

Ok, let me explain what I was doing.
I was testing an image proxy with Spring integration. with http-inbound-gateway and http-outbound-gateway.

And actually the 'If-Modified-Since' value was passed by http-inbound-gateway.
According to your comment, it seems the value should have been parsed to long type at the http-inbound-gateway.

If you say the header value should be parsed by some enricher or something, not by framework, I'm fine and this is an invalid issue.

My configuration was as shown below.

  <bean class="org.springframework.integration.http.inbound.UriPathHandlerMapping"/>
  <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
  <bean id="imageView" class="com.imageproxytest.ImageView"></bean>

  <util:list id="bufferedImageConverter">
    <bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/>
  </util:list>
  
  <int-http:inbound-gateway request-channel="channel1" path="/test" supported-methods="GET"
   view-name="imageView"/>

  <int:channel id="channel1">
    <int:interceptors>
      <int:wire-tap channel="channel2" />
    </int:interceptors>
  </int:channel>

  <int-http:outbound-gateway request-channel="channel1" url="https://www.google.com/images/srpr/logo4w.png"
  http-method="GET" expected-response-type="java.awt.image.BufferedImage" message-converters="bufferedImageConverter"/>
  <int:channel id="channel2"></int:channel>

  <int:logging-channel-adapter channel="channel2" expression="headers"/>

Thanks.

Comment by Artem Bilan [ 17/Apr/13 ]

Hi, Shinki!
Now I see what do you mean. Thanks for your code snippet.

@Test
public void testInt2995IfModifiedSince() throws Exception{
	HeaderMapper<HttpHeaders> mapper  = DefaultHttpHeaderMapper.inboundMapper();
	long ifModifiedSinceTime = new Date().getTime();
	HttpHeaders headers = new HttpHeaders();
	headers.setIfModifiedSince(ifModifiedSinceTime);
	Map<String, ?> result = mapper.toHeaders(headers);
	assertEquals(ifModifiedSinceTime, result.get("If-Modified-Since")); //failed !!!
}

Gary, I reopen the issue, because there is really some confused point:
DefaultHttpHeaderMapper#getHttpHeader takes care about If-Unmodified-Since and invokes org.springframework.http.HttpHeaders#getIfNotModifiedSince(), but the JavaDoc and the code say that this method returns the long of If-Modified-Since. From other side DefaultHttpHeaderMapper#getHttpHeader doesn't do anything with If-Modified-Since.
I don't understand why Spring-MVC team made this inconsistent method (I can open a SPR JIRA), but we should fix this bug in the Spring Integration unambiguously.

Comment by Gary Russell [ 17/Apr/13 ]

Yes, I agree; the inconsistency is bad; thanks, Shinki for pointing it out; the framework should definitely take care of this, regardless of what the underlying MVC code is doing.

Artem, we need to be aware of existing applications, though (they might be expecting the RFC-1123 date). The value probably should be converted to a long, and we can probably do that with 3.0, with a clear explanation in "What's New", and the migration guide.

Perhaps for full flexibility, we should always allow both formats outbound and, on inbound, have a switch on the mapper as to what to emit for 3.0.x the default would be to convert to a long.

I don't think we need namespace support for the switch.

The work-around for 2.2.x would be to add a header-enricher to convert the timestamp; if we decide to backport, we should probably make the default to leave it as an RFC-1123 date.

Comment by Artem Bilan [ 17/Apr/13 ]

I suggest this minimal fix:
1. For 2.2.x - leave it as: there is always a workaround to place between int-http:inbound-gateway & int-http:outbound-gateway header-enricher to parse String with date to the long.
2. For 3.0.x - make it right way: from/to long and avoid Spring-MVC inconsistency with If-Modified-Since & If-Unmodified-Since.
I don't see any reason to provide format flexibility: let HttpHeaders does it!
And this way we'll fix an issue for proxy-scenario as Shinki's case, when we propagate headers from inbound to outbound.
Of course, we should say a note in the Migration Guide, that now If-Modified-Since is mapped as long, not formated date.

Let me know and I'll go ahead.

P.S. Gary, WDYT about Spring-MVC issue? Is there a reason to disturb Arjen and Rossen?

Comment by Artem Bilan [ 27/May/13 ]

PR: https://github.com/SpringSource/spring-integration/pull/813

Comment by Gary Russell [ 30/Aug/13 ]

Merged

Generated at Sun Nov 17 19:54:53 UTC 2019 using Jira 7.13.8#713008-sha1:1606a5c1e7006e1ab135aac81f7a9566b2dbc3a6.