You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ni...@apache.org on 2014/09/05 15:13:26 UTC
[8/9] git commit: CAMEL-7782 Added camel-netty4-http component
CAMEL-7782 Added camel-netty4-http component
Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/db88eeda
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/db88eeda
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/db88eeda
Branch: refs/heads/master
Commit: db88eeda8e7c40b00b9f9ff015ed0b54e6922ea9
Parents: 8c5529f
Author: Willem Jiang <wi...@gmail.com>
Authored: Fri Sep 5 20:55:54 2014 +0800
Committer: Willem Jiang <wi...@gmail.com>
Committed: Fri Sep 5 20:55:54 2014 +0800
----------------------------------------------------------------------
.../apache/camel/impl/DefaultCamelContext.java | 2 +
components/camel-netty4-http/pom.xml | 86 +++
.../netty4/http/ContextPathMatcher.java | 55 ++
.../netty4/http/DefaultContextPathMatcher.java | 88 +++
.../netty4/http/DefaultNettyHttpBinding.java | 550 +++++++++++++++++++
.../http/DefaultNettySharedHttpServer.java | 132 +++++
.../netty4/http/HttpClientPipelineFactory.java | 165 ++++++
.../component/netty4/http/HttpPrincipal.java | 52 ++
.../netty4/http/HttpServerBootstrapFactory.java | 104 ++++
.../http/HttpServerConsumerChannelFactory.java | 63 +++
.../netty4/http/HttpServerPipelineFactory.java | 172 ++++++
.../http/HttpServerSharedPipelineFactory.java | 159 ++++++
.../netty4/http/JAASSecurityAuthenticator.java | 72 +++
.../http/NettyChannelBufferStreamCache.java | 99 ++++
.../component/netty4/http/NettyHttpBinding.java | 119 ++++
.../netty4/http/NettyHttpComponent.java | 303 ++++++++++
.../netty4/http/NettyHttpConfiguration.java | 166 ++++++
.../netty4/http/NettyHttpConstants.java | 31 ++
.../netty4/http/NettyHttpConsumer.java | 75 +++
.../netty4/http/NettyHttpConverter.java | 118 ++++
.../netty4/http/NettyHttpEndpoint.java | 198 +++++++
.../http/NettyHttpHeaderFilterStrategy.java | 53 ++
.../component/netty4/http/NettyHttpHelper.java | 243 ++++++++
.../component/netty4/http/NettyHttpMessage.java | 51 ++
.../http/NettyHttpOperationFailedException.java | 77 +++
.../netty4/http/NettyHttpProducer.java | 120 ++++
.../http/NettyHttpSecurityConfiguration.java | 115 ++++
.../netty4/http/NettySharedHttpServer.java | 74 +++
...ySharedHttpServerBootstrapConfiguration.java | 50 ++
.../netty4/http/RestContextPathMatcher.java | 102 ++++
.../netty4/http/RestNettyHttpBinding.java | 92 ++++
.../netty4/http/SecurityAuthenticator.java | 76 +++
.../http/SecurityAuthenticatorSupport.java | 127 +++++
.../netty4/http/SecurityConstraint.java | 31 ++
.../netty4/http/SecurityConstraintMapping.java | 133 +++++
.../http/handlers/HttpClientChannelHandler.java | 46 ++
.../http/handlers/HttpServerChannelHandler.java | 316 +++++++++++
.../HttpServerMultiplexChannelHandler.java | 235 ++++++++
.../src/main/resources/META-INF/LICENSE.txt | 203 +++++++
.../src/main/resources/META-INF/NOTICE.txt | 11 +
.../services/org/apache/camel/TypeConverter | 18 +
.../org/apache/camel/component/netty4-http | 17 +
.../component/netty4/http/BaseNettyTest.java | 95 ++++
.../netty4/http/ManagedNettyEndpointTest.java | 81 +++
.../component/netty4/http/MyLoginModule.java | 102 ++++
.../component/netty4/http/MyRolePrincipal.java | 33 ++
.../netty4/http/NettyHttp500ErrorTest.java | 66 +++
...yHttp500ErrorThrowExceptionOnServerTest.java | 67 +++
...ttpAccessHttpRequestAndResponseBeanTest.java | 74 +++
.../NettyHttpAccessHttpRequestBeanTest.java | 54 ++
.../http/NettyHttpAccessHttpRequestTest.java | 59 ++
.../NettyHttpBasicAuthConstraintMapperTest.java | 96 ++++
...asicAuthCustomSecurityAuthenticatorTest.java | 105 ++++
.../netty4/http/NettyHttpBasicAuthTest.java | 104 ++++
...ndingPreservePostFormUrlEncodedBodyTest.java | 68 +++
.../http/NettyHttpBridgeEncodedPathTest.java | 66 +++
...NettyHttpBridgeRouteUsingHttpClientTest.java | 92 ++++
.../http/NettyHttpCharacterEncodingTest.java | 63 +++
.../netty4/http/NettyHttpClientChunkedTest.java | 46 ++
.../http/NettyHttpClientExpectContinueTest.java | 58 ++
...ponentConfigurationAndDocumentationTest.java | 57 ++
.../netty4/http/NettyHttpContentTypeTest.java | 87 +++
...ettyHttpConvertPayloadToInputStreamTest.java | 62 +++
...dpointUriCustomHeaderFilterStrategyTest.java | 70 +++
.../NettyHttpEndpointUriEncodingIssueTest.java | 57 ++
...ntUriEncodingIssueUrlDecodeDisabledTest.java | 49 ++
.../http/NettyHttpFilterCamelHeadersTest.java | 76 +++
.../NettyHttpGetWithInvalidMessageTest.java | 106 ++++
...ttyHttpGetWithParamAsExchangeHeaderTest.java | 127 +++++
.../netty4/http/NettyHttpGetWithParamTest.java | 78 +++
.../netty4/http/NettyHttpHandle404Test.java | 92 ++++
.../netty4/http/NettyHttpHeaderCaseTest.java | 73 +++
...ettyHttpHeaderFilterStrategyRemovalTest.java | 78 +++
.../http/NettyHttpHeaderFilterStrategyTest.java | 106 ++++
.../netty4/http/NettyHttpHeadersTest.java | 54 ++
.../http/NettyHttpMapHeadersFalseTest.java | 70 +++
.../http/NettyHttpMethodRestrictTest.java | 74 +++
.../http/NettyHttpOnExceptionHandledTest.java | 56 ++
...tpProducerBridgePathWithSpacesAtEndTest.java | 54 ++
.../http/NettyHttpProducerBridgeTest.java | 50 ++
.../http/NettyHttpProducerConcurrentTest.java | 87 +++
.../http/NettyHttpProducerKeepAliveTest.java | 64 +++
.../http/NettyHttpProducerQueryParamTest.java | 75 +++
.../NettyHttpProducerSendEmptyHeaderTest.java | 48 ++
.../http/NettyHttpProducerSimpleGetTest.java | 69 +++
.../http/NettyHttpProducerSimpleTest.java | 76 +++
...ttpProducerTwoParametersWithSameKeyTest.java | 100 ++++
.../http/NettyHttpProducerWithHeaderTest.java | 60 ++
.../netty4/http/NettyHttpRawQueryTest.java | 57 ++
.../http/NettyHttpRedirectNoLocationTest.java | 59 ++
.../netty4/http/NettyHttpRedirectTest.java | 56 ++
.../http/NettyHttpRequestTimeoutTest.java | 62 +++
...ReturnDataNotInputStreamConvertableTest.java | 53 ++
.../netty4/http/NettyHttpReturnFaultTest.java | 58 ++
.../component/netty4/http/NettyHttpSSLTest.java | 109 ++++
...ettyHttpSameHostDifferentParametersTest.java | 52 ++
...HttpSimpleBasicAuthConstraintMapperTest.java | 90 +++
.../http/NettyHttpSimpleBasicAuthTest.java | 70 +++
.../netty4/http/NettyHttpSimpleTest.java | 46 ++
.../http/NettyHttpSimpleUriParametersTest.java | 46 ++
.../NettyHttpStreamCacheFileResponseTest.java | 75 +++
.../http/NettyHttpSuspendResume503Test.java | 74 +++
.../netty4/http/NettyHttpSuspendResumeTest.java | 73 +++
.../netty4/http/NettyHttpTraceDisabledTest.java | 62 +++
.../http/NettyHttpTransferExceptionTest.java | 52 ++
...HttpTwoRoutesBootstrapConfigurationTest.java | 93 ++++
.../NettyHttpTwoRoutesMatchOnUriPrefixTest.java | 77 +++
.../NettyHttpTwoRoutesStopOneRouteTest.java | 77 +++
.../netty4/http/NettyHttpTwoRoutesTest.java | 54 ++
...outesValidateBootstrapConfigurationTest.java | 52 ++
.../http/NettyHttpXMLXPathResponseTest.java | 53 ++
.../netty4/http/NettyHttpXMLXPathTest.java | 53 ++
.../http/NettyRecipientListHttpBaseTest.java | 53 ++
.../netty4/http/NettyRouteSimpleTest.java | 51 ++
.../netty4/http/NettySharedHttpServerTest.java | 90 +++
.../http/NettyUseRawHttpResponseTest.java | 63 +++
.../http/SecurityConstraintMappingTest.java | 108 ++++
.../http/SpringNettyHttpBasicAuthTest.java | 118 ++++
.../netty4/http/SpringNettyHttpSSLTest.java | 79 +++
.../component/netty4/http/rest/CountryPojo.java | 40 ++
...estNettyHttpBindingModeAutoWithJsonTest.java | 59 ++
...RestNettyHttpBindingModeAutoWithXmlTest.java | 59 ++
.../rest/RestNettyHttpBindingModeJsonTest.java | 76 +++
.../rest/RestNettyHttpBindingModeXmlTest.java | 77 +++
.../RestNettyHttpContextPathMatchGetTest.java | 68 +++
.../netty4/http/rest/RestNettyHttpGetTest.java | 56 ++
.../http/rest/RestNettyHttpPojoInOutTest.java | 53 ++
.../rest/RestNettyHttpPostJsonJaxbPojoTest.java | 61 ++
.../rest/RestNettyHttpPostJsonPojoListTest.java | 68 +++
.../rest/RestNettyHttpPostJsonPojoTest.java | 61 ++
.../rest/RestNettyHttpPostXmlJaxbPojoTest.java | 79 +++
.../netty4/http/rest/RestPathMatchingTest.java | 86 +++
.../netty4/http/rest/UserJaxbPojo.java | 48 ++
.../component/netty4/http/rest/UserPojo.java | 40 ++
.../component/netty4/http/rest/UserService.java | 33 ++
.../src/test/resources/jsse/localhost.ks | Bin 0 -> 1265 bytes
.../src/test/resources/log4j.properties | 39 ++
.../src/test/resources/myjaas.config | 5 +
.../http/SpringNettyHttpBasicAuthTest.xml | 70 +++
.../netty4/http/SpringNettyHttpSSLTest.xml | 62 +++
components/pom.xml | 1 +
parent/pom.xml | 5 +
142 files changed, 11644 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/camel/blob/db88eeda/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
----------------------------------------------------------------------
diff --git a/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java b/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
index f825a25..7dd681d 100644
--- a/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
+++ b/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
@@ -1101,6 +1101,8 @@ public class DefaultCamelContext extends ServiceSupport implements ModelCamelCon
return "atmosphere/websocket";
} else if ("netty-http".equals(componentName)) {
return "netty/http";
+ } else if ("netty4-http".equals(componentName)) {
+ return "netty4/http";
}
return componentName.replaceAll("-", "");
}
http://git-wip-us.apache.org/repos/asf/camel/blob/db88eeda/components/camel-netty4-http/pom.xml
----------------------------------------------------------------------
diff --git a/components/camel-netty4-http/pom.xml b/components/camel-netty4-http/pom.xml
new file mode 100644
index 0000000..47e8269
--- /dev/null
+++ b/components/camel-netty4-http/pom.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>components</artifactId>
+ <version>2.14-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>camel-netty4-http</artifactId>
+ <packaging>bundle</packaging>
+ <name>Camel :: Netty4 HTTP</name>
+ <description>Camel Netty4 HTTP support</description>
+
+ <properties>
+ <camel.osgi.export.pkg>
+ org.apache.camel.component.netty4.http.*
+ </camel.osgi.export.pkg>
+ <camel.osgi.export.service>org.apache.camel.spi.ComponentResolver;component=netty4-http</camel.osgi.export.service>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-netty4</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-codec-http</artifactId>
+ <version>${netty-version}</version>
+ </dependency>
+
+ <!-- testing -->
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test-spring</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-http</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <!-- for testing rest-dsl -->
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-jackson</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-jaxb</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- logging -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+</project>
http://git-wip-us.apache.org/repos/asf/camel/blob/db88eeda/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/ContextPathMatcher.java
----------------------------------------------------------------------
diff --git a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/ContextPathMatcher.java b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/ContextPathMatcher.java
new file mode 100644
index 0000000..1b58af8
--- /dev/null
+++ b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/ContextPathMatcher.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.netty4.http;
+
+/**
+ * A matcher used for selecting the correct {@link org.apache.camel.component.netty.http.handlers.HttpServerChannelHandler}
+ * to handle an incoming {@link io.netty.handler.codec.http.HttpRequest} when you use multiple routes on the same
+ * port.
+ * <p/>
+ * As when we do that, we need to multiplex and select the correct consumer route to process the HTTP request.
+ * To do that we need to match on the incoming HTTP request context-path from the request.
+ */
+public interface ContextPathMatcher {
+
+ /**
+ * Whether the target context-path matches a regular url.
+ *
+ * @param path the context-path from the incoming HTTP request
+ * @return <tt>true</tt> to match, <tt>false</tt> if not.
+ */
+ boolean matches(String path);
+
+ /**
+ * Whether the target context-path matches a REST url.
+ *
+ * @param path the context-path from the incoming HTTP request
+ * @param wildcard whether to match strict or by wildcards
+ * @return <tt>true</tt> to match, <tt>false</tt> if not.
+ */
+ boolean matchesRest(String path, boolean wildcard);
+
+ /**
+ * Matches the given request HTTP method with the configured HTTP method of the consumer
+ *
+ * @param method the request HTTP method
+ * @param restrict the consumer configured HTTP restrict method
+ * @return <tt>true</tt> if matched, <tt>false</tt> otherwise
+ */
+ boolean matchMethod(String method, String restrict);
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/db88eeda/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/DefaultContextPathMatcher.java
----------------------------------------------------------------------
diff --git a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/DefaultContextPathMatcher.java b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/DefaultContextPathMatcher.java
new file mode 100644
index 0000000..f30cc66
--- /dev/null
+++ b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/DefaultContextPathMatcher.java
@@ -0,0 +1,88 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.netty4.http;
+
+import java.util.Locale;
+
+/**
+ * A default {@link ContextPathMatcher} which supports the <tt>matchOnUriPrefix</tt> option.
+ */
+public class DefaultContextPathMatcher implements ContextPathMatcher {
+
+ private final String path;
+ private final boolean matchOnUriPrefix;
+
+ public DefaultContextPathMatcher(String path, boolean matchOnUriPrefix) {
+ this.path = path.toLowerCase(Locale.US);
+ this.matchOnUriPrefix = matchOnUriPrefix;
+ }
+
+ @Override
+ public boolean matches(String path) {
+ path = path.toLowerCase(Locale.US);
+ if (!matchOnUriPrefix) {
+ // exact match
+ return path.equals(this.path);
+ } else {
+ // match on prefix, then we just need to match the start of the context-path
+ return path.startsWith(this.path);
+ }
+ }
+
+ @Override
+ public boolean matchesRest(String path, boolean wildcard) {
+ return false;
+ }
+
+ @Override
+ public boolean matchMethod(String method, String restrict) {
+ // always match as HttpServerChannelHandler will deal with HTTP method restrictions
+ return true;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ DefaultContextPathMatcher that = (DefaultContextPathMatcher) o;
+
+ if (matchOnUriPrefix != that.matchOnUriPrefix) {
+ return false;
+ }
+ if (!path.equals(that.path)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = path.hashCode();
+ result = 31 * result + (matchOnUriPrefix ? 1 : 0);
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/db88eeda/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/DefaultNettyHttpBinding.java
----------------------------------------------------------------------
diff --git a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/DefaultNettyHttpBinding.java b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/DefaultNettyHttpBinding.java
new file mode 100644
index 0000000..42e4bec
--- /dev/null
+++ b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/DefaultNettyHttpBinding.java
@@ -0,0 +1,550 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.netty4.http;
+
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.nio.charset.Charset;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.handler.codec.http.DefaultFullHttpRequest;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
+import io.netty.handler.codec.http.DefaultHttpRequest;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpHeaders;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpRequest;
+import io.netty.handler.codec.http.HttpResponse;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.HttpVersion;
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.NoTypeConversionAvailableException;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.TypeConverter;
+import org.apache.camel.component.netty4.NettyConverter;
+import org.apache.camel.spi.HeaderFilterStrategy;
+import org.apache.camel.util.ExchangeHelper;
+import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.MessageHelper;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.URISupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Default {@link NettyHttpBinding}.
+ */
+public class DefaultNettyHttpBinding implements NettyHttpBinding, Cloneable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultNettyHttpBinding.class);
+ private HeaderFilterStrategy headerFilterStrategy = new NettyHttpHeaderFilterStrategy();
+
+ public DefaultNettyHttpBinding() {
+ }
+
+ public DefaultNettyHttpBinding(HeaderFilterStrategy headerFilterStrategy) {
+ this.headerFilterStrategy = headerFilterStrategy;
+ }
+
+ public DefaultNettyHttpBinding copy() {
+ try {
+ return (DefaultNettyHttpBinding)this.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeCamelException(e);
+ }
+ }
+
+ @Override
+ public Message toCamelMessage(FullHttpRequest request, Exchange exchange, NettyHttpConfiguration configuration) throws Exception {
+ LOG.trace("toCamelMessage: {}", request);
+
+ NettyHttpMessage answer = new NettyHttpMessage(request, null);
+ answer.setExchange(exchange);
+ if (configuration.isMapHeaders()) {
+ populateCamelHeaders(request, answer.getHeaders(), exchange, configuration);
+ }
+
+ if (configuration.isDisableStreamCache()) {
+ // keep the body as is, and use type converters
+ answer.setBody(request.content());
+ } else {
+ // turn the body into stream cached
+ NettyChannelBufferStreamCache cache = new NettyChannelBufferStreamCache(request.content());
+ answer.setBody(cache);
+ }
+ return answer;
+ }
+
+ @Override
+ public void populateCamelHeaders(FullHttpRequest request, Map<String, Object> headers, Exchange exchange, NettyHttpConfiguration configuration) throws Exception {
+ LOG.trace("populateCamelHeaders: {}", request);
+
+ // NOTE: these headers is applied using the same logic as camel-http/camel-jetty to be consistent
+
+ headers.put(Exchange.HTTP_METHOD, request.getMethod().name());
+ // strip query parameters from the uri
+ String s = request.getUri();
+ if (s.contains("?")) {
+ s = ObjectHelper.before(s, "?");
+ }
+
+ // we want the full path for the url, as the client may provide the url in the HTTP headers as absolute or relative, eg
+ // /foo
+ // http://servername/foo
+ String http = configuration.isSsl() ? "https://" : "http://";
+ if (!s.startsWith(http)) {
+ if (configuration.getPort() != 80) {
+ s = http + configuration.getHost() + ":" + configuration.getPort() + s;
+ } else {
+ s = http + configuration.getHost() + s;
+ }
+ }
+
+ headers.put(Exchange.HTTP_URL, s);
+ // uri is without the host and port
+ URI uri = new URI(request.getUri());
+ // uri is path and query parameters
+ headers.put(Exchange.HTTP_URI, uri.getPath());
+ headers.put(Exchange.HTTP_QUERY, uri.getQuery());
+ headers.put(Exchange.HTTP_RAW_QUERY, uri.getRawQuery());
+
+ // strip the starting endpoint path so the path is relative to the endpoint uri
+ String path = uri.getPath();
+ if (configuration.getPath() != null && path.startsWith(configuration.getPath())) {
+ path = path.substring(configuration.getPath().length());
+ }
+ headers.put(Exchange.HTTP_PATH, path);
+
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("HTTP-Method {}", request.getMethod().name());
+ LOG.trace("HTTP-Uri {}", request.getUri());
+ }
+
+ for (String name : request.headers().names()) {
+ // mapping the content-type
+ if (name.toLowerCase(Locale.US).equals("content-type")) {
+ name = Exchange.CONTENT_TYPE;
+ }
+
+ if (name.toLowerCase(Locale.US).equals("authorization")) {
+ String value = request.headers().get(name);
+ // store a special header that this request was authenticated using HTTP Basic
+ if (value != null && value.trim().startsWith("Basic")) {
+ if (headerFilterStrategy != null
+ && !headerFilterStrategy.applyFilterToExternalHeaders(NettyHttpConstants.HTTP_AUTHENTICATION, "Basic", exchange)) {
+ NettyHttpHelper.appendHeader(headers, NettyHttpConstants.HTTP_AUTHENTICATION, "Basic");
+ }
+ }
+ }
+
+ // add the headers one by one, and use the header filter strategy
+ List<String> values = request.headers().getAll(name);
+ Iterator<?> it = ObjectHelper.createIterator(values);
+ while (it.hasNext()) {
+ Object extracted = it.next();
+ Object decoded = shouldUrlDecodeHeader(configuration, name, extracted, "UTF-8");
+ LOG.trace("HTTP-header: {}", extracted);
+ if (headerFilterStrategy != null
+ && !headerFilterStrategy.applyFilterToExternalHeaders(name, decoded, exchange)) {
+ NettyHttpHelper.appendHeader(headers, name, decoded);
+ }
+ }
+ }
+
+ // add uri parameters as headers to the Camel message
+ if (request.getUri().contains("?")) {
+ String query = ObjectHelper.after(request.getUri(), "?");
+ Map<String, Object> uriParameters = URISupport.parseQuery(query);
+
+ for (Map.Entry<String, Object> entry : uriParameters.entrySet()) {
+ String name = entry.getKey();
+ Object values = entry.getValue();
+ Iterator<?> it = ObjectHelper.createIterator(values);
+ while (it.hasNext()) {
+ Object extracted = it.next();
+ Object decoded = shouldUrlDecodeHeader(configuration, name, extracted, "UTF-8");
+ LOG.trace("URI-Parameter: {}", extracted);
+ if (headerFilterStrategy != null
+ && !headerFilterStrategy.applyFilterToExternalHeaders(name, decoded, exchange)) {
+ NettyHttpHelper.appendHeader(headers, name, decoded);
+ }
+ }
+ }
+ }
+
+ // if body is application/x-www-form-urlencoded then extract the body as query string and append as headers
+ // if it is a bridgeEndpoint we need to skip this part of work
+ if (request.getMethod().name().equals("POST") && request.headers().get(Exchange.CONTENT_TYPE) != null
+ && request.headers().get(Exchange.CONTENT_TYPE).startsWith(NettyHttpConstants.CONTENT_TYPE_WWW_FORM_URLENCODED)
+ && !configuration.isBridgeEndpoint()) {
+
+ String charset = "UTF-8";
+
+ // Push POST form params into the headers to retain compatibility with DefaultHttpBinding
+ String body = request.content().toString(Charset.forName(charset));
+ if (ObjectHelper.isNotEmpty(body)) {
+ for (String param : body.split("&")) {
+ String[] pair = param.split("=", 2);
+ if (pair.length == 2) {
+ String name = shouldUrlDecodeHeader(configuration, "", pair[0], charset);
+ String value = shouldUrlDecodeHeader(configuration, name, pair[1], charset);
+ if (headerFilterStrategy != null
+ && !headerFilterStrategy.applyFilterToExternalHeaders(name, value, exchange)) {
+ NettyHttpHelper.appendHeader(headers, name, value);
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid parameter, expected to be a pair but was " + param);
+ }
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Decodes the header if needed to, or returns the header value as is.
+ *
+ * @param configuration the configuration
+ * @param headerName the header name
+ * @param value the current header value
+ * @param charset the charset to use for decoding
+ * @return the decoded value (if decoded was needed) or a <tt>toString</tt> representation of the value.
+ * @throws UnsupportedEncodingException is thrown if error decoding.
+ */
+ protected String shouldUrlDecodeHeader(NettyHttpConfiguration configuration, String headerName, Object value, String charset) throws UnsupportedEncodingException {
+ // do not decode Content-Type
+ if (Exchange.CONTENT_TYPE.equals(headerName)) {
+ return value.toString();
+ } else if (configuration.isUrlDecodeHeaders()) {
+ return URLDecoder.decode(value.toString(), charset);
+ } else {
+ return value.toString();
+ }
+ }
+
+ @Override
+ public Message toCamelMessage(FullHttpResponse response, Exchange exchange, NettyHttpConfiguration configuration) throws Exception {
+ LOG.trace("toCamelMessage: {}", response);
+
+ NettyHttpMessage answer = new NettyHttpMessage(null, response);
+ answer.setExchange(exchange);
+ if (configuration.isMapHeaders()) {
+ populateCamelHeaders(response, answer.getHeaders(), exchange, configuration);
+ }
+
+ // keep the body as is, and use type converters
+ answer.setBody(response.content());
+ return answer;
+ }
+
+ @Override
+ public void populateCamelHeaders(FullHttpResponse response, Map<String, Object> headers, Exchange exchange, NettyHttpConfiguration configuration) throws Exception {
+ LOG.trace("populateCamelHeaders: {}", response);
+
+ headers.put(Exchange.HTTP_RESPONSE_CODE, response.getStatus().code());
+ headers.put(NettyHttpConstants.HTTP_RESPONSE_TEXT, response.getStatus().reasonPhrase());
+
+ for (String name : response.headers().names()) {
+ // mapping the content-type
+ if (name.toLowerCase().equals("content-type")) {
+ name = Exchange.CONTENT_TYPE;
+ }
+ // add the headers one by one, and use the header filter strategy
+ List<String> values = response.headers().getAll(name);
+ Iterator<?> it = ObjectHelper.createIterator(values);
+ while (it.hasNext()) {
+ Object extracted = it.next();
+ LOG.trace("HTTP-header: {}", extracted);
+ if (headerFilterStrategy != null
+ && !headerFilterStrategy.applyFilterToExternalHeaders(name, extracted, exchange)) {
+ NettyHttpHelper.appendHeader(headers, name, extracted);
+ }
+ }
+ }
+ }
+
+ @Override
+ public HttpResponse toNettyResponse(Message message, NettyHttpConfiguration configuration) throws Exception {
+ LOG.trace("toNettyResponse: {}", message);
+
+ // the message body may already be a Netty HTTP response
+ if (message.getBody() instanceof HttpResponse) {
+ return (HttpResponse) message.getBody();
+ }
+
+ Object body = message.getBody();
+ Exception cause = message.getExchange().getException();
+ // support bodies as native Netty
+ ByteBuf buffer;
+ // the response code is 200 for OK and 500 for failed
+ boolean failed = message.getExchange().isFailed();
+ int defaultCode = failed ? 500 : 200;
+
+ int code = message.getHeader(Exchange.HTTP_RESPONSE_CODE, defaultCode, int.class);
+
+ LOG.trace("HTTP Status Code: {}", code);
+
+
+ // if there was an exception then use that as body
+ if (cause != null) {
+ if (configuration.isTransferException()) {
+ // we failed due an exception, and transfer it as java serialized object
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+ oos.writeObject(cause);
+ oos.flush();
+ IOHelper.close(oos, bos);
+
+ // the body should be the serialized java object of the exception
+ body = NettyConverter.toByteBuffer(bos.toByteArray());
+ // force content type to be serialized java object
+ message.setHeader(Exchange.CONTENT_TYPE, NettyHttpConstants.CONTENT_TYPE_JAVA_SERIALIZED_OBJECT);
+ } else {
+ // we failed due an exception so print it as plain text
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ cause.printStackTrace(pw);
+
+ // the body should then be the stacktrace
+ body = NettyConverter.toByteBuffer(sw.toString().getBytes());
+ // force content type to be text/plain as that is what the stacktrace is
+ message.setHeader(Exchange.CONTENT_TYPE, "text/plain");
+ }
+
+ // and mark the exception as failure handled, as we handled it by returning it as the response
+ ExchangeHelper.setFailureHandled(message.getExchange());
+ }
+
+ if (body instanceof ByteBuf) {
+ buffer = (ByteBuf) body;
+ } else {
+ // try to convert to buffer first
+ buffer = message.getBody(ByteBuf.class);
+ if (buffer == null) {
+ // fallback to byte array as last resort
+ byte[] data = message.getBody(byte[].class);
+ if (data != null) {
+ buffer = NettyConverter.toByteBuffer(data);
+ } else {
+ // and if byte array fails then try String
+ String str;
+ if (body != null) {
+ str = message.getMandatoryBody(String.class);
+ } else {
+ str = "";
+ }
+ buffer = NettyConverter.toByteBuffer(str.getBytes());
+ }
+ }
+ }
+
+ HttpResponse response = null;
+
+ if (buffer != null) {
+ response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(code), buffer);
+ // We just need to reset the readerIndex this time
+ if (buffer.readerIndex() == buffer.writerIndex()) {
+ buffer.setIndex(0, buffer.writerIndex());
+ }
+ // TODO How to enable the chunk transport
+ int len = buffer.readableBytes();
+ // set content-length
+ response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, len);
+ LOG.trace("Content-Length: {}", len);
+ } else {
+ response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(code));
+ }
+
+ TypeConverter tc = message.getExchange().getContext().getTypeConverter();
+
+ // append headers
+ // must use entrySet to ensure case of keys is preserved
+ for (Map.Entry<String, Object> entry : message.getHeaders().entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ // use an iterator as there can be multiple values. (must not use a delimiter)
+ final Iterator<?> it = ObjectHelper.createIterator(value, null);
+ while (it.hasNext()) {
+ String headerValue = tc.convertTo(String.class, it.next());
+ if (headerValue != null && headerFilterStrategy != null
+ && !headerFilterStrategy.applyFilterToCamelHeaders(key, headerValue, message.getExchange())) {
+ LOG.trace("HTTP-Header: {}={}", key, headerValue);
+ response.headers().add(key, headerValue);
+ }
+ }
+ }
+
+ // set the content type in the response.
+ String contentType = MessageHelper.getContentType(message);
+ if (contentType != null) {
+ // set content-type
+ response.headers().set(HttpHeaders.Names.CONTENT_TYPE, contentType);
+ LOG.trace("Content-Type: {}", contentType);
+ }
+
+ // configure connection to accordingly to keep alive configuration
+ // favor using the header from the message
+ String connection = message.getHeader(HttpHeaders.Names.CONNECTION, String.class);
+ if (connection == null) {
+ // fallback and use the keep alive from the configuration
+ if (configuration.isKeepAlive()) {
+ connection = HttpHeaders.Values.KEEP_ALIVE;
+ } else {
+ connection = HttpHeaders.Values.CLOSE;
+ }
+ }
+ response.headers().set(HttpHeaders.Names.CONNECTION, connection);
+ LOG.trace("Connection: {}", connection);
+
+ return response;
+ }
+
+ @Override
+ public HttpRequest toNettyRequest(Message message, String uri, NettyHttpConfiguration configuration) throws Exception {
+ LOG.trace("toNettyRequest: {}", message);
+
+ // the message body may already be a Netty HTTP response
+ if (message.getBody() instanceof HttpRequest) {
+ return (HttpRequest) message.getBody();
+ }
+
+ // just assume GET for now, we will later change that to the actual method to use
+ HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri);
+
+ Object body = message.getBody();
+ if (body != null) {
+ // support bodies as native Netty
+ ByteBuf buffer;
+ if (body instanceof ByteBuf) {
+ buffer = (ByteBuf) body;
+ } else {
+ // try to convert to buffer first
+ buffer = message.getBody(ByteBuf.class);
+ if (buffer == null) {
+ // fallback to byte array as last resort
+ byte[] data = message.getMandatoryBody(byte[].class);
+ buffer = NettyConverter.toByteBuffer(data);
+ }
+ }
+ if (buffer != null) {
+ request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uri, buffer);
+ int len = buffer.readableBytes();
+ // set content-length
+ request.headers().set(HttpHeaders.Names.CONTENT_LENGTH, len);
+ LOG.trace("Content-Length: {}", len);
+ } else {
+ // we do not support this kind of body
+ throw new NoTypeConversionAvailableException(body, ByteBuf.class);
+ }
+ }
+
+ // update HTTP method accordingly as we know if we have a body or not
+ HttpMethod method = NettyHttpHelper.createMethod(message, body != null);
+ request.setMethod(method);
+
+ TypeConverter tc = message.getExchange().getContext().getTypeConverter();
+
+ // if we bridge endpoint then we need to skip matching headers with the HTTP_QUERY to avoid sending
+ // duplicated headers to the receiver, so use this skipRequestHeaders as the list of headers to skip
+ Map<String, Object> skipRequestHeaders = null;
+ if (configuration.isBridgeEndpoint()) {
+ String queryString = message.getHeader(Exchange.HTTP_QUERY, String.class);
+ if (queryString != null) {
+ skipRequestHeaders = URISupport.parseQuery(queryString);
+ }
+ // Need to remove the Host key as it should be not used
+ message.getHeaders().remove("host");
+ }
+
+ // append headers
+ // must use entrySet to ensure case of keys is preserved
+ for (Map.Entry<String, Object> entry : message.getHeaders().entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+
+ // 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;
+ }
+
+ // use an iterator as there can be multiple values. (must not use a delimiter)
+ final Iterator<?> it = ObjectHelper.createIterator(value, null, true);
+ while (it.hasNext()) {
+ String headerValue = tc.convertTo(String.class, it.next());
+
+ if (headerValue != null && headerFilterStrategy != null
+ && !headerFilterStrategy.applyFilterToCamelHeaders(key, headerValue, message.getExchange())) {
+ LOG.trace("HTTP-Header: {}={}", key, headerValue);
+ request.headers().add(key, headerValue);
+ }
+ }
+ }
+
+ // set the content type in the response.
+ String contentType = MessageHelper.getContentType(message);
+ if (contentType != null) {
+ // set content-type
+ request.headers().set(HttpHeaders.Names.CONTENT_TYPE, contentType);
+ LOG.trace("Content-Type: {}", contentType);
+ }
+
+ // must include HOST header as required by HTTP 1.1
+ // use URI as its faster than URL (no DNS lookup)
+ URI u = new URI(uri);
+ String host = u.getHost();
+ request.headers().set(HttpHeaders.Names.HOST, host);
+ LOG.trace("Host: {}", host);
+
+ // configure connection to accordingly to keep alive configuration
+ // favor using the header from the message
+ String connection = message.getHeader(HttpHeaders.Names.CONNECTION, String.class);
+ if (connection == null) {
+ // fallback and use the keep alive from the configuration
+ if (configuration.isKeepAlive()) {
+ connection = HttpHeaders.Values.KEEP_ALIVE;
+ } else {
+ connection = HttpHeaders.Values.CLOSE;
+ }
+ }
+ request.headers().set(HttpHeaders.Names.CONNECTION, connection);
+ LOG.trace("Connection: {}", connection);
+
+ return request;
+ }
+
+ @Override
+ public HeaderFilterStrategy getHeaderFilterStrategy() {
+ return headerFilterStrategy;
+ }
+
+ @Override
+ public void setHeaderFilterStrategy(HeaderFilterStrategy headerFilterStrategy) {
+ this.headerFilterStrategy = headerFilterStrategy;
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/db88eeda/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/DefaultNettySharedHttpServer.java
----------------------------------------------------------------------
diff --git a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/DefaultNettySharedHttpServer.java b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/DefaultNettySharedHttpServer.java
new file mode 100644
index 0000000..3ad1fb1
--- /dev/null
+++ b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/DefaultNettySharedHttpServer.java
@@ -0,0 +1,132 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.netty4.http;
+
+import java.util.concurrent.ThreadFactory;
+import java.util.regex.Matcher;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelInitializer;
+import org.apache.camel.component.netty4.NettyServerBootstrapFactory;
+import org.apache.camel.component.netty4.http.handlers.HttpServerMultiplexChannelHandler;
+import org.apache.camel.spi.ClassResolver;
+import org.apache.camel.support.ServiceSupport;
+import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.ServiceHelper;
+import org.apache.camel.util.concurrent.CamelThreadFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A default {@link NettySharedHttpServer} to make sharing Netty server in Camel applications easier.
+ */
+public class DefaultNettySharedHttpServer extends ServiceSupport implements NettySharedHttpServer {
+
+ // TODO: option to enlist in JMX
+
+ public static final String DEFAULT_PATTERN = "Camel Thread ##counter# - #name#:#port#";
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultNettySharedHttpServer.class);
+
+ private NettySharedHttpServerBootstrapConfiguration configuration;
+ private HttpServerConsumerChannelFactory channelFactory;
+ private HttpServerBootstrapFactory bootstrapFactory;
+ private ClassResolver classResolver;
+ private boolean startServer = true;
+ private String threadPattern = DEFAULT_PATTERN;
+
+ public void setNettyServerBootstrapConfiguration(NettySharedHttpServerBootstrapConfiguration configuration) {
+ this.configuration = configuration;
+ }
+
+ public void setClassResolver(ClassResolver classResolver) {
+ this.classResolver = classResolver;
+ }
+
+ public int getPort() {
+ return configuration != null ? configuration.getPort() : -1;
+ }
+
+ public HttpServerConsumerChannelFactory getConsumerChannelFactory() {
+ return channelFactory;
+ }
+
+ public NettyServerBootstrapFactory getServerBootstrapFactory() {
+ return bootstrapFactory;
+ }
+
+ public int getConsumersSize() {
+ if (channelFactory != null) {
+ return channelFactory.consumers();
+ } else {
+ return -1;
+ }
+ }
+
+ public void setStartServer(boolean startServer) {
+ this.startServer = startServer;
+ }
+
+ public void setThreadNamePattern(String pattern) {
+ this.threadPattern = pattern;
+ }
+
+ protected void doStart() throws Exception {
+ ObjectHelper.notNull(configuration, "setNettyServerBootstrapConfiguration() must be called with a NettyServerBootstrapConfiguration instance", this);
+
+ // port must be set
+ if (configuration.getPort() <= 0) {
+ throw new IllegalArgumentException("Port must be configured on NettySharedHttpServerBootstrapConfiguration " + configuration);
+ }
+ // hostname must be set
+ if (ObjectHelper.isEmpty(configuration.getHost())) {
+ throw new IllegalArgumentException("Host must be configured on NettySharedHttpServerBootstrapConfiguration " + configuration);
+ }
+
+ LOG.debug("NettySharedHttpServer using configuration: {}", configuration);
+
+ // force using tcp as the underlying transport
+ configuration.setProtocol("tcp");
+
+ channelFactory = new HttpServerMultiplexChannelHandler();
+ channelFactory.init(configuration.getPort());
+
+ ChannelInitializer<Channel> pipelineFactory = new HttpServerSharedPipelineFactory(configuration, channelFactory, classResolver);
+
+ // thread factory and pattern
+ String port = Matcher.quoteReplacement("" + configuration.getPort());
+ String pattern = threadPattern;
+ pattern = pattern.replaceFirst("#port#", port);
+ ThreadFactory tf = new CamelThreadFactory(pattern, "NettySharedHttpServer", true);
+
+ // create bootstrap factory and disable compatible check as its shared among the consumers
+ bootstrapFactory = new HttpServerBootstrapFactory(channelFactory, false);
+ bootstrapFactory.init(tf, configuration, pipelineFactory);
+
+ ServiceHelper.startServices(channelFactory);
+
+ if (startServer) {
+ LOG.info("Starting NettySharedHttpServer on {}:{}", configuration.getHost(), configuration.getPort());
+ ServiceHelper.startServices(bootstrapFactory);
+ }
+ }
+
+ @Override
+ protected void doStop() throws Exception {
+ LOG.info("Stopping NettySharedHttpServer on {}:{}", configuration.getHost(), configuration.getPort());
+ ServiceHelper.stopServices(bootstrapFactory, channelFactory);
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/db88eeda/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpClientPipelineFactory.java
----------------------------------------------------------------------
diff --git a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpClientPipelineFactory.java b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpClientPipelineFactory.java
new file mode 100644
index 0000000..cb6610c
--- /dev/null
+++ b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpClientPipelineFactory.java
@@ -0,0 +1,165 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.netty4.http;
+
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.codec.http.HttpClientCodec;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.handler.timeout.ReadTimeoutHandler;
+import org.apache.camel.component.netty4.ClientPipelineFactory;
+import org.apache.camel.component.netty4.NettyConfiguration;
+import org.apache.camel.component.netty4.NettyProducer;
+import org.apache.camel.component.netty4.http.handlers.HttpClientChannelHandler;
+import org.apache.camel.component.netty4.ssl.SSLEngineFactory;
+import org.apache.camel.util.ObjectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link org.apache.camel.component.netty.ClientPipelineFactory} for the Netty HTTP client.
+ */
+public class HttpClientPipelineFactory extends ClientPipelineFactory {
+
+ private static final Logger LOG = LoggerFactory.getLogger(HttpClientPipelineFactory.class);
+ protected NettyHttpConfiguration configuration;
+ private NettyHttpProducer producer;
+ private SSLContext sslContext;
+
+ public HttpClientPipelineFactory() {
+ // default constructor needed
+ }
+
+ public HttpClientPipelineFactory(NettyHttpProducer nettyProducer) {
+ this.producer = nettyProducer;
+ try {
+ this.sslContext = createSSLContext(producer);
+ } catch (Exception e) {
+ throw ObjectHelper.wrapRuntimeCamelException(e);
+ }
+
+ if (sslContext != null) {
+ LOG.info("Created SslContext {}", sslContext);
+ }
+ configuration = nettyProducer.getConfiguration();
+ }
+
+ @Override
+ public ClientPipelineFactory createPipelineFactory(NettyProducer nettyProducer) {
+ return new HttpClientPipelineFactory((NettyHttpProducer) nettyProducer);
+ }
+
+ @Override
+ protected void initChannel(Channel ch) throws Exception {
+ // create a new pipeline
+ ChannelPipeline pipeline = ch.pipeline();
+
+ SslHandler sslHandler = configureClientSSLOnDemand();
+ if (sslHandler != null) {
+ //TODO must close on SSL exception
+ //sslHandler.setCloseOnSSLException(true);
+ LOG.debug("Client SSL handler configured and added as an interceptor against the ChannelPipeline: {}", sslHandler);
+ pipeline.addLast("ssl", sslHandler);
+ }
+
+ pipeline.addLast("http", new HttpClientCodec());
+ pipeline.addLast("aggregator", new HttpObjectAggregator(configuration.getChunkedMaxContentLength()));
+
+ if (producer.getConfiguration().getRequestTimeout() > 0) {
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("Using request timeout {} millis", producer.getConfiguration().getRequestTimeout());
+ }
+ ChannelHandler timeout = new ReadTimeoutHandler(producer.getConfiguration().getRequestTimeout(), TimeUnit.MILLISECONDS);
+ pipeline.addLast("timeout", timeout);
+ }
+
+ // handler to route Camel messages
+ pipeline.addLast("handler", new HttpClientChannelHandler(producer));
+
+
+ }
+
+ private SSLContext createSSLContext(NettyProducer producer) throws Exception {
+ NettyConfiguration configuration = producer.getConfiguration();
+
+ if (!configuration.isSsl()) {
+ return null;
+ }
+
+ SSLContext answer;
+
+ // create ssl context once
+ if (configuration.getSslContextParameters() != null) {
+ answer = configuration.getSslContextParameters().createSSLContext();
+ } else {
+ if (configuration.getKeyStoreFile() == null && configuration.getKeyStoreResource() == null) {
+ LOG.debug("keystorefile is null");
+ }
+ if (configuration.getTrustStoreFile() == null && configuration.getTrustStoreResource() == null) {
+ LOG.debug("truststorefile is null");
+ }
+ if (configuration.getPassphrase().toCharArray() == null) {
+ LOG.debug("passphrase is null");
+ }
+
+ SSLEngineFactory sslEngineFactory;
+ if (configuration.getKeyStoreFile() != null || configuration.getTrustStoreFile() != null) {
+ sslEngineFactory = new SSLEngineFactory();
+ answer = sslEngineFactory.createSSLContext(producer.getContext().getClassResolver(),
+ configuration.getKeyStoreFormat(),
+ configuration.getSecurityProvider(),
+ "file:" + configuration.getKeyStoreFile().getPath(),
+ "file:" + configuration.getTrustStoreFile().getPath(),
+ configuration.getPassphrase().toCharArray());
+ } else {
+ sslEngineFactory = new SSLEngineFactory();
+ answer = sslEngineFactory.createSSLContext(producer.getContext().getClassResolver(),
+ configuration.getKeyStoreFormat(),
+ configuration.getSecurityProvider(),
+ configuration.getKeyStoreResource(),
+ configuration.getTrustStoreResource(),
+ configuration.getPassphrase().toCharArray());
+ }
+ }
+
+ return answer;
+ }
+
+ private SslHandler configureClientSSLOnDemand() throws Exception {
+ if (!producer.getConfiguration().isSsl()) {
+ return null;
+ }
+
+ if (producer.getConfiguration().getSslHandler() != null) {
+ return producer.getConfiguration().getSslHandler();
+ } else if (sslContext != null) {
+ SSLEngine engine = sslContext.createSSLEngine();
+ engine.setUseClientMode(true);
+ return new SslHandler(engine);
+ }
+
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/db88eeda/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpPrincipal.java
----------------------------------------------------------------------
diff --git a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpPrincipal.java b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpPrincipal.java
new file mode 100644
index 0000000..5e10ed9
--- /dev/null
+++ b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpPrincipal.java
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.netty4.http;
+
+import java.security.Principal;
+
+/**
+ * Http {@link Principal}.
+ */
+public final class HttpPrincipal implements Principal {
+
+ private final String username;
+ private final String password;
+
+ public HttpPrincipal(String username, String password) {
+ this.username = username;
+ this.password = password;
+ }
+
+ @Override
+ public String getName() {
+ return username;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public String toString() {
+ // do not display the password
+ return "HttpPrincipal[" + username + "]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/db88eeda/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerBootstrapFactory.java
----------------------------------------------------------------------
diff --git a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerBootstrapFactory.java b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerBootstrapFactory.java
new file mode 100644
index 0000000..be4c9e1
--- /dev/null
+++ b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerBootstrapFactory.java
@@ -0,0 +1,104 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.netty4.http;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelInitializer;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.netty4.NettyConsumer;
+import org.apache.camel.component.netty4.NettyServerBootstrapConfiguration;
+import org.apache.camel.component.netty4.SingleTCPNettyServerBootstrapFactory;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HttpServerBootstrapFactory extends SingleTCPNettyServerBootstrapFactory {
+
+ private static final Logger LOG = LoggerFactory.getLogger(HttpServerBootstrapFactory.class);
+ private final HttpServerConsumerChannelFactory channelFactory;
+ private int port;
+ private NettyServerBootstrapConfiguration bootstrapConfiguration;
+ private boolean compatibleCheck;
+
+ public HttpServerBootstrapFactory(HttpServerConsumerChannelFactory channelFactory) {
+ this(channelFactory, true);
+ }
+
+ public HttpServerBootstrapFactory(HttpServerConsumerChannelFactory channelFactory, boolean compatibleCheck) {
+ this.channelFactory = channelFactory;
+ this.compatibleCheck = compatibleCheck;
+ }
+
+ @Override
+ public void init(CamelContext camelContext, NettyServerBootstrapConfiguration configuration, ChannelInitializer<Channel> pipelineFactory) {
+ super.init(camelContext, configuration, pipelineFactory);
+ this.port = configuration.getPort();
+ this.bootstrapConfiguration = configuration;
+
+ LOG.info("BootstrapFactory on port {} is using bootstrap configuration: [{}]", port, bootstrapConfiguration.toStringBootstrapConfiguration());
+ }
+
+ public void addConsumer(NettyConsumer consumer) {
+ if (compatibleCheck) {
+ // when adding additional consumers on the same port (eg to reuse port for multiple routes etc) then the Netty server bootstrap
+ // configuration must match, as its the 1st consumer that calls the init method, which configuration is used for the Netty server bootstrap
+ // we do this to avoid mis configuration, so people configure SSL and plain configuration on the same port etc.
+
+ // first it may be the same instance, so only check for compatibility of different instance
+ if (bootstrapConfiguration != consumer.getConfiguration() && !bootstrapConfiguration.compatible(consumer.getConfiguration())) {
+ throw new IllegalArgumentException("Bootstrap configuration must be identical when adding additional consumer: " + consumer.getEndpoint() + " on same port: " + port
+ + ".\n Existing " + bootstrapConfiguration.toStringBootstrapConfiguration() + "\n New " + consumer.getConfiguration().toStringBootstrapConfiguration());
+ }
+ }
+
+ if (LOG.isDebugEnabled()) {
+ NettyHttpConsumer httpConsumer = (NettyHttpConsumer) consumer;
+ LOG.debug("BootstrapFactory on port {} is adding consumer with context-path {}", port, httpConsumer.getConfiguration().getPath());
+ }
+
+ channelFactory.addConsumer((NettyHttpConsumer) consumer);
+ }
+
+ @Override
+ public void removeConsumer(NettyConsumer consumer) {
+ if (LOG.isDebugEnabled()) {
+ NettyHttpConsumer httpConsumer = (NettyHttpConsumer) consumer;
+ LOG.debug("BootstrapFactory on port {} is removing consumer with context-path {}", port, httpConsumer.getConfiguration().getPath());
+ }
+ channelFactory.removeConsumer((NettyHttpConsumer) consumer);
+ }
+
+ @Override
+ protected void doStart() throws Exception {
+ LOG.debug("BootstrapFactory on port {} is starting", port);
+ super.doStart();
+ }
+
+ @Override
+ public void stop() throws Exception {
+ // only stop if no more active consumers
+ int consumers = channelFactory.consumers();
+ if (consumers == 0) {
+ LOG.debug("BootstrapFactory on port {} is stopping", port);
+ super.stop();
+ } else {
+ LOG.debug("BootstrapFactory on port {} has {} registered consumers, so cannot stop yet.", port, consumers);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/db88eeda/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerConsumerChannelFactory.java
----------------------------------------------------------------------
diff --git a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerConsumerChannelFactory.java b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerConsumerChannelFactory.java
new file mode 100644
index 0000000..e1d51f6
--- /dev/null
+++ b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerConsumerChannelFactory.java
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.netty4.http;
+
+import io.netty.channel.ChannelHandler;
+
+/**
+ * Factory for setting up Netty {@link ChannelHandler} bound to a given Netty port.
+ * <p/>
+ * This factory allows for consumers to reuse existing {@link io.netty.bootstrap.ServerBootstrap} which
+ * allows to share the same port for multiple consumers.
+ *
+ * This factory is needed to ensure we can handle the situations when consumers is added and removing in
+ * a dynamic environment such as OSGi, where Camel applications can be hot-deployed. And we want these
+ * Camel applications to be able to share the same Netty port in a easy way.
+ */
+public interface HttpServerConsumerChannelFactory {
+
+ /**
+ * Initializes this consumer channel factory with the given port.
+ */
+ void init(int port);
+
+ /**
+ * The port number this consumer channel factory is using.
+ */
+ int getPort();
+
+ /**
+ * Adds the given consumer.
+ */
+ void addConsumer(NettyHttpConsumer consumer);
+
+ /**
+ * Removes the given consumer
+ */
+ void removeConsumer(NettyHttpConsumer consumer);
+
+ /**
+ * Number of active consumers
+ */
+ int consumers();
+
+ /**
+ * Gets the {@link ChannelHandler}
+ */
+ ChannelHandler getChannelHandler();
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/db88eeda/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerPipelineFactory.java
----------------------------------------------------------------------
diff --git a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerPipelineFactory.java b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerPipelineFactory.java
new file mode 100644
index 0000000..51ae265
--- /dev/null
+++ b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerPipelineFactory.java
@@ -0,0 +1,172 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.netty4.http;
+
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.codec.http.HttpContentCompressor;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpRequestDecoder;
+import io.netty.handler.codec.http.HttpResponseEncoder;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.util.concurrent.EventExecutorGroup;
+import org.apache.camel.CamelContext;
+import org.apache.camel.component.netty4.NettyConsumer;
+import org.apache.camel.component.netty4.NettyServerBootstrapConfiguration;
+import org.apache.camel.component.netty4.ServerPipelineFactory;
+import org.apache.camel.component.netty4.ssl.SSLEngineFactory;
+import org.apache.camel.util.ObjectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@link ServerPipelineFactory} for the Netty HTTP server.
+ */
+public class HttpServerPipelineFactory extends ServerPipelineFactory {
+
+ private static final Logger LOG = LoggerFactory.getLogger(HttpServerPipelineFactory.class);
+ protected NettyHttpConsumer consumer;
+ protected SSLContext sslContext;
+ protected NettyHttpConfiguration configuration;
+
+ public HttpServerPipelineFactory() {
+ // default constructor needed
+ }
+
+ public HttpServerPipelineFactory(NettyHttpConsumer nettyConsumer) {
+ this.consumer = nettyConsumer;
+ this.configuration = nettyConsumer.getConfiguration();
+ try {
+ this.sslContext = createSSLContext(consumer.getContext(), consumer.getConfiguration());
+ } catch (Exception e) {
+ throw ObjectHelper.wrapRuntimeCamelException(e);
+ }
+
+ if (sslContext != null) {
+ LOG.info("Created SslContext {}", sslContext);
+ }
+ }
+
+ @Override
+ public ServerPipelineFactory createPipelineFactory(NettyConsumer nettyConsumer) {
+ return new HttpServerPipelineFactory((NettyHttpConsumer) nettyConsumer);
+ }
+
+ @Override
+ protected void initChannel(Channel ch) throws Exception {
+ // create a new pipeline
+ ChannelPipeline pipeline = ch.pipeline();
+
+ SslHandler sslHandler = configureServerSSLOnDemand();
+ if (sslHandler != null) {
+ //TODO must close on SSL exception
+ // sslHandler.setCloseOnSSLException(true);
+ LOG.debug("Server SSL handler configured and added as an interceptor against the ChannelPipeline: {}", sslHandler);
+ pipeline.addLast("ssl", sslHandler);
+ }
+
+ pipeline.addLast("decoder", new HttpRequestDecoder());
+ pipeline.addLast("aggregator", new HttpObjectAggregator(configuration.getChunkedMaxContentLength()));
+
+ pipeline.addLast("encoder", new HttpResponseEncoder());
+ if (supportCompressed()) {
+ pipeline.addLast("deflater", new HttpContentCompressor());
+ }
+
+ int port = consumer.getConfiguration().getPort();
+ ChannelHandler handler = consumer.getEndpoint().getComponent().getMultiplexChannelHandler(port).getChannelHandler();
+
+ if (consumer.getConfiguration().isOrderedThreadPoolExecutor()) {
+ EventExecutorGroup applicationExecutor = consumer.getEndpoint().getComponent().getExecutorService();
+ pipeline.addLast(applicationExecutor, "handler", handler);
+ } else {
+ pipeline.addLast("handler", handler);
+ }
+
+ }
+
+ private SSLContext createSSLContext(CamelContext camelContext, NettyServerBootstrapConfiguration configuration) throws Exception {
+ if (!configuration.isSsl()) {
+ return null;
+ }
+
+ SSLContext answer;
+
+ // create ssl context once
+ if (configuration.getSslContextParameters() != null) {
+ answer = configuration.getSslContextParameters().createSSLContext();
+ } else {
+ if (configuration.getKeyStoreFile() == null && configuration.getKeyStoreResource() == null) {
+ LOG.debug("keystorefile is null");
+ }
+ if (configuration.getTrustStoreFile() == null && configuration.getTrustStoreResource() == null) {
+ LOG.debug("truststorefile is null");
+ }
+ if (configuration.getPassphrase().toCharArray() == null) {
+ LOG.debug("passphrase is null");
+ }
+
+ SSLEngineFactory sslEngineFactory;
+ if (configuration.getKeyStoreFile() != null || configuration.getTrustStoreFile() != null) {
+ sslEngineFactory = new SSLEngineFactory();
+ answer = sslEngineFactory.createSSLContext(camelContext.getClassResolver(),
+ configuration.getKeyStoreFormat(),
+ configuration.getSecurityProvider(),
+ "file:" + configuration.getKeyStoreFile().getPath(),
+ "file:" + configuration.getTrustStoreFile().getPath(),
+ configuration.getPassphrase().toCharArray());
+ } else {
+ sslEngineFactory = new SSLEngineFactory();
+ answer = sslEngineFactory.createSSLContext(camelContext.getClassResolver(),
+ configuration.getKeyStoreFormat(),
+ configuration.getSecurityProvider(),
+ configuration.getKeyStoreResource(),
+ configuration.getTrustStoreResource(),
+ configuration.getPassphrase().toCharArray());
+ }
+ }
+
+ return answer;
+ }
+
+ private SslHandler configureServerSSLOnDemand() throws Exception {
+ if (!consumer.getConfiguration().isSsl()) {
+ return null;
+ }
+
+ if (consumer.getConfiguration().getSslHandler() != null) {
+ return consumer.getConfiguration().getSslHandler();
+ } else if (sslContext != null) {
+ SSLEngine engine = sslContext.createSSLEngine();
+ engine.setUseClientMode(false);
+ engine.setNeedClientAuth(consumer.getConfiguration().isNeedClientAuth());
+ return new SslHandler(engine);
+ }
+
+ return null;
+ }
+
+ private boolean supportCompressed() {
+ return consumer.getEndpoint().getConfiguration().isCompression();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/db88eeda/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerSharedPipelineFactory.java
----------------------------------------------------------------------
diff --git a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerSharedPipelineFactory.java b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerSharedPipelineFactory.java
new file mode 100644
index 0000000..3f47ff9
--- /dev/null
+++ b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/HttpServerSharedPipelineFactory.java
@@ -0,0 +1,159 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.netty4.http;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.codec.http.HttpContentCompressor;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpRequestDecoder;
+import io.netty.handler.codec.http.HttpResponseEncoder;
+import io.netty.handler.ssl.SslHandler;
+import org.apache.camel.component.netty4.NettyConsumer;
+import org.apache.camel.component.netty4.ServerPipelineFactory;
+import org.apache.camel.component.netty4.ssl.SSLEngineFactory;
+import org.apache.camel.impl.DefaultClassResolver;
+import org.apache.camel.spi.ClassResolver;
+import org.apache.camel.util.ObjectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A shared {@link org.apache.camel.component.netty.ServerPipelineFactory} for a shared Netty HTTP server.
+ *
+ * @see NettySharedHttpServer
+ */
+public class HttpServerSharedPipelineFactory extends HttpServerPipelineFactory {
+
+ private static final Logger LOG = LoggerFactory.getLogger(HttpServerSharedPipelineFactory.class);
+ private final NettySharedHttpServerBootstrapConfiguration configuration;
+ private final HttpServerConsumerChannelFactory channelFactory;
+ private final ClassResolver classResolver;
+ private SSLContext sslContext;
+
+ public HttpServerSharedPipelineFactory(NettySharedHttpServerBootstrapConfiguration configuration, HttpServerConsumerChannelFactory channelFactory,
+ ClassResolver classResolver) {
+ this.configuration = configuration;
+ this.channelFactory = channelFactory;
+ // fallback and use default resolver
+ this.classResolver = classResolver != null ? classResolver : new DefaultClassResolver();
+
+ try {
+ this.sslContext = createSSLContext();
+ } catch (Exception e) {
+ throw ObjectHelper.wrapRuntimeCamelException(e);
+ }
+
+ if (sslContext != null) {
+ LOG.info("Created SslContext {}", sslContext);
+ }
+ }
+
+ @Override
+ public ServerPipelineFactory createPipelineFactory(NettyConsumer nettyConsumer) {
+ throw new UnsupportedOperationException("Should not call this operation");
+ }
+
+ @Override
+ protected void initChannel(Channel ch) throws Exception {
+ // create a new pipeline
+ ChannelPipeline pipeline = ch.pipeline();
+
+ SslHandler sslHandler = configureServerSSLOnDemand();
+ if (sslHandler != null) {
+ LOG.debug("Server SSL handler configured and added as an interceptor against the ChannelPipeline: {}", sslHandler);
+ pipeline.addLast("ssl", sslHandler);
+ }
+
+ pipeline.addLast("decoder", new HttpRequestDecoder());
+ if (configuration.isChunked()) {
+ pipeline.addLast("aggregator", new HttpObjectAggregator(configuration.getChunkedMaxContentLength()));
+ }
+ pipeline.addLast("encoder", new HttpResponseEncoder());
+ if (configuration.isCompression()) {
+ pipeline.addLast("deflater", new HttpContentCompressor());
+ }
+
+ pipeline.addLast("handler", channelFactory.getChannelHandler());
+
+ }
+
+ private SSLContext createSSLContext() throws Exception {
+ if (!configuration.isSsl()) {
+ return null;
+ }
+
+ SSLContext answer;
+
+ // create ssl context once
+ if (configuration.getSslContextParameters() != null) {
+ answer = configuration.getSslContextParameters().createSSLContext();
+ } else {
+ if (configuration.getKeyStoreFile() == null && configuration.getKeyStoreResource() == null) {
+ LOG.debug("keystorefile is null");
+ }
+ if (configuration.getTrustStoreFile() == null && configuration.getTrustStoreResource() == null) {
+ LOG.debug("truststorefile is null");
+ }
+ if (configuration.getPassphrase().toCharArray() == null) {
+ LOG.debug("passphrase is null");
+ }
+
+ SSLEngineFactory sslEngineFactory;
+ if (configuration.getKeyStoreFile() != null || configuration.getTrustStoreFile() != null) {
+ sslEngineFactory = new SSLEngineFactory();
+ answer = sslEngineFactory.createSSLContext(classResolver,
+ configuration.getKeyStoreFormat(),
+ configuration.getSecurityProvider(),
+ "file:" + configuration.getKeyStoreFile().getPath(),
+ "file:" + configuration.getTrustStoreFile().getPath(),
+ configuration.getPassphrase().toCharArray());
+ } else {
+ sslEngineFactory = new SSLEngineFactory();
+ answer = sslEngineFactory.createSSLContext(classResolver,
+ configuration.getKeyStoreFormat(),
+ configuration.getSecurityProvider(),
+ configuration.getKeyStoreResource(),
+ configuration.getTrustStoreResource(),
+ configuration.getPassphrase().toCharArray());
+ }
+ }
+
+ return answer;
+ }
+
+ private SslHandler configureServerSSLOnDemand() throws Exception {
+ if (!configuration.isSsl()) {
+ return null;
+ }
+
+ if (configuration.getSslHandler() != null) {
+ return configuration.getSslHandler();
+ } else if (sslContext != null) {
+ SSLEngine engine = sslContext.createSSLEngine();
+ engine.setUseClientMode(false);
+ engine.setNeedClientAuth(configuration.isNeedClientAuth());
+ return new SslHandler(engine);
+ }
+
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/camel/blob/db88eeda/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/JAASSecurityAuthenticator.java
----------------------------------------------------------------------
diff --git a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/JAASSecurityAuthenticator.java b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/JAASSecurityAuthenticator.java
new file mode 100644
index 0000000..4f21def
--- /dev/null
+++ b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/JAASSecurityAuthenticator.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.component.netty4.http;
+
+import java.security.Principal;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.apache.camel.util.ObjectHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A JAAS based {@link SecurityAuthenticator} implementation.
+ */
+public class JAASSecurityAuthenticator extends SecurityAuthenticatorSupport {
+
+ private static final Logger LOG = LoggerFactory.getLogger(JAASSecurityAuthenticator.class);
+
+ @Override
+ public Subject login(HttpPrincipal principal) throws LoginException {
+ if (ObjectHelper.isEmpty(getName())) {
+ throw new IllegalArgumentException("Realm has not been configured on this SecurityAuthenticator: " + this);
+ }
+
+ LOG.trace("Login username: {} using realm: {}", principal.getName(), getName());
+ LoginContext context = new LoginContext(getName(), new HttpPrincipalCallbackHandler(principal));
+ context.login();
+ Subject subject = context.getSubject();
+ LOG.debug("Login username: {} successful returning Subject: {}", principal.getName(), subject);
+
+ if (LOG.isTraceEnabled()) {
+ for (Principal p : subject.getPrincipals()) {
+ LOG.trace("Principal on subject {} -> {}", p.getClass().getName(), p.getName());
+ }
+ }
+
+ return subject;
+ }
+
+ @Override
+ public void logout(Subject subject) throws LoginException {
+ if (ObjectHelper.isEmpty(getName())) {
+ throw new LoginException("Realm has not been configured on this SecurityAuthenticator: " + this);
+ }
+
+ String username = "";
+ if (!subject.getPrincipals().isEmpty()) {
+ username = subject.getPrincipals().iterator().next().getName();
+ }
+ LOG.trace("Logging out username: {} using realm: {}", username, getName());
+ LoginContext context = new LoginContext(getName(), subject);
+ context.logout();
+ LOG.debug("Logout username: {} successful", username);
+ }
+
+}