From 352964ee2a2c9e08bd685d28f35c41a2d9bd2269 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Tue, 7 Dec 2010 19:14:47 -0500 Subject: [PATCH] INT-1662 DefaultHttpHeaderMapper now supports standard HTTP Headers --- .../HttpRequestHandlingEndpointSupport.java | 2 +- .../HttpRequestExecutingMessageHandler.java | 12 +- .../http/support/DefaultHttpHeaderMapper.java | 638 +++++++++++++++++- 3 files changed, 623 insertions(+), 29 deletions(-) diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java index e30142fd5f..385884dafd 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java @@ -98,7 +98,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor private volatile List> messageConverters = new ArrayList>(); - private volatile HeaderMapper headerMapper = new DefaultHttpHeaderMapper(); + private volatile HeaderMapper headerMapper = DefaultHttpHeaderMapper.inboundMapper(); private final boolean expectReply; diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java index 8d5b35450d..889b750b39 100755 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java @@ -83,7 +83,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe private volatile String charset = "UTF-8"; - private volatile HeaderMapper headerMapper = new DefaultHttpHeaderMapper(); + private volatile HeaderMapper headerMapper = DefaultHttpHeaderMapper.outboundMapper(); private final Map uriVariableExpressions = new HashMap(); @@ -339,11 +339,11 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe private boolean isMultipart(Map map) { for (String key : map.keySet()) { Object value = map.get(key); - if (value != null){ - if (value.getClass().isArray()){ + if (value != null) { + if (value.getClass().isArray()) { value = CollectionUtils.arrayToList(value); } - if (value instanceof Collection){ + if (value instanceof Collection) { Collection cValues = (Collection) value; for (Object cValue : cValues) { if (cValue != null && !(cValue instanceof String)) { @@ -358,15 +358,17 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe } return false; } + /** * If all keys and values are Strings, we'll consider the Map to be form data. */ private boolean isFormData(Map map) { for (Object key : map.keySet()) { - if (!(key instanceof String)){ + if (!(key instanceof String)) { return false; } } return true; } + } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java b/spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java index 7240572271..1efa1d5f91 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java @@ -16,14 +16,27 @@ package org.springframework.integration.http.support; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; import org.springframework.integration.MessageHeaders; import org.springframework.integration.mapping.HeaderMapper; import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; /** * Default {@link HeaderMapper} implementation for HTTP. @@ -36,59 +49,638 @@ public class DefaultHttpHeaderMapper implements HeaderMapper { public static final String USER_DEFINED_HEADER_PREFIX = "X-"; + private static final String ACCEPT = "Accept"; + + private static final String ACCEPT_CHARSET = "Accept-Charset"; + + private static final String ACCEPT_ENCODING = "Accept-Encoding"; + + private static final String ACCEPT_LANGUAGE = "Accept-Language"; + + private static final String ACCEPT_RANGES = "Accept-Ranges"; + + private static final String AGE = "Age"; + + private static final String ALLOW = "Allow"; + + private static final String AUTHORIZATION = "Authorization"; + + private static final String CACHE_CONTROL = "Cache-Control"; + + private static final String CONNECTION = "Connection"; + + private static final String CONTENT_ENCODING = "Content-Encoding"; + + private static final String CONTENT_LANGUAGE = "Content-Language"; + + private static final String CONTENT_LENGTH = "Content-Length"; + + private static final String CONTENT_LOCATION = "Content-Location"; + + private static final String CONTENT_MD5 = "Content-MD5"; + + private static final String CONTENT_RANGE = "Content-Range"; + + private static final String CONTENT_TYPE = "Content-Type"; + + private static final String COOKIE = "Cookie"; + + private static final String DATE = "Date"; + + private static final String ETAG = "ETag"; + + private static final String EXPECT = "Expect"; + + private static final String EXPIRES = "Expires"; + + private static final String FROM = "From"; + + private static final String HOST = "Host"; + + private static final String IF_MATCH = "If-Match"; + + private static final String IF_MODIFIED_SINCE = "If-Modified-Since"; + + private static final String IF_NONE_MATCH = "If-None-Match"; + + private static final String IF_RANGE = "If-Range"; + + private static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; + + private static final String LAST_MODIFIED = "Last-Modified"; + + private static final String LOCATION = "Location"; + + private static final String MAX_FORWARDS = "Max-Forwards"; + + private static final String PRAGMA = "Pragma"; + + private static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; + + private static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; + + private static final String RANGE = "Range"; + + private static final String REFERER = "Referer"; + + private static final String REFRESH = "Refresh"; + + private static final String RETRY_AFTER = "Retry-After"; + + private static final String SERVER = "Server"; + + private static final String SET_COOKIE = "Set-Cookie"; + + private static final String TE = "TE"; + + private static final String TRAILER = "Trailer"; + + private static final String TRANSFER_ENCODING = "Transfer-Encoding"; + + private static final String UPGRADE = "Upgrade"; + + private static final String USER_AGENT = "User-Agent"; + + private static final String VARY = "Vary"; + + private static final String VIA = "Via"; + + private static final String WARNING = "Warning"; + + private static final String WWW_AUTHENTICATE = "WWW-Authenticate"; + + private static final String[] HTTP_REQUEST_HEADER_NAMES = new String[] { + ACCEPT, + ACCEPT_CHARSET, + ACCEPT_ENCODING, + ACCEPT_LANGUAGE, + ACCEPT_RANGES, + AUTHORIZATION, + CACHE_CONTROL, + CONNECTION, + CONTENT_LENGTH, + CONTENT_TYPE, + COOKIE, + DATE, + EXPECT, + FROM, + HOST, + IF_MATCH, + IF_MODIFIED_SINCE, + IF_NONE_MATCH, + IF_RANGE, + IF_UNMODIFIED_SINCE, + MAX_FORWARDS, + PRAGMA, + PROXY_AUTHORIZATION, + RANGE, + REFERER, + TE, + UPGRADE, + USER_AGENT, + VIA, + WARNING + }; + + private static String[] HTTP_RESPONSE_HEADER_NAMES = new String[] { + ACCEPT_RANGES, + AGE, + ALLOW, + CACHE_CONTROL, + CONTENT_ENCODING, + CONTENT_LANGUAGE, + CONTENT_LENGTH, + CONTENT_LOCATION, + CONTENT_MD5, + CONTENT_RANGE, + CONTENT_TYPE, + DATE, + ETAG, + EXPIRES, + LAST_MODIFIED, + LOCATION, + PRAGMA, + PROXY_AUTHENTICATE, + REFRESH, + RETRY_AFTER, + SERVER, + SET_COOKIE, + TRAILER, + TRANSFER_ENCODING, + VARY, + VIA, + WARNING, + WWW_AUTHENTICATE + }; + private volatile String[] outboundHeaderNames = new String[0]; private volatile String[] inboundHeaderNames = new String[0]; + /** + * Provide the header names that should be mapped to an HTTP request (for outbound adapters) + * or HTTP response (for inbound adapters) from a Spring Integration Message's headers. + *

+ * Any non-standard headers will be prefixed by {@value #USER_DEFINED_HEADER_PREFIX} if not already. + */ public void setOutboundHeaderNames(String[] outboundHeaderNames) { this.outboundHeaderNames = (outboundHeaderNames != null) ? outboundHeaderNames : new String[0]; } + /** + * Provide the header names that should be mapped from an HTTP request (for inbound adapters) + * or HTTP response (for outbound adapters) to a Spring Integration Message's headers. + *

+ * This will match the header name directly or, for non-standard HTTP headers, it will match + * the header name prefixed by {@value #USER_DEFINED_HEADER_PREFIX}. + */ public void setInboundHeaderNames(String[] inboundHeaderNames) { this.inboundHeaderNames = (inboundHeaderNames != null) ? inboundHeaderNames : new String[0]; } + /** + * Map from the integration MessageHeaders to an HttpHeaders instance. + * Depending on which type of adapter is using this mapper, the HttpHeaders might be + * for an HTTP request (outbound adapter) or for an HTTP response (inbound adapter). + */ public void fromHeaders(MessageHeaders headers, HttpHeaders target) { for (String name : this.outboundHeaderNames) { Object value = headers.get(name); - String prefixedName = name.startsWith(USER_DEFINED_HEADER_PREFIX) ? name - : USER_DEFINED_HEADER_PREFIX + name; - if (value instanceof String) { - target.add(prefixedName, (String) value); - } - else if (value instanceof String[]) { - for (String next : (String[]) value) { - target.add(prefixedName, next); - } - } - else if (value instanceof Iterable) { - for (Object next : (Iterable) value) { - if (next instanceof String) { - target.add(prefixedName, (String) next); - } + if (value != null) { + if (!ObjectUtils.containsElement(HTTP_REQUEST_HEADER_NAMES, name) && !ObjectUtils.containsElement(HTTP_RESPONSE_HEADER_NAMES, name)) { + // prefix the user-defined header names if not already prefixed + name = name.startsWith(USER_DEFINED_HEADER_PREFIX) ? name : USER_DEFINED_HEADER_PREFIX + name; } + this.setHttpHeader(target, name, value); } } } + /** + * Map from an HttpHeaders instance to integration MessageHeaders. + * Depending on which type of adapter is using this mapper, the HttpHeaders might be + * from an HTTP request (inbound adapter) or from an HTTP response (outbound adapter). + */ public Map toHeaders(HttpHeaders source) { Map target = new HashMap(); for (String name : this.inboundHeaderNames) { - String prefixedName = name.startsWith(USER_DEFINED_HEADER_PREFIX) ? name - : USER_DEFINED_HEADER_PREFIX + name; - List values = source.containsKey(prefixedName) ? source.get(prefixedName) : source.get(name); - if (!CollectionUtils.isEmpty(values)) { - if (values.size() == 1) { - target.put(name, values.get(0)); + if (!ObjectUtils.containsElement(HTTP_REQUEST_HEADER_NAMES, name) && !ObjectUtils.containsElement(HTTP_RESPONSE_HEADER_NAMES, name)) { + String prefixedName = name.startsWith(USER_DEFINED_HEADER_PREFIX) ? name + : USER_DEFINED_HEADER_PREFIX + name; + Object value = source.containsKey(prefixedName) ? this.getHttpHeader(source, prefixedName) : this.getHttpHeader(source, name); + if (value != null) { + this.setMessageHeader(target, name, value); } - else { - target.put(name, values); + } + else { + Object value = this.getHttpHeader(source, name); + if (value != null) { + this.setMessageHeader(target, name, value); } } } return target; } + private void setHttpHeader(HttpHeaders target, String name, Object value) { + if (ACCEPT.equals(name)) { + if (value instanceof Collection) { + Collection values = (Collection) value; + if (!CollectionUtils.isEmpty(values)) { + List acceptableMediaTypes = new ArrayList(); + for (Object type : values) { + if (type instanceof MediaType) { + acceptableMediaTypes.add((MediaType) type); + } + else if (type instanceof String) { + acceptableMediaTypes.addAll(MediaType.parseMediaTypes((String) type)); + } + else { + Class clazz = (type != null) ? type.getClass() : null; + throw new IllegalArgumentException( + "Expected MediaType or String value for 'Accept' header value, but received: " + clazz); + } + } + target.setAccept(acceptableMediaTypes); + } + } + else if (value instanceof MediaType) { + target.setAccept(Collections.singletonList((MediaType) value)); + } + else if (value instanceof String[]) { + List acceptableMediaTypes = new ArrayList(); + for (String next : (String[]) value) { + acceptableMediaTypes.add(MediaType.parseMediaType(next)); + } + target.setAccept(acceptableMediaTypes); + } + else if (value instanceof String) { + target.setAccept(MediaType.parseMediaTypes((String) value)); + } + else { + Class clazz = (value != null) ? value.getClass() : null; + throw new IllegalArgumentException( + "Expected MediaType or String value for 'Accept' header value, but received: " + clazz); + } + } + else if (ACCEPT_CHARSET.equals(name)) { + if (value instanceof Collection) { + Collection values = (Collection) value; + if (!CollectionUtils.isEmpty(values)) { + List acceptableCharsets = new ArrayList(); + for (Object charset : values) { + if (charset instanceof Charset) { + acceptableCharsets.add((Charset) charset); + } + else if (charset instanceof String) { + acceptableCharsets.add(Charset.forName((String) charset)); + } + else { + Class clazz = (charset != null) ? charset.getClass() : null; + throw new IllegalArgumentException( + "Expected Charset or String value for 'Accept-Charset' header value, but received: " + clazz); + } + } + target.setAcceptCharset(acceptableCharsets); + } + } + else if (value instanceof Charset) { + target.setAcceptCharset(Collections.singletonList((Charset) value)); + } + else if (value instanceof String) { + target.setAcceptCharset(Collections.singletonList(Charset.forName((String) value))); + } + else { + Class clazz = (value != null) ? value.getClass() : null; + throw new IllegalArgumentException( + "Expected Charset or String value for 'Accept-Charset' header value, but received: " + clazz); + } + } + else if (ALLOW.equals(name)) { + if (value instanceof Collection) { + Collection values = (Collection) value; + if (!CollectionUtils.isEmpty(values)) { + Set allowedMethods = new HashSet(); + for (Object method : values) { + if (method instanceof HttpMethod) { + allowedMethods.add((HttpMethod) method); + } + else if (method instanceof String) { + allowedMethods.add(HttpMethod.valueOf((String) method)); + } + else { + Class clazz = (method != null) ? method.getClass() : null; + throw new IllegalArgumentException( + "Expected HttpMethod or String value for 'Allow' header value, but received: " + clazz); + } + } + target.setAllow(allowedMethods); + } + } + else { + if (value instanceof HttpMethod) { + target.setAllow(Collections.singleton((HttpMethod) value)); + } + else if (value instanceof String || value instanceof String[]) { + String[] values = (value instanceof String[]) ? (String[]) value + : StringUtils.commaDelimitedListToStringArray((String) value); + Set allowedMethods = new HashSet(); + for (String next : values) { + allowedMethods.add(HttpMethod.valueOf(next)); + } + target.setAllow(allowedMethods); + } + else { + Class clazz = (value != null) ? value.getClass() : null; + throw new IllegalArgumentException( + "Expected HttpMethod or String value for 'Allow' header value, but received: " + clazz); + } + } + } + else if (CACHE_CONTROL.equals(name)) { + if (value instanceof String) { + target.setCacheControl((String) value); + } + else { + Class clazz = (value != null) ? value.getClass() : null; + throw new IllegalArgumentException( + "Expected String value for 'Cache-Control' header value, but received: " + clazz); + } + } + else if (CONTENT_LENGTH.equals(name)) { + if (value instanceof Number) { + target.setContentLength(((Number) value).longValue()); + } + else if (value instanceof String) { + target.setContentLength(Long.parseLong((String) value)); + } + else { + Class clazz = (value != null) ? value.getClass() : null; + throw new IllegalArgumentException( + "Expected Number or String value for 'Content-Length' header value, but received: " + clazz); + } + } + else if (CONTENT_TYPE.equals(name)) { + if (value instanceof MediaType) { + target.setContentType((MediaType) value); + } + else if (value instanceof String) { + target.setContentType(MediaType.parseMediaType((String) value)); + } + else { + Class clazz = (value != null) ? value.getClass() : null; + throw new IllegalArgumentException( + "Expected MediaType or String value for 'Content-Type' header value, but received: " + clazz); + } + } + else if (DATE.equals(name)) { + if (value instanceof Date) { + target.setDate(((Date) value).getTime()); + } + else if (value instanceof Number) { + target.setDate(((Number) value).longValue()); + } + else if (value instanceof String) { + target.setDate(Long.parseLong((String) value)); + } + else { + Class clazz = (value != null) ? value.getClass() : null; + throw new IllegalArgumentException( + "Expected Date, Number, or String value for 'Date' header value, but received: " + clazz); + } + } + else if (ETAG.equals(name)) { + if (value instanceof String) { + target.setETag((String) value); + } + else { + Class clazz = (value != null) ? value.getClass() : null; + throw new IllegalArgumentException( + "Expected String value for 'ETag' header value, but received: " + clazz); + } + } + else if (EXPIRES.equals(name)) { + if (value instanceof Date) { + target.setExpires(((Date) value).getTime()); + } + else if (value instanceof Number) { + target.setExpires(((Number) value).longValue()); + } + else if (value instanceof String) { + target.setExpires(Long.parseLong((String) value)); + } + else { + Class clazz = (value != null) ? value.getClass() : null; + throw new IllegalArgumentException( + "Expected Date, Number, or String value for 'Expires' header value, but received: " + clazz); + } + } + else if (IF_MODIFIED_SINCE.equals(name)) { + if (value instanceof Date) { + target.setIfModifiedSince(((Date) value).getTime()); + } + else if (value instanceof Number) { + target.setIfModifiedSince(((Number) value).longValue()); + } + else if (value instanceof String) { + target.setIfModifiedSince(Long.parseLong((String) value)); + } + else { + Class clazz = (value != null) ? value.getClass() : null; + throw new IllegalArgumentException( + "Expected Date, Number, or String value for 'If-Modified-Since' header value, but received: " + clazz); + } + } + else if (IF_NONE_MATCH.equals(name)) { + if (value instanceof String) { + target.setIfNoneMatch((String) value); + } + else if (value instanceof Collection) { + Collection values = (Collection) value; + if (!CollectionUtils.isEmpty(values)) { + List ifNoneMatchList = new ArrayList(); + for (Object next : values) { + if (next instanceof String) { + ifNoneMatchList.add((String) next); + } + else { + Class clazz = (next != null) ? next.getClass() : null; + throw new IllegalArgumentException( + "Expected String value for 'If-None-Match' header value, but received: " + clazz); + } + } + target.setIfNoneMatch(ifNoneMatchList); + } + } + } + else if (LAST_MODIFIED.equals(name)) { + if (value instanceof Date) { + target.setLastModified(((Date) value).getTime()); + } + else if (value instanceof Number) { + target.setLastModified(((Number) value).longValue()); + } + else if (value instanceof String) { + target.setLastModified(Long.parseLong((String) value)); + } + else { + Class clazz = (value != null) ? value.getClass() : null; + throw new IllegalArgumentException( + "Expected Date, Number, or String value for 'Last-Modified' header value, but received: " + clazz); + } + } + else if (LOCATION.equals(name)) { + if (value instanceof URI) { + target.setLocation((URI) value); + } + else if (value instanceof String) { + try { + target.setLocation(new URI((String) value)); + } + catch (URISyntaxException e) { + throw new IllegalArgumentException(e); + } + } + else { + Class clazz = (value != null) ? value.getClass() : null; + throw new IllegalArgumentException( + "Expected URI or String value for 'Location' header value, but received: " + clazz); + } + } + else if (PRAGMA.equals(name)) { + if (value instanceof String) { + target.setPragma((String) value); + } + else { + Class clazz = (value != null) ? value.getClass() : null; + throw new IllegalArgumentException( + "Expected String value for 'Pragma' header value, but received: " + clazz); + } + } + else if (value instanceof String) { + target.set(name, (String) value); + } + else if (value instanceof String[]) { + for (String next : (String[]) value) { + target.add(name, next); + } + } + else if (value instanceof Iterable) { + for (Object next : (Iterable) value) { + if (next instanceof String) { + target.add(name, (String) next); + } + } + } + } + + private Object getHttpHeader(HttpHeaders source, String name) { + if (ACCEPT.equals(name)) { + return source.getAccept(); + } + else if (ACCEPT_CHARSET.equals(name)) { + return source.getAcceptCharset(); + } + else if (ALLOW.equals(name)) { + return source.getAllow(); + } + else if (CACHE_CONTROL.equals(name)) { + String cacheControl = source.getCacheControl(); + return (StringUtils.hasText(cacheControl)) ? cacheControl : null; + } + else if (CONTENT_LENGTH.equals(name)) { + long contentLength = source.getContentLength(); + return (contentLength > -1) ? contentLength : null; + } + else if (CONTENT_TYPE.equals(name)) { + return source.getContentType(); + } + else if (DATE.equals(name)) { + long date = source.getDate(); + return (date > -1) ? date : null; + } + else if (ETAG.equals(name)) { + String eTag = source.getETag(); + return (StringUtils.hasText(eTag)) ? eTag : null; + } + else if (EXPIRES.equals(name)) { + long expires = source.getExpires(); + return (expires > -1) ? expires : null; + } + else if (IF_NONE_MATCH.equals(name)) { + return source.getIfNoneMatch(); + } + else if (IF_UNMODIFIED_SINCE.equals(name)) { + long unmodifiedSince = source.getIfNotModifiedSince(); + return (unmodifiedSince > -1) ? unmodifiedSince : null; + } + else if (LAST_MODIFIED.equals(name)) { + long lastModified = source.getLastModified(); + return (lastModified > -1) ? lastModified : null; + } + else if (LOCATION.equals(name)) { + return source.getLocation(); + } + else if (PRAGMA.equals(name)) { + String pragma = source.getPragma(); + return (StringUtils.hasText(pragma)) ? pragma : null; + } + return source.get(name); + } + + private void setMessageHeader(Map target, String name, Object value) { + if (ObjectUtils.isArray(value)) { + Object[] values = ObjectUtils.toObjectArray(value); + if (!ObjectUtils.isEmpty(values)) { + if (values.length == 1) { + target.put(name, values); + } + else { + target.put(name, values[0]); + } + } + } + else if (value instanceof Collection) { + Collection values = (Collection) value; + if (!CollectionUtils.isEmpty(values)) { + if (values.size() == 1) { + target.put(name, values.iterator().next()); + } + else { + target.put(name, values); + } + } + } + else if (value != null) { + target.put(name, value); + } + } + + + /** + * Factory method for creating a basic outbound mapper instance. + * This will map all standard HTTP request headers when sending an HTTP request, + * and it will map all standard HTTP response headers when receiving an HTTP response. + */ + public static DefaultHttpHeaderMapper outboundMapper() { + DefaultHttpHeaderMapper mapper = new DefaultHttpHeaderMapper(); + mapper.setOutboundHeaderNames(HTTP_REQUEST_HEADER_NAMES); + mapper.setInboundHeaderNames(HTTP_RESPONSE_HEADER_NAMES); + return mapper; + } + + /** + * Factory method for creating a basic inbound mapper instance. + * This will map all standard HTTP request headers when receiving an HTTP request, + * and it will map all standard HTTP response headers when sending an HTTP response. + */ + public static DefaultHttpHeaderMapper inboundMapper() { + DefaultHttpHeaderMapper mapper = new DefaultHttpHeaderMapper(); + mapper.setInboundHeaderNames(HTTP_REQUEST_HEADER_NAMES); + mapper.setOutboundHeaderNames(HTTP_RESPONSE_HEADER_NAMES); + return mapper; + } + }