This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
commit d988d4790b834d0f5328954468b946c344563ddd Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Fri Feb 26 07:35:40 2021 +0100 CAMEL-16264: camel-http - Add option to ignore request headers --- .../apache/camel/catalog/docs/http-component.adoc | 3 +- .../component/http/HttpEndpointConfigurer.java | 6 ++ .../component/http/HttpEndpointUriFactory.java | 3 +- .../org/apache/camel/component/http/http.json | 1 + .../org/apache/camel/component/http/https.json | 1 + .../camel-http/src/main/docs/http-component.adoc | 3 +- .../apache/camel/component/http/HttpEndpoint.java | 21 +++- .../apache/camel/component/http/HttpProducer.java | 119 +++++++++++---------- .../endpoint/dsl/HttpEndpointBuilderFactory.java | 39 +++++++ .../modules/ROOT/pages/http-component.adoc | 3 +- 10 files changed, 139 insertions(+), 60 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/http-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/http-component.adoc index 61d5f5f..9171e16 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/http-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/http-component.adoc @@ -126,7 +126,7 @@ with the following path and query parameters: |=== -=== Query Parameters (49 parameters): +=== Query Parameters (50 parameters): [width="100%",cols="2,5,^1,2",options="header"] @@ -161,6 +161,7 @@ with the following path and query parameters: | *httpContext* (advanced) | To use a custom HttpContext instance | | HttpContext | *maxTotalConnections* (advanced) | The maximum number of connections. | 200 | int | *useSystemProperties* (advanced) | To use System Properties as fallback for configuration | false | boolean +| *skipRequestHeaders* (producer.advanced) | Whether to skip mapping all the Camel headers as HTTP request headers. If there are no data from Camel headers needed to be included in the HTTP request then this can avoid parsing overhead with many object allocations for the JVM garbage collector. | false | boolean | *skipResponseHeaders* (producer.advanced) | Whether to skip mapping all the HTTP response headers to Camel headers. If there are no data needed from HTTP headers then this can avoid parsing overhead with many object allocations for the JVM garbage collector. | false | boolean | *proxyAuthDomain* (proxy) | Proxy authentication domain to use with NTML | | String | *proxyAuthHost* (proxy) | Proxy authentication host | | String diff --git a/components/camel-http/src/generated/java/org/apache/camel/component/http/HttpEndpointConfigurer.java b/components/camel-http/src/generated/java/org/apache/camel/component/http/HttpEndpointConfigurer.java index 237aa97..ba95b5c 100644 --- a/components/camel-http/src/generated/java/org/apache/camel/component/http/HttpEndpointConfigurer.java +++ b/components/camel-http/src/generated/java/org/apache/camel/component/http/HttpEndpointConfigurer.java @@ -106,6 +106,8 @@ public class HttpEndpointConfigurer extends PropertyConfigurerSupport implements case "proxyHost": target.setProxyHost(property(camelContext, java.lang.String.class, value)); return true; case "proxyport": case "proxyPort": target.setProxyPort(property(camelContext, int.class, value)); return true; + case "skiprequestheaders": + case "skipRequestHeaders": target.setSkipRequestHeaders(property(camelContext, boolean.class, value)); return true; case "skipresponseheaders": case "skipResponseHeaders": target.setSkipResponseHeaders(property(camelContext, boolean.class, value)); return true; case "sslcontextparameters": @@ -210,6 +212,8 @@ public class HttpEndpointConfigurer extends PropertyConfigurerSupport implements case "proxyHost": return java.lang.String.class; case "proxyport": case "proxyPort": return int.class; + case "skiprequestheaders": + case "skipRequestHeaders": return boolean.class; case "skipresponseheaders": case "skipResponseHeaders": return boolean.class; case "sslcontextparameters": @@ -315,6 +319,8 @@ public class HttpEndpointConfigurer extends PropertyConfigurerSupport implements case "proxyHost": return target.getProxyHost(); case "proxyport": case "proxyPort": return target.getProxyPort(); + case "skiprequestheaders": + case "skipRequestHeaders": return target.isSkipRequestHeaders(); case "skipresponseheaders": case "skipResponseHeaders": return target.isSkipResponseHeaders(); case "sslcontextparameters": diff --git a/components/camel-http/src/generated/java/org/apache/camel/component/http/HttpEndpointUriFactory.java b/components/camel-http/src/generated/java/org/apache/camel/component/http/HttpEndpointUriFactory.java index 9b247bd..4e4a6d3 100644 --- a/components/camel-http/src/generated/java/org/apache/camel/component/http/HttpEndpointUriFactory.java +++ b/components/camel-http/src/generated/java/org/apache/camel/component/http/HttpEndpointUriFactory.java @@ -21,7 +21,7 @@ public class HttpEndpointUriFactory extends org.apache.camel.support.component.E private static final Set<String> PROPERTY_NAMES; private static final Set<String> SECRET_PROPERTY_NAMES; static { - Set<String> props = new HashSet<>(50); + Set<String> props = new HashSet<>(51); props.add("clientBuilder"); props.add("authMethodPriority"); props.add("ignoreResponseBody"); @@ -42,6 +42,7 @@ public class HttpEndpointUriFactory extends org.apache.camel.support.component.E props.add("connectionClose"); props.add("proxyHost"); props.add("authPassword"); + props.add("skipRequestHeaders"); props.add("lazyStartProducer"); props.add("preserveHostHeader"); props.add("httpClientConfigurer"); diff --git a/components/camel-http/src/generated/resources/org/apache/camel/component/http/http.json b/components/camel-http/src/generated/resources/org/apache/camel/component/http/http.json index fc55d55..3329ee8 100644 --- a/components/camel-http/src/generated/resources/org/apache/camel/component/http/http.json +++ b/components/camel-http/src/generated/resources/org/apache/camel/component/http/http.json @@ -90,6 +90,7 @@ "httpContext": { "kind": "parameter", "displayName": "Http Context", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.http.protocol.HttpContext", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom HttpContext instance" }, "maxTotalConnections": { "kind": "parameter", "displayName": "Max Total Connections", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 200, "description": "The maximum number of connections." }, "useSystemProperties": { "kind": "parameter", "displayName": "Use System Properties", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "To use System Properties as fallback for configuration" }, + "skipRequestHeaders": { "kind": "parameter", "displayName": "Skip Request Headers", "group": "producer.advanced", "label": "producer.advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to skip mapping all the Camel headers as HTTP request headers. If there are no data from Camel headers needed to be included in the HTTP request then this can avoid parsing overh [...] "skipResponseHeaders": { "kind": "parameter", "displayName": "Skip Response Headers", "group": "producer.advanced", "label": "producer.advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to skip mapping all the HTTP response headers to Camel headers. If there are no data needed from HTTP headers then this can avoid parsing overhead with many object allocations [...] "proxyAuthDomain": { "kind": "parameter", "displayName": "Proxy Auth Domain", "group": "proxy", "label": "producer,proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Proxy authentication domain to use with NTML" }, "proxyAuthHost": { "kind": "parameter", "displayName": "Proxy Auth Host", "group": "proxy", "label": "producer,proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Proxy authentication host" }, diff --git a/components/camel-http/src/generated/resources/org/apache/camel/component/http/https.json b/components/camel-http/src/generated/resources/org/apache/camel/component/http/https.json index b2e1527..209faf2 100644 --- a/components/camel-http/src/generated/resources/org/apache/camel/component/http/https.json +++ b/components/camel-http/src/generated/resources/org/apache/camel/component/http/https.json @@ -90,6 +90,7 @@ "httpContext": { "kind": "parameter", "displayName": "Http Context", "group": "advanced", "label": "advanced", "required": false, "type": "object", "javaType": "org.apache.http.protocol.HttpContext", "deprecated": false, "autowired": false, "secret": false, "description": "To use a custom HttpContext instance" }, "maxTotalConnections": { "kind": "parameter", "displayName": "Max Total Connections", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 200, "description": "The maximum number of connections." }, "useSystemProperties": { "kind": "parameter", "displayName": "Use System Properties", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "To use System Properties as fallback for configuration" }, + "skipRequestHeaders": { "kind": "parameter", "displayName": "Skip Request Headers", "group": "producer.advanced", "label": "producer.advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to skip mapping all the Camel headers as HTTP request headers. If there are no data from Camel headers needed to be included in the HTTP request then this can avoid parsing overh [...] "skipResponseHeaders": { "kind": "parameter", "displayName": "Skip Response Headers", "group": "producer.advanced", "label": "producer.advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to skip mapping all the HTTP response headers to Camel headers. If there are no data needed from HTTP headers then this can avoid parsing overhead with many object allocations [...] "proxyAuthDomain": { "kind": "parameter", "displayName": "Proxy Auth Domain", "group": "proxy", "label": "producer,proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Proxy authentication domain to use with NTML" }, "proxyAuthHost": { "kind": "parameter", "displayName": "Proxy Auth Host", "group": "proxy", "label": "producer,proxy", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Proxy authentication host" }, diff --git a/components/camel-http/src/main/docs/http-component.adoc b/components/camel-http/src/main/docs/http-component.adoc index 61d5f5f..9171e16 100644 --- a/components/camel-http/src/main/docs/http-component.adoc +++ b/components/camel-http/src/main/docs/http-component.adoc @@ -126,7 +126,7 @@ with the following path and query parameters: |=== -=== Query Parameters (49 parameters): +=== Query Parameters (50 parameters): [width="100%",cols="2,5,^1,2",options="header"] @@ -161,6 +161,7 @@ with the following path and query parameters: | *httpContext* (advanced) | To use a custom HttpContext instance | | HttpContext | *maxTotalConnections* (advanced) | The maximum number of connections. | 200 | int | *useSystemProperties* (advanced) | To use System Properties as fallback for configuration | false | boolean +| *skipRequestHeaders* (producer.advanced) | Whether to skip mapping all the Camel headers as HTTP request headers. If there are no data from Camel headers needed to be included in the HTTP request then this can avoid parsing overhead with many object allocations for the JVM garbage collector. | false | boolean | *skipResponseHeaders* (producer.advanced) | Whether to skip mapping all the HTTP response headers to Camel headers. If there are no data needed from HTTP headers then this can avoid parsing overhead with many object allocations for the JVM garbage collector. | false | boolean | *proxyAuthDomain* (proxy) | Proxy authentication domain to use with NTML | | String | *proxyAuthHost* (proxy) | Proxy authentication host | | String diff --git a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpEndpoint.java b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpEndpoint.java index 52e6c42..2ddb4c6 100644 --- a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpEndpoint.java +++ b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpEndpoint.java @@ -138,8 +138,14 @@ public class HttpEndpoint extends HttpCommonEndpoint { + "be ignored. When set will override host header derived from url.") private String customHostHeader; @UriParam(label = "producer.advanced", + description = "Whether to skip mapping all the Camel headers as HTTP request headers." + + " If there are no data from Camel headers needed to be included in the HTTP request then this can avoid" + + " parsing overhead with many object allocations for the JVM garbage collector.") + private boolean skipRequestHeaders; + @UriParam(label = "producer.advanced", description = "Whether to skip mapping all the HTTP response headers to Camel headers." - + " If there are no data needed from HTTP headers then this can avoid parsing overhead with many object allocations for the JVM garbage collector.") + + " If there are no data needed from HTTP headers then this can avoid parsing overhead" + + " with many object allocations for the JVM garbage collector.") private boolean skipResponseHeaders; public HttpEndpoint() { @@ -537,6 +543,19 @@ public class HttpEndpoint extends HttpCommonEndpoint { return customHostHeader; } + public boolean isSkipRequestHeaders() { + return skipRequestHeaders; + } + + /** + * Whether to skip mapping all the Camel headers as HTTP request headers. If there are no data from Camel headers + * needed to be included in the HTTP request then this can avoid parsing overhead with many object allocations for + * the JVM garbage collector. + */ + public void setSkipRequestHeaders(boolean skipRequestHeaders) { + this.skipRequestHeaders = skipRequestHeaders; + } + public boolean isSkipResponseHeaders() { return skipResponseHeaders; } diff --git a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java index e1869eb..7e53412 100644 --- a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java +++ b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java @@ -84,6 +84,8 @@ public class HttpProducer extends DefaultProducer { private static final Logger LOG = LoggerFactory.getLogger(HttpProducer.class); + private static final Integer OK_RESPONSE_CODE = 200; + private HttpClient httpClient; private final HttpContext httpContext; private final boolean throwException; @@ -156,68 +158,67 @@ public class HttpProducer extends DefaultProducer { int[] version = HttpHelper.parserHttpVersion(httpProtocolVersion); httpRequest.setProtocolVersion(new HttpVersion(version[0], version[1])); } - HeaderFilterStrategy strategy = getEndpoint().getHeaderFilterStrategy(); - if (getEndpoint().getCustomHostHeader() != null) { - httpRequest.setHeader(HOST, getEndpoint().getCustomHostHeader()); - } + HeaderFilterStrategy strategy = getEndpoint().getHeaderFilterStrategy(); - // propagate headers as HTTP headers - if (strategy != null) { - final TypeConverter tc = exchange.getContext().getTypeConverter(); - for (Map.Entry<String, Object> entry : in.getHeaders().entrySet()) { - String key = entry.getKey(); - // we should not add headers for the parameters in the uri if we bridge the endpoint - // as then we would duplicate headers on both the endpoint uri, and in HTTP headers as well - if (skipRequestHeaders != null && skipRequestHeaders.containsKey(key)) { - continue; - } - Object headerValue = entry.getValue(); - - if (headerValue != null) { - if (headerValue instanceof String || headerValue instanceof Integer || headerValue instanceof Long - || headerValue instanceof Boolean || headerValue instanceof Date) { - // optimise for common types - String value = headerValue.toString(); - if (!strategy.applyFilterToCamelHeaders(key, value, exchange)) { - httpRequest.addHeader(key, value); - } + if (!getEndpoint().isSkipRequestHeaders()) { + // propagate headers as HTTP headers + if (strategy != null) { + final TypeConverter tc = exchange.getContext().getTypeConverter(); + for (Map.Entry<String, Object> entry : in.getHeaders().entrySet()) { + String key = entry.getKey(); + // we should not add headers for the parameters in the uri if we bridge the endpoint + // as then we would duplicate headers on both the endpoint uri, and in HTTP headers as well + if (skipRequestHeaders != null && skipRequestHeaders.containsKey(key)) { continue; } + Object headerValue = entry.getValue(); + + if (headerValue != null) { + if (headerValue instanceof String || headerValue instanceof Integer || headerValue instanceof Long + || headerValue instanceof Boolean || headerValue instanceof Date) { + // optimise for common types + String value = headerValue.toString(); + if (!strategy.applyFilterToCamelHeaders(key, value, exchange)) { + httpRequest.addHeader(key, value); + } + continue; + } - // use an iterator as there can be multiple values. (must not use a delimiter, and allow empty values) - final Iterator<?> it = ObjectHelper.createIterator(headerValue, null, true); - - // the value to add as request header - List<String> multiValues = null; - String prev = null; - - // if its a multi value then check each value if we can add it and for multi values they - // should be combined into a single value - while (it.hasNext()) { - String value = tc.convertTo(String.class, it.next()); - if (value != null && !strategy.applyFilterToCamelHeaders(key, value, exchange)) { - if (prev == null) { - prev = value; - } else { - // only create array for multi values when really needed - if (multiValues == null) { - multiValues = new ArrayList<>(); - multiValues.add(prev); + // use an iterator as there can be multiple values. (must not use a delimiter, and allow empty values) + final Iterator<?> it = ObjectHelper.createIterator(headerValue, null, true); + + // the value to add as request header + List<String> multiValues = null; + String prev = null; + + // if its a multi value then check each value if we can add it and for multi values they + // should be combined into a single value + while (it.hasNext()) { + String value = tc.convertTo(String.class, it.next()); + if (value != null && !strategy.applyFilterToCamelHeaders(key, value, exchange)) { + if (prev == null) { + prev = value; + } else { + // only create array for multi values when really needed + if (multiValues == null) { + multiValues = new ArrayList<>(); + multiValues.add(prev); + } + multiValues.add(value); } - multiValues.add(value); } } - } - // add the value(s) as a http request header - if (multiValues != null) { - // use the default toString of a ArrayList to create in the form [xxx, yyy] - // if multi valued, for a single value, then just output the value as is - String s = multiValues.size() > 1 ? multiValues.toString() : multiValues.get(0); - httpRequest.addHeader(key, s); - } else if (prev != null) { - httpRequest.addHeader(key, prev); + // add the value(s) as a http request header + if (multiValues != null) { + // use the default toString of a ArrayList to create in the form [xxx, yyy] + // if multi valued, for a single value, then just output the value as is + String s = multiValues.size() > 1 ? multiValues.toString() : multiValues.get(0); + httpRequest.addHeader(key, s); + } else if (prev != null) { + httpRequest.addHeader(key, prev); + } } } } @@ -235,13 +236,16 @@ public class HttpProducer extends DefaultProducer { } } + if (getEndpoint().getCustomHostHeader() != null) { + httpRequest.setHeader(HOST, getEndpoint().getCustomHostHeader()); + } //In reverse proxy applications it can be desirable for the downstream service to see the original Host header //if this option is set, and the exchange Host header is not null, we will set it's current value on the httpRequest if (getEndpoint().isPreserveHostHeader()) { String hostHeader = exchange.getIn().getHeader("Host", String.class); if (hostHeader != null) { //HttpClient 4 will check to see if the Host header is present, and use it if it is, see org.apache.http.protocol.RequestTargetHost in httpcore - httpRequest.setHeader("Host", hostHeader); + httpRequest.setHeader(HOST, hostHeader); } } @@ -317,7 +321,12 @@ public class HttpProducer extends DefaultProducer { Object response = extractResponseBody(httpRequest, httpResponse, exchange, getEndpoint().isIgnoreResponseBody()); Message answer = exchange.getOut(); - answer.setHeader(Exchange.HTTP_RESPONSE_CODE, responseCode); + // optimize for 200 response code as the boxing is outside the cached integers + if (responseCode == 200) { + answer.setHeader(Exchange.HTTP_RESPONSE_CODE, OK_RESPONSE_CODE); + } else { + answer.setHeader(Exchange.HTTP_RESPONSE_CODE, responseCode); + } if (httpResponse.getStatusLine() != null) { answer.setHeader(Exchange.HTTP_RESPONSE_TEXT, httpResponse.getStatusLine().getReasonPhrase()); } diff --git a/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/HttpEndpointBuilderFactory.java b/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/HttpEndpointBuilderFactory.java index 2561783..d3e0b1e 100644 --- a/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/HttpEndpointBuilderFactory.java +++ b/core/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/HttpEndpointBuilderFactory.java @@ -1440,6 +1440,45 @@ public interface HttpEndpointBuilderFactory { return this; } /** + * Whether to skip mapping all the Camel headers as HTTP request + * headers. If there are no data from Camel headers needed to be + * included in the HTTP request then this can avoid parsing overhead + * with many object allocations for the JVM garbage collector. + * + * The option is a: <code>boolean</code> type. + * + * Default: false + * Group: producer.advanced + * + * @param skipRequestHeaders the value to set + * @return the dsl builder + */ + default AdvancedHttpEndpointBuilder skipRequestHeaders( + boolean skipRequestHeaders) { + doSetProperty("skipRequestHeaders", skipRequestHeaders); + return this; + } + /** + * Whether to skip mapping all the Camel headers as HTTP request + * headers. If there are no data from Camel headers needed to be + * included in the HTTP request then this can avoid parsing overhead + * with many object allocations for the JVM garbage collector. + * + * The option will be converted to a <code>boolean</code> + * type. + * + * Default: false + * Group: producer.advanced + * + * @param skipRequestHeaders the value to set + * @return the dsl builder + */ + default AdvancedHttpEndpointBuilder skipRequestHeaders( + String skipRequestHeaders) { + doSetProperty("skipRequestHeaders", skipRequestHeaders); + return this; + } + /** * Whether to skip mapping all the HTTP response headers to Camel * headers. If there are no data needed from HTTP headers then this can * avoid parsing overhead with many object allocations for the JVM diff --git a/docs/components/modules/ROOT/pages/http-component.adoc b/docs/components/modules/ROOT/pages/http-component.adoc index 3d3f2a6..e9e0a90 100644 --- a/docs/components/modules/ROOT/pages/http-component.adoc +++ b/docs/components/modules/ROOT/pages/http-component.adoc @@ -128,7 +128,7 @@ with the following path and query parameters: |=== -=== Query Parameters (49 parameters): +=== Query Parameters (50 parameters): [width="100%",cols="2,5,^1,2",options="header"] @@ -163,6 +163,7 @@ with the following path and query parameters: | *httpContext* (advanced) | To use a custom HttpContext instance | | HttpContext | *maxTotalConnections* (advanced) | The maximum number of connections. | 200 | int | *useSystemProperties* (advanced) | To use System Properties as fallback for configuration | false | boolean +| *skipRequestHeaders* (producer.advanced) | Whether to skip mapping all the Camel headers as HTTP request headers. If there are no data from Camel headers needed to be included in the HTTP request then this can avoid parsing overhead with many object allocations for the JVM garbage collector. | false | boolean | *skipResponseHeaders* (producer.advanced) | Whether to skip mapping all the HTTP response headers to Camel headers. If there are no data needed from HTTP headers then this can avoid parsing overhead with many object allocations for the JVM garbage collector. | false | boolean | *proxyAuthDomain* (proxy) | Proxy authentication domain to use with NTML | | String | *proxyAuthHost* (proxy) | Proxy authentication host | | String