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

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration/http"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<beans:bean class="org.springframework.integration.http.inbound.UriPathHandlerMapping"/>
<inbound-gateway path="/test" request-channel="testChannel"
payload-expression="T(org.springframework.web.context.request.RequestContextHolder).requestAttributes.request.queryString"/>
<int:publish-subscribe-channel id="testChannel"/>
<outbound-gateway id="proxyGateway" request-channel="testChannel"
url-expression="'http://testServer/test?' + payload"/>
<int:bridge input-channel="testChannel" output-channel="checkHeadersChannel"/>
<int:channel id="checkHeadersChannel">
<int:queue/>
</int:channel>
</beans:beans>

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2002-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.http;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.net.URI;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.PropertyAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHeaders;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
/**
* @author Artem Bilan
* @since 3.0
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class HttpProxyScenarioTests {
private final HandlerAdapter handlerAdapter = new HttpRequestHandlerAdapter();
@Autowired
private HandlerMapping handlerMapping;
@Autowired
@Qualifier("proxyGateway.handler")
private HttpRequestExecutingMessageHandler handler;
@Autowired
private PollableChannel checkHeadersChannel;
@Test
public void testHttpProxyScenario() throws Exception {
DateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
Calendar c = Calendar.getInstance();
c.set(Calendar.MILLISECOND, 0);
final long ifModifiedSince = c.getTimeInMillis();
String ifModifiedSinceValue = dateFormat.format(ifModifiedSince);
c.add(Calendar.DATE, -1);
long ifUnmodifiedSince = c.getTimeInMillis();
final String ifUnmodifiedSinceValue = dateFormat.format(ifUnmodifiedSince);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test");
request.setQueryString("foo=bar&FOO=BAR");
request.addHeader("If-Modified-Since", ifModifiedSinceValue);
request.addHeader("If-Unmodified-Since", ifUnmodifiedSinceValue);
Object handler = this.handlerMapping.getHandler(request).getHandler();
assertNotNull(handler);
MockHttpServletResponse response = new MockHttpServletResponse();
RestTemplate template = Mockito.spy(new RestTemplate());
Mockito.doAnswer(new Answer<ResponseEntity<?>>() {
@Override
public ResponseEntity<?> answer(InvocationOnMock invocation) throws Throwable {
URI uri = (URI) invocation.getArguments()[0];
assertEquals(new URI("http://testServer/test?foo=bar&FOO=BAR"), uri);
HttpEntity<?> httpEntity = (HttpEntity) invocation.getArguments()[2];
HttpHeaders httpHeaders = httpEntity.getHeaders();
assertEquals(ifModifiedSince, httpHeaders.getIfNotModifiedSince());
assertEquals(ifUnmodifiedSinceValue, httpHeaders.getFirst("If-Unmodified-Since"));
return new ResponseEntity<Object>(httpEntity.getHeaders(), HttpStatus.OK);
}
}).when(template).exchange(Mockito.any(URI.class), Mockito.any(HttpMethod.class),
Mockito.any(HttpEntity.class), (Class<?>) Mockito.any(Class.class));
PropertyAccessor dfa = new DirectFieldAccessor(this.handler);
dfa.setPropertyValue("restTemplate", template);
RequestAttributes attributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(attributes);
this.handlerAdapter.handle(request, response, handler);
assertNull(response.getHeaderValue("If-Modified-Since"));
assertNull(response.getHeaderValue("If-Unmodified-Since"));
Message<?> message = this.checkHeadersChannel.receive(2000);
MessageHeaders headers = message.getHeaders();
assertEquals(ifModifiedSince, headers.get("If-Modified-Since"));
assertEquals(ifUnmodifiedSince, headers.get("If-Unmodified-Since"));
}
}

View File

@@ -25,6 +25,7 @@ import java.net.URI;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -48,6 +49,7 @@ import org.springframework.util.CollectionUtils;
* @author Oleg Zhurakousky
* @author Mark Fisher
* @author Gunnar Hillert
* @author Artem Bilan
* @since 2.0.1
*/
public class DefaultHttpHeaderMapperFromMessageInboundTests {
@@ -549,13 +551,27 @@ public class DefaultHttpHeaderMapperFromMessageInboundTests {
HeaderMapper<HttpHeaders> mapper = DefaultHttpHeaderMapper.inboundMapper();
HttpHeaders headers = new HttpHeaders();
// suppressed in response on inbound, by default
headers.put("Content-Length", Arrays.asList(new String[] {"3"}));
headers.put("Content-Length", Arrays.asList("3"));
Map<String, Object> messageHeaders = mapper.toHeaders(headers);
headers = new HttpHeaders();
mapper.fromHeaders(new MessageHeaders(messageHeaders), headers);
assertNull(headers.get("Content-Length"));
}
@Test
public void testInt2995IfModifiedSince() throws Exception{
HeaderMapper<HttpHeaders> mapper = DefaultHttpHeaderMapper.inboundMapper();
Date ifModifiedSince = new Date();
long ifModifiedSinceTime = ifModifiedSince.getTime();
HttpHeaders headers = new HttpHeaders();
headers.setIfModifiedSince(ifModifiedSinceTime);
Map<String, ?> result = mapper.toHeaders(headers);
Calendar c = Calendar.getInstance();
c.setTime(ifModifiedSince);
c.set(Calendar.MILLISECOND, 0);
assertEquals(c.getTimeInMillis(), result.get("If-Modified-Since"));
}
public static class TestClass {
}

View File

@@ -25,17 +25,22 @@ import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import org.junit.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHeaders;
import org.springframework.integration.mapping.HeaderMapper;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.util.CollectionUtils;
/**
@@ -668,4 +673,19 @@ public class DefaultHttpHeaderMapperFromMessageOutboundTests {
assertEquals(0, messageHeaders.size());
}
public void testInt2995IfModifiedSince() throws Exception{
Date ifModifiedSince = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
String value = dateFormat.format(ifModifiedSince);
Message<?> testMessage = MessageBuilder.withPayload("foo").setHeader("If-Modified-Since", value).build();
HeaderMapper<HttpHeaders> mapper = DefaultHttpHeaderMapper.outboundMapper();
HttpHeaders headers = new HttpHeaders();
mapper.fromHeaders(testMessage.getHeaders(), headers);
Calendar c = Calendar.getInstance();
c.setTime(ifModifiedSince);
c.set(Calendar.MILLISECOND, 0);
assertEquals(c.getTimeInMillis(), headers.getIfNotModifiedSince());
}
}