http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java ---------------------------------------------------------------------- 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 e855f92..8935278 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 @@ -23,11 +23,14 @@ import java.io.InputStream; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import org.apache.camel.CamelExchangeException; import org.apache.camel.Exchange; @@ -51,6 +54,7 @@ import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpVersion; +import org.apache.commons.httpclient.cookie.CookiePolicy; import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; import org.apache.commons.httpclient.methods.EntityEnclosingMethod; import org.apache.commons.httpclient.methods.FileRequestEntity; @@ -140,6 +144,21 @@ public class HttpProducer extends DefaultProducer { } } + if (getEndpoint().getCookieHandler() != null) { + // disable the per endpoint cookie handling + method.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES); + Map<String, List<String>> cookieHeaders = getEndpoint().getCookieHandler().loadCookies(exchange, new URI(method.getURI().getEscapedURI())); + for (Map.Entry<String, List<String>> entry : cookieHeaders.entrySet()) { + String key = entry.getKey(); + if (entry.getValue().size() > 0) { + // 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 = entry.getValue().size() > 1 ? entry.getValue().toString() : entry.getValue().get(0); + method.addRequestHeader(key, s); + } + } + } + if (getEndpoint().isConnectionClose()) { method.addRequestHeader("Connection", "close"); } @@ -175,7 +194,7 @@ public class HttpProducer extends DefaultProducer { return (HttpEndpoint) super.getEndpoint(); } - protected void populateResponse(Exchange exchange, HttpMethod method, Message in, HeaderFilterStrategy strategy, int responseCode) throws IOException, ClassNotFoundException { + protected void populateResponse(Exchange exchange, HttpMethod method, Message in, HeaderFilterStrategy strategy, int responseCode) throws IOException, ClassNotFoundException, URISyntaxException { //We just make the out message is not create when extractResponseBody throws exception, Object response = extractResponseBody(method, exchange, getEndpoint().isIgnoreResponseBody()); Message answer = exchange.getOut(); @@ -186,9 +205,11 @@ public class HttpProducer extends DefaultProducer { // propagate HTTP response headers Header[] headers = method.getResponseHeaders(); + Map<String, List<String>> m = new HashMap<String, List<String>>(); for (Header header : headers) { String name = header.getName(); String value = header.getValue(); + m.put(name, Collections.singletonList(value)); if (name.toLowerCase().equals("content-type")) { name = Exchange.CONTENT_TYPE; exchange.setProperty(Exchange.CHARSET_NAME, IOHelper.getCharsetNameFromContentType(value)); @@ -200,7 +221,10 @@ public class HttpProducer extends DefaultProducer { HttpHelper.appendHeader(answer.getHeaders(), name, extracted); } } - + // handle cookies + if (getEndpoint().getCookieHandler() != null) { + getEndpoint().getCookieHandler().storeCookies(exchange, new URI(method.getURI().getEscapedURI()), m); + } // endpoint might be configured to copy headers from in to out // to avoid overriding existing headers with old values just // filter the http protocol headers @@ -209,12 +233,20 @@ public class HttpProducer extends DefaultProducer { } } - protected Exception populateHttpOperationFailedException(Exchange exchange, HttpMethod method, int responseCode) throws IOException, ClassNotFoundException { + protected Exception populateHttpOperationFailedException(Exchange exchange, HttpMethod method, int responseCode) throws IOException, ClassNotFoundException, URISyntaxException { Exception answer; String uri = method.getURI().toString(); String statusText = method.getStatusLine() != null ? method.getStatusLine().getReasonPhrase() : null; Map<String, String> headers = extractResponseHeaders(method.getResponseHeaders()); + // handle cookies + if (getEndpoint().getCookieHandler() != null) { + Map<String, List<String>> m = new HashMap<String, List<String>>(); + for (Entry<String, String> e : headers.entrySet()) { + m.put(e.getKey(), Collections.singletonList(e.getValue())); + } + getEndpoint().getCookieHandler().storeCookies(exchange, new URI(method.getURI().getEscapedURI()), m); + } Object responseBody = extractResponseBody(method, exchange, getEndpoint().isIgnoreResponseBody()); if (transferException && responseBody != null && responseBody instanceof Exception) {
http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-http/src/test/java/org/apache/camel/component/http/HttpProducerSessionTest.java ---------------------------------------------------------------------- diff --git a/components/camel-http/src/test/java/org/apache/camel/component/http/HttpProducerSessionTest.java b/components/camel-http/src/test/java/org/apache/camel/component/http/HttpProducerSessionTest.java new file mode 100644 index 0000000..d33e792 --- /dev/null +++ b/components/camel-http/src/test/java/org/apache/camel/component/http/HttpProducerSessionTest.java @@ -0,0 +1,117 @@ +/** + * 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.http; + +import java.net.InetSocketAddress; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.http.handler.SessionReflectionHandler; +import org.apache.camel.http.common.cookie.ExchangeCookieHandler; +import org.apache.camel.http.common.cookie.InstanceCookieHandler; +import org.apache.camel.impl.JndiRegistry; +import org.apache.camel.test.AvailablePortFinder; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.session.SessionHandler; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class HttpProducerSessionTest extends CamelTestSupport { + private static volatile int port; + private static Server localServer; + + @BeforeClass + public static void initServer() throws Exception { + port = AvailablePortFinder.getNextAvailable(24000); + localServer = new Server(new InetSocketAddress("127.0.0.1", port)); + ContextHandler contextHandler = new ContextHandler(); + contextHandler.setContextPath("/session"); + SessionHandler sessionHandler = new SessionHandler(); + sessionHandler.setHandler(new SessionReflectionHandler()); + contextHandler.setHandler(sessionHandler); + localServer.setHandler(contextHandler); + localServer.start(); + } + + @AfterClass + public static void shutdownServer() throws Exception { + localServer.stop(); + } + + @Test + public void testNoSession() throws Exception { + // the camel-http component will handle cookies at endpoint level + getMockEndpoint("mock:result").expectedBodiesReceived("New New World", "Old Old World"); + template.sendBody("direct:start", "World"); + template.sendBody("direct:start", "World"); + assertMockEndpointsSatisfied(); + } + + @Test + public void testInstanceSession() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("Old New World", "Old Old World"); + template.sendBody("direct:instance", "World"); + template.sendBody("direct:instance", "World"); + assertMockEndpointsSatisfied(); + } + + @Test + public void testExchangeSession() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("Old New World", "Old New World"); + template.sendBody("direct:exchange", "World"); + template.sendBody("direct:exchange", "World"); + assertMockEndpointsSatisfied(); + } + + @Override + protected JndiRegistry createRegistry() throws Exception { + JndiRegistry jndiRegistry = super.createRegistry(); + jndiRegistry.bind("instanceCookieHandler", new InstanceCookieHandler()); + jndiRegistry.bind("exchangeCookieHandler", new ExchangeCookieHandler()); + return jndiRegistry; + } + + private String getTestServerEndpointSessionUrl() { + // session handling will not work for localhost + return "http://127.0.0.1:" + port + "/session/"; + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start") + .to(getTestServerEndpointSessionUrl()) + .to(getTestServerEndpointSessionUrl()) + .to("mock:result"); + + from("direct:instance") + .to(getTestServerEndpointSessionUrl() + "?cookieHandler=#instanceCookieHandler") + .to(getTestServerEndpointSessionUrl() + "?cookieHandler=#instanceCookieHandler") + .to("mock:result"); + + from("direct:exchange") + .to(getTestServerEndpointSessionUrl() + "?cookieHandler=#exchangeCookieHandler") + .to(getTestServerEndpointSessionUrl() + "?cookieHandler=#exchangeCookieHandler") + .to("mock:result"); + } + }; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-http/src/test/java/org/apache/camel/component/http/handler/SessionReflectionHandler.java ---------------------------------------------------------------------- diff --git a/components/camel-http/src/test/java/org/apache/camel/component/http/handler/SessionReflectionHandler.java b/components/camel-http/src/test/java/org/apache/camel/component/http/handler/SessionReflectionHandler.java new file mode 100644 index 0000000..63c13e3 --- /dev/null +++ b/components/camel-http/src/test/java/org/apache/camel/component/http/handler/SessionReflectionHandler.java @@ -0,0 +1,47 @@ +/** + * 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.http.handler; + +import java.io.IOException; +import java.io.OutputStream; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.camel.util.IOHelper; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; + +public class SessionReflectionHandler extends AbstractHandler { + + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + HttpSession session = request.getSession(); + OutputStream os = response.getOutputStream(); + baseRequest.setHandled(true); + if (session.getAttribute("foo") == null) { + session.setAttribute("foo", "bar"); + os.write("New ".getBytes()); + } else { + os.write("Old ".getBytes()); + } + IOHelper.copyAndCloseInput(request.getInputStream(), os); + response.setStatus(HttpServletResponse.SC_OK); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-http4/pom.xml ---------------------------------------------------------------------- diff --git a/components/camel-http4/pom.xml b/components/camel-http4/pom.xml index 8209bce..2c54bd0 100644 --- a/components/camel-http4/pom.xml +++ b/components/camel-http4/pom.xml @@ -95,6 +95,12 @@ <classifier>tests</classifier> <scope>test</scope> </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-server</artifactId> + <version>${jetty-version}</version> + <scope>test</scope> + </dependency> </dependencies> <build> http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-http4/src/main/docs/http4-component.adoc ---------------------------------------------------------------------- diff --git a/components/camel-http4/src/main/docs/http4-component.adoc b/components/camel-http4/src/main/docs/http4-component.adoc index f90dcdd..c45269d 100644 --- a/components/camel-http4/src/main/docs/http4-component.adoc +++ b/components/camel-http4/src/main/docs/http4-component.adoc @@ -92,7 +92,7 @@ The HTTP4 component supports 13 options which are listed below. // endpoint options: START -The HTTP4 component supports 31 endpoint options which are listed below: +The HTTP4 component supports 32 endpoint options which are listed below: {% raw %} [width="100%",cols="2,1,1m,1m,5",options="header"] @@ -106,7 +106,8 @@ The HTTP4 component supports 31 endpoint options which are listed below: | chunked | producer | true | boolean | If this option is false the Servlet will disable the HTTP streaming and set the content-length header on the response | clearExpiredCookies | producer | true | boolean | Whether to clear expired cookies before sending the HTTP request. This ensures the cookies store does not keep growing by adding new cookies which is newer removed when they are expired. | connectionClose | producer | false | boolean | Specifies whether a Connection Close header must be added to HTTP Request. By default connectionClose is false. -| cookieStore | producer | | CookieStore | To use a custom org.apache.http.client.CookieStore. By default the org.apache.http.impl.client.BasicCookieStore is used which is an in-memory only cookie store. Notice if bridgeEndpoint=true then the cookie store is forced to be a noop cookie store as cookie shouldn't be stored as we are just bridging (eg acting as a proxy). +| cookieHandler | producer | | CookieHandler | Configure a cookie handler to maintain a HTTP session +| cookieStore | producer | | CookieStore | To use a custom org.apache.http.client.CookieStore. By default the org.apache.http.impl.client.BasicCookieStore is used which is an in-memory only cookie store. Notice if bridgeEndpoint=true then the cookie store is forced to be a noop cookie store as cookie shouldn't be stored as we are just bridging (eg acting as a proxy). If a cookieHandler is set then the cookie store is also forced to be a noop cookie store as cookie handling is then performed by the cookieHandler. | copyHeaders | producer | true | boolean | If this option is true then IN exchange headers will be copied to OUT exchange headers according to copy strategy. Setting this to false allows to only include the headers from the HTTP response (not propagating IN headers). | headerFilterStrategy | producer | | HeaderFilterStrategy | To use a custom HeaderFilterStrategy to filter header to and from Camel message. | httpBinding | producer | | HttpBinding | To use a custom HttpBinding to control the mapping between Camel message and HttpClient. http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpEndpoint.java ---------------------------------------------------------------------- diff --git a/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpEndpoint.java b/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpEndpoint.java index 34161e6..01d1b7e 100644 --- a/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpEndpoint.java +++ b/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpEndpoint.java @@ -27,6 +27,7 @@ import org.apache.camel.Processor; import org.apache.camel.Producer; import org.apache.camel.http.common.HttpCommonEndpoint; import org.apache.camel.http.common.HttpHelper; +import org.apache.camel.http.common.cookie.CookieHandler; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; import org.apache.camel.util.IOHelper; @@ -269,11 +270,19 @@ public class HttpEndpoint extends HttpCommonEndpoint { * By default the org.apache.http.impl.client.BasicCookieStore is used which is an in-memory only cookie store. * Notice if bridgeEndpoint=true then the cookie store is forced to be a noop cookie store as cookie * shouldn't be stored as we are just bridging (eg acting as a proxy). + * If a cookieHandler is set then the cookie store is also forced to be a noop cookie store as cookie handling is + * then performed by the cookieHandler. */ public void setCookieStore(CookieStore cookieStore) { this.cookieStore = cookieStore; } + public void setCookieHandler(CookieHandler cookieHandler) { + super.setCookieHandler(cookieHandler); + // if we set an explicit cookie handler + this.cookieStore = new NoopCookieStore(); + } + public boolean isAuthenticationPreemptive() { return authenticationPreemptive; } http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpProducer.java ---------------------------------------------------------------------- diff --git a/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpProducer.java b/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpProducer.java index dc60660..085d3d7 100644 --- a/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpProducer.java +++ b/components/camel-http4/src/main/java/org/apache/camel/component/http4/HttpProducer.java @@ -26,11 +26,13 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import org.apache.camel.CamelExchangeException; import org.apache.camel.Exchange; @@ -155,6 +157,19 @@ public class HttpProducer extends DefaultProducer { } } + if (getEndpoint().getCookieHandler() != null) { + Map<String, List<String>> cookieHeaders = getEndpoint().getCookieHandler().loadCookies(exchange, httpRequest.getURI()); + for (Map.Entry<String, List<String>> entry : cookieHeaders.entrySet()) { + String key = entry.getKey(); + if (entry.getValue().size() > 0) { + // 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 = entry.getValue().size() > 1 ? entry.getValue().toString() : entry.getValue().get(0); + httpRequest.addHeader(key, s); + } + } + } + //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()) { @@ -236,9 +251,11 @@ public class HttpProducer extends DefaultProducer { // propagate HTTP response headers Header[] headers = httpResponse.getAllHeaders(); + Map<String, List<String>> m = new HashMap<String, List<String>>(); for (Header header : headers) { String name = header.getName(); String value = header.getValue(); + m.put(name, Collections.singletonList(value)); if (name.toLowerCase().equals("content-type")) { name = Exchange.CONTENT_TYPE; exchange.setProperty(Exchange.CHARSET_NAME, IOHelper.getCharsetNameFromContentType(value)); @@ -249,7 +266,10 @@ public class HttpProducer extends DefaultProducer { HttpHelper.appendHeader(answer.getHeaders(), name, extracted); } } - + // handle cookies + if (getEndpoint().getCookieHandler() != null) { + getEndpoint().getCookieHandler().storeCookies(exchange, httpRequest.getURI(), m); + } // endpoint might be configured to copy headers from in to out // to avoid overriding existing headers with old values just // filter the http protocol headers @@ -264,6 +284,14 @@ public class HttpProducer extends DefaultProducer { String uri = httpRequest.getURI().toString(); String statusText = httpResponse.getStatusLine() != null ? httpResponse.getStatusLine().getReasonPhrase() : null; Map<String, String> headers = extractResponseHeaders(httpResponse.getAllHeaders()); + // handle cookies + if (getEndpoint().getCookieHandler() != null) { + Map<String, List<String>> m = new HashMap<String, List<String>>(); + for (Entry<String, String> e : headers.entrySet()) { + m.put(e.getKey(), Collections.singletonList(e.getValue())); + } + getEndpoint().getCookieHandler().storeCookies(exchange, httpRequest.getURI(), m); + } Object responseBody = extractResponseBody(httpRequest, httpResponse, exchange, getEndpoint().isIgnoreResponseBody()); if (transferException && responseBody != null && responseBody instanceof Exception) { http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpProducerSessionTest.java ---------------------------------------------------------------------- diff --git a/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpProducerSessionTest.java b/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpProducerSessionTest.java new file mode 100644 index 0000000..0a81d41 --- /dev/null +++ b/components/camel-http4/src/test/java/org/apache/camel/component/http4/HttpProducerSessionTest.java @@ -0,0 +1,117 @@ +/** + * 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.http4; + +import java.net.InetSocketAddress; + +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.http4.handler.SessionReflectionHandler; +import org.apache.camel.http.common.cookie.ExchangeCookieHandler; +import org.apache.camel.http.common.cookie.InstanceCookieHandler; +import org.apache.camel.impl.JndiRegistry; +import org.apache.camel.test.AvailablePortFinder; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.session.SessionHandler; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class HttpProducerSessionTest extends CamelTestSupport { + private static volatile int port; + private static Server localServer; + + @BeforeClass + public static void initServer() throws Exception { + port = AvailablePortFinder.getNextAvailable(24000); + localServer = new Server(new InetSocketAddress("127.0.0.1", port)); + ContextHandler contextHandler = new ContextHandler(); + contextHandler.setContextPath("/session"); + SessionHandler sessionHandler = new SessionHandler(); + sessionHandler.setHandler(new SessionReflectionHandler()); + contextHandler.setHandler(sessionHandler); + localServer.setHandler(contextHandler); + localServer.start(); + } + + @AfterClass + public static void shutdownServer() throws Exception { + localServer.stop(); + } + + @Test + public void testNoSession() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("New New World", "New New World"); + template.sendBody("direct:start", "World"); + template.sendBody("direct:start", "World"); + assertMockEndpointsSatisfied(); + } + + @Test + public void testInstanceSession() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("Old New World", "Old Old World"); + template.sendBody("direct:instance", "World"); + template.sendBody("direct:instance", "World"); + assertMockEndpointsSatisfied(); + } + + @Test + public void testExchangeSession() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("Old New World", "Old New World"); + template.sendBody("direct:exchange", "World"); + template.sendBody("direct:exchange", "World"); + assertMockEndpointsSatisfied(); + } + + @Override + protected JndiRegistry createRegistry() throws Exception { + JndiRegistry jndiRegistry = super.createRegistry(); + jndiRegistry.bind("instanceCookieHandler", new InstanceCookieHandler()); + jndiRegistry.bind("exchangeCookieHandler", new ExchangeCookieHandler()); + jndiRegistry.bind("noopCookieStore", new NoopCookieStore()); + return jndiRegistry; + } + + private String getTestServerEndpointSessionUrl() { + // session handling will not work for localhost + return "http4://127.0.0.1:" + port + "/session/"; + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start") + .to(getTestServerEndpointSessionUrl() + "?cookieStore=#noopCookieStore") + .to(getTestServerEndpointSessionUrl() + "?cookieStore=#noopCookieStore") + .to("mock:result"); + + from("direct:instance") + .to(getTestServerEndpointSessionUrl() + "?cookieHandler=#instanceCookieHandler") + .to(getTestServerEndpointSessionUrl() + "?cookieHandler=#instanceCookieHandler") + .to("mock:result"); + + from("direct:exchange") + .to(getTestServerEndpointSessionUrl() + "?cookieHandler=#exchangeCookieHandler") + .to(getTestServerEndpointSessionUrl() + "?cookieHandler=#exchangeCookieHandler") + .to("mock:result"); + } + }; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-http4/src/test/java/org/apache/camel/component/http4/handler/SessionReflectionHandler.java ---------------------------------------------------------------------- diff --git a/components/camel-http4/src/test/java/org/apache/camel/component/http4/handler/SessionReflectionHandler.java b/components/camel-http4/src/test/java/org/apache/camel/component/http4/handler/SessionReflectionHandler.java new file mode 100644 index 0000000..0358ce5 --- /dev/null +++ b/components/camel-http4/src/test/java/org/apache/camel/component/http4/handler/SessionReflectionHandler.java @@ -0,0 +1,47 @@ +/** + * 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.http4.handler; + +import java.io.IOException; +import java.io.OutputStream; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.camel.util.IOHelper; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.handler.AbstractHandler; + +public class SessionReflectionHandler extends AbstractHandler { + + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + HttpSession session = request.getSession(); + OutputStream os = response.getOutputStream(); + baseRequest.setHandled(true); + if (session.getAttribute("foo") == null) { + session.setAttribute("foo", "bar"); + os.write("New ".getBytes()); + } else { + os.write("Old ".getBytes()); + } + IOHelper.copyAndCloseInput(request.getInputStream(), os); + response.setStatus(HttpServletResponse.SC_OK); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-jetty-common/src/main/java/org/apache/camel/component/jetty/JettyHttpEndpoint.java ---------------------------------------------------------------------- diff --git a/components/camel-jetty-common/src/main/java/org/apache/camel/component/jetty/JettyHttpEndpoint.java b/components/camel-jetty-common/src/main/java/org/apache/camel/component/jetty/JettyHttpEndpoint.java index 8cfc32a..9d2a291 100644 --- a/components/camel-jetty-common/src/main/java/org/apache/camel/component/jetty/JettyHttpEndpoint.java +++ b/components/camel-jetty-common/src/main/java/org/apache/camel/component/jetty/JettyHttpEndpoint.java @@ -29,6 +29,7 @@ import org.apache.camel.Producer; import org.apache.camel.ResolveEndpointFailedException; import org.apache.camel.http.common.HttpCommonEndpoint; import org.apache.camel.http.common.HttpConsumer; +import org.apache.camel.http.common.cookie.CookieHandler; import org.apache.camel.impl.SynchronousDelegateProducer; import org.apache.camel.spi.UriParam; import org.apache.camel.util.IntrospectionSupport; @@ -129,6 +130,8 @@ public abstract class JettyHttpEndpoint extends HttpCommonEndpoint { description = "To configure security using SSLContextParameters") @Deprecated private String sslContextParametersRef; + @UriParam(label = "producer", description = "Configure a cookie handler to maintain a HTTP session") + private CookieHandler cookieHandler; public JettyHttpEndpoint(JettyHttpComponent component, String uri, URI httpURL) throws URISyntaxException { super(uri, component, httpURL); @@ -437,6 +440,17 @@ public abstract class JettyHttpEndpoint extends HttpCommonEndpoint { this.enableCORS = enableCORS; } + public CookieHandler getCookieHandler() { + return cookieHandler; + } + + /** + * Configure a cookie handler to maintain a HTTP session + */ + public void setCookieHandler(CookieHandler cookieHandler) { + this.cookieHandler = cookieHandler; + } + public abstract JettyContentExchange createContentExchange(); } http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-jetty-common/src/main/java/org/apache/camel/component/jetty/JettyHttpProducer.java ---------------------------------------------------------------------- diff --git a/components/camel-jetty-common/src/main/java/org/apache/camel/component/jetty/JettyHttpProducer.java b/components/camel-jetty-common/src/main/java/org/apache/camel/component/jetty/JettyHttpProducer.java index e0595bc..e8a7f1b 100644 --- a/components/camel-jetty-common/src/main/java/org/apache/camel/component/jetty/JettyHttpProducer.java +++ b/components/camel-jetty-common/src/main/java/org/apache/camel/component/jetty/JettyHttpProducer.java @@ -19,6 +19,7 @@ package org.apache.camel.component.jetty; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.Serializable; +import java.net.CookieStore; import java.net.URI; import java.util.ArrayList; import java.util.Iterator; @@ -225,7 +226,7 @@ public class JettyHttpProducer extends DefaultAsyncProducer implements AsyncProc } } } - + if (getEndpoint().isConnectionClose()) { httpExchange.addRequestHeader("Connection", "close"); } @@ -244,6 +245,15 @@ public class JettyHttpProducer extends DefaultAsyncProducer implements AsyncProc if (LOG.isDebugEnabled()) { LOG.debug("Sending HTTP request to: {}", httpExchange.getUrl()); } + + if (getEndpoint().getCookieHandler() != null) { + // this will store the cookie in the cookie store + CookieStore cookieStore = getEndpoint().getCookieHandler().getCookieStore(exchange); + if (!client.getCookieStore().equals(cookieStore)) { + client.setCookieStore(cookieStore); + } + } + httpExchange.send(client); } http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-jetty9/src/main/docs/jetty-component.adoc ---------------------------------------------------------------------- diff --git a/components/camel-jetty9/src/main/docs/jetty-component.adoc b/components/camel-jetty9/src/main/docs/jetty-component.adoc index febf26e..9aae5a7 100644 --- a/components/camel-jetty9/src/main/docs/jetty-component.adoc +++ b/components/camel-jetty9/src/main/docs/jetty-component.adoc @@ -106,7 +106,7 @@ The Jetty 9 component supports 31 options which are listed below. // endpoint options: START -The Jetty 9 component supports 53 endpoint options which are listed below: +The Jetty 9 component supports 54 endpoint options which are listed below: {% raw %} [width="100%",cols="2,1,1m,1m,5",options="header"] @@ -145,6 +145,7 @@ The Jetty 9 component supports 53 endpoint options which are listed below: | authMethodPriority | producer | | String | Authentication method for proxy either as Basic Digest or NTLM. | bridgeEndpoint | producer | false | boolean | If the option is true HttpProducer will ignore the Exchange.HTTP_URI header and use the endpoint's URI for request. You may also set the option throwExceptionOnFailure to be false to let the HttpProducer send all the fault response back. | connectionClose | producer | false | boolean | Specifies whether a Connection Close header must be added to HTTP Request. By default connectionClose is false. +| cookieHandler | producer | | CookieHandler | Configure a cookie handler to maintain a HTTP session | copyHeaders | producer | true | boolean | If this option is true then IN exchange headers will be copied to OUT exchange headers according to copy strategy. Setting this to false allows to only include the headers from the HTTP response (not propagating IN headers). | httpClientMaxThreads | producer | 254 | Integer | To set a value for maximum number of threads in HttpClient thread pool. This setting override any setting configured on component level. Notice that both a min and max size must be configured. If not set it default to max 254 threads used in Jettys thread pool. | httpClientMinThreads | producer | 8 | Integer | To set a value for minimum number of threads in HttpClient thread pool. This setting override any setting configured on component level. Notice that both a min and max size must be configured. If not set it default to min 8 threads used in Jettys thread pool. http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/jettyproducer/JettyHttpProducerSessionTest.java ---------------------------------------------------------------------- diff --git a/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/jettyproducer/JettyHttpProducerSessionTest.java b/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/jettyproducer/JettyHttpProducerSessionTest.java new file mode 100644 index 0000000..5aa7a63 --- /dev/null +++ b/components/camel-jetty9/src/test/java/org/apache/camel/component/jetty/jettyproducer/JettyHttpProducerSessionTest.java @@ -0,0 +1,120 @@ +/** + * 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.jetty.jettyproducer; + +import javax.servlet.http.HttpSession; + +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.http.common.HttpMessage; +import org.apache.camel.http.common.cookie.ExchangeCookieHandler; +import org.apache.camel.http.common.cookie.InstanceCookieHandler; +import org.apache.camel.impl.JndiRegistry; +import org.apache.camel.test.AvailablePortFinder; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.junit.BeforeClass; +import org.junit.Test; + +public class JettyHttpProducerSessionTest extends CamelTestSupport { + private static volatile int port; + + @BeforeClass + public static void initPort() throws Exception { + port = AvailablePortFinder.getNextAvailable(24000); + } + + @Test + public void testNoSession() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("New New World", "Old Old World"); + template.sendBody("direct:start", "World"); + template.sendBody("direct:start", "World"); + assertMockEndpointsSatisfied(); + } + + @Test + public void testInstanceSession() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("Old New World", "Old Old World"); + template.sendBody("direct:instance", "World"); + template.sendBody("direct:instance", "World"); + assertMockEndpointsSatisfied(); + } + + @Test + public void testExchangeSession() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("Old New World", "Old New World"); + template.sendBody("direct:exchange", "World"); + template.sendBody("direct:exchange", "World"); + assertMockEndpointsSatisfied(); + } + + @Override + protected JndiRegistry createRegistry() throws Exception { + JndiRegistry jndiRegistry = super.createRegistry(); + jndiRegistry.bind("instanceCookieHandler", new InstanceCookieHandler()); + jndiRegistry.bind("exchangeCookieHandler", new ExchangeCookieHandler()); + return jndiRegistry; + } + + private String getTestServerEndpointSessionUrl() { + // session handling will not work for localhost + return "http://127.0.0.1:" + port + "/session"; + } + + private String getTestServerEndpointSessionUri() { + return "jetty:" + getTestServerEndpointSessionUrl() + "?sessionSupport=true"; + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start") + .to("jetty://" + getTestServerEndpointSessionUrl()) + .to("jetty://" + getTestServerEndpointSessionUrl()) + .to("mock:result"); + + from("direct:instance") + .to("jetty://" + getTestServerEndpointSessionUrl() + "?cookieHandler=#instanceCookieHandler") + .to("jetty://" + getTestServerEndpointSessionUrl() + "?cookieHandler=#instanceCookieHandler") + .to("mock:result"); + + from("direct:exchange") + .to("jetty://" + getTestServerEndpointSessionUrl() + "?cookieHandler=#exchangeCookieHandler") + .to("jetty://" + getTestServerEndpointSessionUrl() + "?cookieHandler=#exchangeCookieHandler") + .to("mock:result"); + + from(getTestServerEndpointSessionUri()) + .process(new Processor() { + @Override + public void process(Exchange exchange) throws Exception { + HttpMessage message = exchange.getIn(HttpMessage.class); + HttpSession session = message.getRequest().getSession(); + String body = message.getBody(String.class); + if ("bar".equals(session.getAttribute("foo"))) { + message.setBody("Old " + body); + } else { + session.setAttribute("foo", "bar"); + message.setBody("New " + body); + } + } + }); + } + }; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-netty4-http/pom.xml ---------------------------------------------------------------------- diff --git a/components/camel-netty4-http/pom.xml b/components/camel-netty4-http/pom.xml index 09e881b..87f46dc 100644 --- a/components/camel-netty4-http/pom.xml +++ b/components/camel-netty4-http/pom.xml @@ -41,7 +41,11 @@ <artifactId>camel-netty4</artifactId> </dependency> <!-- we use netty-all as dependency which has HTTP included --> - + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-http-common</artifactId> + </dependency> + <!-- testing --> <dependency> <groupId>com.jcraft</groupId> @@ -80,6 +84,11 @@ <artifactId>camel-swagger-java</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-jetty9</artifactId> + <scope>test</scope> + </dependency> <!-- logging --> <dependency> http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-netty4-http/src/main/docs/netty4-http-component.adoc ---------------------------------------------------------------------- diff --git a/components/camel-netty4-http/src/main/docs/netty4-http-component.adoc b/components/camel-netty4-http/src/main/docs/netty4-http-component.adoc index 21d994a..f122836 100644 --- a/components/camel-netty4-http/src/main/docs/netty4-http-component.adoc +++ b/components/camel-netty4-http/src/main/docs/netty4-http-component.adoc @@ -109,7 +109,7 @@ The Netty4 HTTP component supports 6 options which are listed below. // endpoint options: START -The Netty4 HTTP component supports 82 endpoint options which are listed below: +The Netty4 HTTP component supports 83 endpoint options which are listed below: {% raw %} [width="100%",cols="2,1,1m,1m,5",options="header"] @@ -151,6 +151,7 @@ The Netty4 HTTP component supports 82 endpoint options which are listed below: | workerCount | consumer (advanced) | | int | When netty works on nio mode it uses default workerCount parameter from Netty which is cpu_core_threads2. User can use this operation to override the default workerCount from Netty | workerGroup | consumer (advanced) | | EventLoopGroup | To use a explicit EventLoopGroup as the boss thread pool. For example to share a thread pool with multiple consumers. By default each consumer has their own boss pool with 1 core thread. | connectTimeout | producer | 10000 | int | Time to wait for a socket connection to be available. Value is in millis. +| cookieHandler | producer | | CookieHandler | Configure a cookie handler to maintain a HTTP session | requestTimeout | producer | | long | Allows to use a timeout for the Netty producer when calling a remote server. By default no timeout is in use. The value is in milli seconds so eg 30000 is 30 seconds. The requestTimeout is using Netty's ReadTimeoutHandler to trigger the timeout. | reuseChannel | producer | false | boolean | This option allows producers to reuse the same Netty Channel for the lifecycle of processing the Exchange. This is useable if you need to call a server multiple times in a Camel route and want to use the same network connection. When using this the channel is not returned to the connection pool until the Exchange is done; or disconnected if the disconnect option is set to true. The reused Channel is stored on the Exchange as an exchange property with the key link NettyConstantsNETTY_CHANNEL which allows you to obtain the channel during routing and use it as well. | throwExceptionOnFailure | producer | true | boolean | Option to disable throwing the HttpOperationFailedException in case of failed responses from the remote server. This allows you to get all responses regardless of the HTTP status code. http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/NettyHttpEndpoint.java ---------------------------------------------------------------------- diff --git a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/NettyHttpEndpoint.java b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/NettyHttpEndpoint.java index 43adfcf..28a1e23 100644 --- a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/NettyHttpEndpoint.java +++ b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/NettyHttpEndpoint.java @@ -29,6 +29,7 @@ import org.apache.camel.Processor; import org.apache.camel.Producer; import org.apache.camel.component.netty4.NettyConfiguration; import org.apache.camel.component.netty4.NettyEndpoint; +import org.apache.camel.http.common.cookie.CookieHandler; import org.apache.camel.impl.SynchronousDelegateProducer; import org.apache.camel.spi.HeaderFilterStrategy; import org.apache.camel.spi.HeaderFilterStrategyAware; @@ -67,6 +68,8 @@ public class NettyHttpEndpoint extends NettyEndpoint implements AsyncEndpoint, H private NettyHttpSecurityConfiguration securityConfiguration; @UriParam(label = "consumer,security", prefix = "securityConfiguration.", multiValue = true) private Map<String, Object> securityOptions; // to include in component docs + @UriParam(label = "producer") + private CookieHandler cookieHandler; public NettyHttpEndpoint(String endpointUri, NettyHttpComponent component, NettyConfiguration configuration) { super(endpointUri, component, configuration); @@ -227,6 +230,17 @@ public class NettyHttpEndpoint extends NettyEndpoint implements AsyncEndpoint, H this.securityOptions = securityOptions; } + public CookieHandler getCookieHandler() { + return cookieHandler; + } + + /** + * Configure a cookie handler to maintain a HTTP session + */ + public void setCookieHandler(CookieHandler cookieHandler) { + this.cookieHandler = cookieHandler; + } + @Override protected void doStart() throws Exception { super.doStart(); http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/NettyHttpProducer.java ---------------------------------------------------------------------- diff --git a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/NettyHttpProducer.java b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/NettyHttpProducer.java index 8a6b540..31710e0 100644 --- a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/NettyHttpProducer.java +++ b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/NettyHttpProducer.java @@ -17,9 +17,10 @@ package org.apache.camel.component.netty4.http; import java.net.URI; +import java.util.List; +import java.util.Map; import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpUtil; import io.netty.util.ReferenceCountUtil; @@ -75,6 +76,16 @@ public class NettyHttpProducer extends NettyProducer { exchange.getIn().removeHeader("host"); } + if (getEndpoint().getCookieHandler() != null) { + Map<String, List<String>> cookieHeaders = getEndpoint().getCookieHandler().loadCookies(exchange, new URI(actualUri)); + for (Map.Entry<String, List<String>> entry : cookieHeaders.entrySet()) { + String key = entry.getKey(); + if (entry.getValue().size() > 0) { + request.headers().add(key, entry.getValue()); + } + } + } + // need to release the request when we are done exchange.addOnCompletion(new SynchronizationAdapter() { @Override http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/handlers/HttpClientChannelHandler.java ---------------------------------------------------------------------- diff --git a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/handlers/HttpClientChannelHandler.java b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/handlers/HttpClientChannelHandler.java index 04b0ad1..3f56e3d 100644 --- a/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/handlers/HttpClientChannelHandler.java +++ b/components/camel-netty4-http/src/main/java/org/apache/camel/component/netty4/http/handlers/HttpClientChannelHandler.java @@ -16,9 +16,13 @@ */ package org.apache.camel.component.netty4.http.handlers; +import java.net.URI; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.FullHttpResponse; -import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpUtil; import org.apache.camel.Exchange; @@ -46,6 +50,16 @@ public class HttpClientChannelHandler extends ClientChannelHandler { // just want to make sure we close the channel if the keepAlive is not true exchange.setProperty(NettyConstants.NETTY_CLOSE_CHANNEL_WHEN_COMPLETE, true); } + // handle cookies + if (producer.getEndpoint().getCookieHandler() != null) { + String actualUri = exchange.getIn().getHeader(Exchange.HTTP_URL, String.class); + URI uri = new URI(actualUri); + Map<String, List<String>> m = new HashMap<String, List<String>>(); + for (String name : response.headers().names()) { + m.put(name, response.headers().getAll(name)); + } + producer.getEndpoint().getCookieHandler().storeCookies(exchange, uri, m); + } // use the binding return producer.getEndpoint().getNettyHttpBinding().toCamelMessage(response, exchange, producer.getConfiguration()); } http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/NettyHttpProducerSessionTest.java ---------------------------------------------------------------------- diff --git a/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/NettyHttpProducerSessionTest.java b/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/NettyHttpProducerSessionTest.java new file mode 100644 index 0000000..b5f55d1 --- /dev/null +++ b/components/camel-netty4-http/src/test/java/org/apache/camel/component/netty4/http/NettyHttpProducerSessionTest.java @@ -0,0 +1,111 @@ +/** + * 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.servlet.http.HttpSession; + +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.http.common.HttpMessage; +import org.apache.camel.http.common.cookie.ExchangeCookieHandler; +import org.apache.camel.http.common.cookie.InstanceCookieHandler; +import org.apache.camel.impl.JndiRegistry; +import org.junit.Test; + +public class NettyHttpProducerSessionTest extends BaseNettyTest { + + @Test + public void testNoSession() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("New New World", "New New World"); + template.sendBody("direct:start", "World"); + template.sendBody("direct:start", "World"); + assertMockEndpointsSatisfied(); + } + + @Test + public void testInstanceSession() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("Old New World", "Old Old World"); + template.sendBody("direct:instance", "World"); + template.sendBody("direct:instance", "World"); + assertMockEndpointsSatisfied(); + } + + @Test + public void testExchangeSession() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("Old New World", "Old New World"); + template.sendBody("direct:exchange", "World"); + template.sendBody("direct:exchange", "World"); + assertMockEndpointsSatisfied(); + } + + @Override + protected JndiRegistry createRegistry() throws Exception { + JndiRegistry jndiRegistry = super.createRegistry(); + jndiRegistry.bind("instanceCookieHandler", new InstanceCookieHandler()); + jndiRegistry.bind("exchangeCookieHandler", new ExchangeCookieHandler()); + return jndiRegistry; + } + + private String getTestServerEndpointSessionUrl() { + // session handling will not work for localhost + return "http://127.0.0.1:" + getPort() + "/session"; + } + + private String getTestServerEndpointSessionUri() { + return "jetty:" + getTestServerEndpointSessionUrl() + "?sessionSupport=true"; + } + + @Override + protected RouteBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start") + .to("netty4-http:" + getTestServerEndpointSessionUrl()) + .to("netty4-http:" + getTestServerEndpointSessionUrl()) + .to("mock:result"); + + from("direct:instance") + .to("netty4-http:" + getTestServerEndpointSessionUrl() + "?cookieHandler=#instanceCookieHandler") + .to("netty4-http:" + getTestServerEndpointSessionUrl() + "?cookieHandler=#instanceCookieHandler") + .to("mock:result"); + + from("direct:exchange") + .to("netty4-http:" + getTestServerEndpointSessionUrl() + "?cookieHandler=#exchangeCookieHandler") + .to("netty4-http:" + getTestServerEndpointSessionUrl() + "?cookieHandler=#exchangeCookieHandler") + .to("mock:result"); + + from(getTestServerEndpointSessionUri()) + .process(new Processor() { + @Override + public void process(Exchange exchange) throws Exception { + HttpMessage message = exchange.getIn(HttpMessage.class); + HttpSession session = message.getRequest().getSession(); + String body = message.getBody(String.class); + if ("bar".equals(session.getAttribute("foo"))) { + message.setBody("Old " + body); + } else { + session.setAttribute("foo", "bar"); + message.setBody("New " + body); + } + } + }); + } + }; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-restlet/pom.xml ---------------------------------------------------------------------- diff --git a/components/camel-restlet/pom.xml b/components/camel-restlet/pom.xml index 9cb2b72..8555deb 100644 --- a/components/camel-restlet/pom.xml +++ b/components/camel-restlet/pom.xml @@ -51,6 +51,10 @@ <groupId>org.apache.camel</groupId> <artifactId>camel-core</artifactId> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-http-common</artifactId> + </dependency> <dependency> <groupId>org.restlet.jee</groupId> http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-restlet/src/main/docs/restlet-component.adoc ---------------------------------------------------------------------- diff --git a/components/camel-restlet/src/main/docs/restlet-component.adoc b/components/camel-restlet/src/main/docs/restlet-component.adoc index b47b775..779addf 100644 --- a/components/camel-restlet/src/main/docs/restlet-component.adoc +++ b/components/camel-restlet/src/main/docs/restlet-component.adoc @@ -102,7 +102,7 @@ The Restlet component supports 20 options which are listed below. // endpoint options: START -The Restlet component supports 21 endpoint options which are listed below: +The Restlet component supports 22 endpoint options which are listed below: {% raw %} [width="100%",cols="2,1,1m,1m,5",options="header"] @@ -119,6 +119,7 @@ The Restlet component supports 21 endpoint options which are listed below: | exceptionHandler | consumer (advanced) | | ExceptionHandler | To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this options is not in use. By default the consumer will deal with exceptions that will be logged at WARN/ERROR level and ignored. | exchangePattern | consumer (advanced) | | ExchangePattern | Sets the exchange pattern when the consumer creates an exchange. | connectTimeout | producer | 30000 | int | The Client will give up connection if the connection is timeout 0 for unlimited wait. +| cookieHandler | producer | | CookieHandler | Configure a cookie handler to maintain a HTTP session | socketTimeout | producer | 30000 | int | The Client socket receive timeout 0 for unlimited wait. | throwExceptionOnFailure | producer | true | boolean | Whether to throw exception on a producer failure. If this option is false then the http status code is set as a message header which can be checked if it has an error value. | autoCloseStream | producer (advanced) | false | boolean | Whether to auto close the stream representation as response from calling a REST service using the restlet producer. If the response is streaming and the option streamRepresentation is enabled then you may want to auto close the InputStream from the streaming response to ensure the input stream is closed when the Camel Exchange is done being routed. However if you need to read the stream outside a Camel route you may need to not auto close the stream. http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletEndpoint.java ---------------------------------------------------------------------- diff --git a/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletEndpoint.java b/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletEndpoint.java index ec2871b..be23c6c 100644 --- a/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletEndpoint.java +++ b/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletEndpoint.java @@ -27,6 +27,7 @@ import org.apache.camel.ExchangePattern; import org.apache.camel.Message; import org.apache.camel.Processor; import org.apache.camel.Producer; +import org.apache.camel.http.common.cookie.CookieHandler; import org.apache.camel.impl.DefaultEndpoint; import org.apache.camel.spi.HeaderFilterStrategy; import org.apache.camel.spi.HeaderFilterStrategyAware; @@ -84,6 +85,8 @@ public class RestletEndpoint extends DefaultEndpoint implements AsyncEndpoint, H private boolean streamRepresentation; @UriParam(label = "producer,advanced") private boolean autoCloseStream; + @UriParam(label = "producer") + private CookieHandler cookieHandler; public RestletEndpoint(RestletComponent component, String remaining) throws Exception { super(remaining, component); @@ -343,6 +346,17 @@ public class RestletEndpoint extends DefaultEndpoint implements AsyncEndpoint, H this.autoCloseStream = autoCloseStream; } + public CookieHandler getCookieHandler() { + return cookieHandler; + } + + /** + * Configure a cookie handler to maintain a HTTP session + */ + public void setCookieHandler(CookieHandler cookieHandler) { + this.cookieHandler = cookieHandler; + } + // Update the endpointUri with the restlet method information protected void updateEndpointUri() { String endpointUri = getEndpointUri(); http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletProducer.java ---------------------------------------------------------------------- diff --git a/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletProducer.java b/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletProducer.java index 97116fb..8b107bb 100644 --- a/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletProducer.java +++ b/components/camel-restlet/src/main/java/org/apache/camel/component/restlet/RestletProducer.java @@ -16,7 +16,12 @@ */ package org.apache.camel.component.restlet; +import java.io.IOException; +import java.net.CookieStore; +import java.net.HttpCookie; +import java.net.URI; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -30,6 +35,9 @@ import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; import org.restlet.Uniform; +import org.restlet.data.Cookie; +import org.restlet.data.CookieSetting; +import org.restlet.util.Series; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -80,14 +88,17 @@ public class RestletProducer extends DefaultAsyncProducer { final RestletBinding binding = endpoint.getRestletBinding(); Request request; String resourceUri = buildUri(endpoint, exchange); + URI uri = new URI(resourceUri); request = new Request(endpoint.getRestletMethod(), resourceUri); binding.populateRestletRequestFromExchange(request, exchange); + loadCookies(exchange, uri, request); LOG.debug("Sending request synchronously: {} for exchangeId: {}", request, exchange.getExchangeId()); Response response = client.handle(request); LOG.debug("Received response synchronously: {} for exchangeId: {}", response, exchange.getExchangeId()); if (response != null) { Integer respCode = response.getStatus().getCode(); + storeCookies(exchange, uri, response); if (respCode > 207 && throwException) { exchange.setException(populateRestletProducerException(exchange, response, respCode)); } else { @@ -96,6 +107,40 @@ public class RestletProducer extends DefaultAsyncProducer { } } + private void storeCookies(Exchange exchange, URI uri, Response response) { + RestletEndpoint endpoint = (RestletEndpoint) getEndpoint(); + if (endpoint.getCookieHandler() != null) { + Series<CookieSetting> cookieSettings = response.getCookieSettings(); + CookieStore cookieJar = endpoint.getCookieHandler().getCookieStore(exchange); + for (CookieSetting s:cookieSettings) { + HttpCookie cookie = new HttpCookie(s.getName(), s.getValue()); + cookie.setComment(s.getComment()); + cookie.setDomain(s.getDomain()); + cookie.setMaxAge(s.getMaxAge()); + cookie.setPath(s.getPath()); + cookie.setSecure(s.isSecure()); + cookie.setVersion(s.getVersion()); + cookieJar.add(uri, cookie); + } + } + } + + private void loadCookies(Exchange exchange, URI uri, Request request) throws IOException { + RestletEndpoint endpoint = (RestletEndpoint) getEndpoint(); + if (endpoint.getCookieHandler() != null) { + Series<Cookie> cookies = request.getCookies(); + Map<String, List<String>> cookieHeaders = endpoint.getCookieHandler().loadCookies(exchange, uri); + // parse the cookies + for (String cookieHeader : cookieHeaders.keySet()) { + for (String cookieStr : cookieHeaders.get(cookieHeader)) { + for (HttpCookie cookie : HttpCookie.parse(cookieStr)) { + cookies.add(new Cookie(cookie.getVersion(), cookie.getName(), cookie.getValue(), cookie.getPath(), cookie.getDomain())); + } + } + } + } + } + @Override public boolean process(final Exchange exchange, final AsyncCallback callback) { RestletEndpoint endpoint = (RestletEndpoint) getEndpoint(); @@ -117,8 +162,10 @@ public class RestletProducer extends DefaultAsyncProducer { Request request; try { String resourceUri = buildUri(endpoint, exchange); + URI uri = new URI(resourceUri); request = new Request(endpoint.getRestletMethod(), resourceUri); binding.populateRestletRequestFromExchange(request, exchange); + loadCookies(exchange, uri, request); } catch (Throwable e) { // break out in case of exception exchange.setException(e); @@ -134,7 +181,10 @@ public class RestletProducer extends DefaultAsyncProducer { LOG.debug("Received response asynchronously: {} for exchangeId: {}", response, exchange.getExchangeId()); try { if (response != null) { + String resourceUri = buildUri(endpoint, exchange); + URI uri = new URI(resourceUri); Integer respCode = response.getStatus().getCode(); + storeCookies(exchange, uri, response); if (respCode > 207 && throwException) { exchange.setException(populateRestletProducerException(exchange, response, respCode)); } else { http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestletProducerSessionTest.java ---------------------------------------------------------------------- diff --git a/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestletProducerSessionTest.java b/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestletProducerSessionTest.java new file mode 100644 index 0000000..abd123d --- /dev/null +++ b/components/camel-restlet/src/test/java/org/apache/camel/component/restlet/RestletProducerSessionTest.java @@ -0,0 +1,107 @@ +/** + * 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.restlet; + +import javax.servlet.http.HttpSession; + +import org.apache.camel.Exchange; +import org.apache.camel.Processor; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.http.common.HttpMessage; +import org.apache.camel.http.common.cookie.ExchangeCookieHandler; +import org.apache.camel.http.common.cookie.InstanceCookieHandler; +import org.apache.camel.impl.JndiRegistry; +import org.junit.Test; + +public class RestletProducerSessionTest extends RestletTestSupport { + private String url = "restlet:http://127.0.0.1:" + portNum + "/session?restletMethod=POST"; + + @Test + public void testProducerNoSession() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("{New New World}", "{New New World}"); + template.sendBodyAndHeader("direct:start", "{World}", Exchange.CONTENT_TYPE, "application/json"); + template.sendBodyAndHeader("direct:start", "{World}", Exchange.CONTENT_TYPE, "application/json"); + assertMockEndpointsSatisfied(); + } + + @Test + public void testProducerInstanceSession() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("{Old New World}", "{Old Old World}"); + template.sendBodyAndHeader("direct:instance", "{World}", Exchange.CONTENT_TYPE, "application/json"); + template.sendBodyAndHeader("direct:instance", "{World}", Exchange.CONTENT_TYPE, "application/json"); + assertMockEndpointsSatisfied(); + } + + @Test + public void testProducerExchangeSession() throws Exception { + getMockEndpoint("mock:result").expectedBodiesReceived("{Old New World}", "{Old New World}"); + template.sendBodyAndHeader("direct:exchange", "{World}", Exchange.CONTENT_TYPE, "application/json"); + template.sendBodyAndHeader("direct:exchange", "{World}", Exchange.CONTENT_TYPE, "application/json"); + assertMockEndpointsSatisfied(); + } + + @Override + protected JndiRegistry createRegistry() throws Exception { + JndiRegistry jndiRegistry = super.createRegistry(); + jndiRegistry.bind("instanceCookieHandler", new InstanceCookieHandler()); + jndiRegistry.bind("exchangeCookieHandler", new ExchangeCookieHandler()); + return jndiRegistry; + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:start") + .to(url) + .to(url) + .to("mock:result"); + + from("direct:instance") + .to(url + "&cookieHandler=#instanceCookieHandler") + .to(url + "&cookieHandler=#instanceCookieHandler") + .to("mock:result"); + + from("direct:exchange") + .to(url + "&cookieHandler=#exchangeCookieHandler") + .to(url + "&cookieHandler=#exchangeCookieHandler") + .to("mock:result"); + + from("jetty://http://127.0.0.1:" + portNum + "/session?sessionSupport=true") + .process(new Processor() { + public void process(Exchange exchange) throws Exception { + HttpMessage message = exchange.getIn(HttpMessage.class); + HttpSession session = message.getRequest().getSession(); + String body = message.getBody(String.class); + if (body.length() > 2) { + body = body.substring(1, body.length() - 1); + } + if ("bar".equals(session.getAttribute("foo"))) { + body = "{Old " + body + "}"; + } else { + session.setAttribute("foo", "bar"); + body = "{New " + body + "}"; + } + exchange.getOut().setBody(body); + exchange.getOut().setHeader(Exchange.CONTENT_TYPE, "application/json"); + } + }); + } + }; + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-undertow/pom.xml ---------------------------------------------------------------------- diff --git a/components/camel-undertow/pom.xml b/components/camel-undertow/pom.xml index 4f930f3..05d81e3 100644 --- a/components/camel-undertow/pom.xml +++ b/components/camel-undertow/pom.xml @@ -40,6 +40,10 @@ <artifactId>camel-core</artifactId> </dependency> <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-http-common</artifactId> + </dependency> + <dependency> <groupId>io.undertow</groupId> <artifactId>undertow-core</artifactId> <version>${undertow-version}</version> http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-undertow/src/main/docs/undertow-component.adoc ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/main/docs/undertow-component.adoc b/components/camel-undertow/src/main/docs/undertow-component.adoc index 490052f..2422104 100644 --- a/components/camel-undertow/src/main/docs/undertow-component.adoc +++ b/components/camel-undertow/src/main/docs/undertow-component.adoc @@ -65,7 +65,7 @@ The Undertow component supports 2 options which are listed below. // endpoint options: START -The Undertow component supports 17 endpoint options which are listed below: +The Undertow component supports 18 endpoint options which are listed below: {% raw %} [width="100%",cols="2,1,1m,1m,5",options="header"] @@ -78,6 +78,7 @@ The Undertow component supports 17 endpoint options which are listed below: | optionsEnabled | consumer | false | boolean | Specifies whether to enable HTTP OPTIONS for this Servlet consumer. By default OPTIONS is turned off. | exceptionHandler | consumer (advanced) | | ExceptionHandler | To let the consumer use a custom ExceptionHandler. Notice if the option bridgeErrorHandler is enabled then this options is not in use. By default the consumer will deal with exceptions that will be logged at WARN/ERROR level and ignored. | exchangePattern | consumer (advanced) | | ExchangePattern | Sets the exchange pattern when the consumer creates an exchange. +| cookieHandler | producer | | CookieHandler | Configure a cookie handler to maintain a HTTP session | keepAlive | producer | true | Boolean | Setting to ensure socket is not closed due to inactivity | options | producer | | Map | Sets additional channel options. The options that can be used are defined in org.xnio.Options. To configure from endpoint uri then prefix each option with option. such as option.close-abort=true&option.send-buffer=8192 | reuseAddresses | producer | true | Boolean | Setting to facilitate socket multiplexing http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowEndpoint.java ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowEndpoint.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowEndpoint.java index 9a36978..9ad7ed2 100644 --- a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowEndpoint.java +++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowEndpoint.java @@ -30,6 +30,7 @@ import org.apache.camel.Message; import org.apache.camel.PollingConsumer; import org.apache.camel.Processor; import org.apache.camel.Producer; +import org.apache.camel.http.common.cookie.CookieHandler; import org.apache.camel.impl.DefaultEndpoint; import org.apache.camel.spi.HeaderFilterStrategy; import org.apache.camel.spi.HeaderFilterStrategyAware; @@ -83,6 +84,8 @@ public class UndertowEndpoint extends DefaultEndpoint implements AsyncEndpoint, @UriParam(label = "consumer", description = "Specifies whether to enable HTTP OPTIONS for this Servlet consumer. By default OPTIONS is turned off.") private boolean optionsEnabled; + @UriParam(label = "producer") + private CookieHandler cookieHandler; public UndertowEndpoint(String uri, UndertowComponent component) throws URISyntaxException { super(uri, component); @@ -289,6 +292,17 @@ public class UndertowEndpoint extends DefaultEndpoint implements AsyncEndpoint, this.optionsEnabled = optionsEnabled; } + public CookieHandler getCookieHandler() { + return cookieHandler; + } + + /** + * Configure a cookie handler to maintain a HTTP session + */ + public void setCookieHandler(CookieHandler cookieHandler) { + this.cookieHandler = cookieHandler; + } + @Override protected void doStart() throws Exception { super.doStart(); http://git-wip-us.apache.org/repos/asf/camel/blob/e607dc3f/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowProducer.java ---------------------------------------------------------------------- diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowProducer.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowProducer.java index cbdcba6..7cabc4f 100644 --- a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowProducer.java +++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowProducer.java @@ -18,7 +18,12 @@ package org.apache.camel.component.undertow; import java.io.IOException; import java.net.URI; +import java.net.URISyntaxException; import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import io.undertow.client.ClientCallback; import io.undertow.client.ClientConnection; @@ -26,6 +31,7 @@ import io.undertow.client.ClientExchange; import io.undertow.client.ClientRequest; import io.undertow.client.UndertowClient; import io.undertow.server.DefaultByteBufferPool; +import io.undertow.util.HeaderMap; import io.undertow.util.Headers; import io.undertow.util.HttpString; import io.undertow.util.Protocols; @@ -100,6 +106,13 @@ public class UndertowProducer extends DefaultAsyncProducer { request.getRequestHeaders().put(Headers.CONTENT_LENGTH, bodyAsByte.array().length); } + if (getEndpoint().getCookieHandler() != null) { + Map<String, List<String>> cookieHeaders = getEndpoint().getCookieHandler().loadCookies(exchange, uri); + for (Map.Entry<String, List<String>> entry : cookieHeaders.entrySet()) { + request.getRequestHeaders().putAll(new HttpString(entry.getKey()), entry.getValue()); + } + } + if (LOG.isDebugEnabled()) { LOG.debug("Executing http {} method: {}", method, url); } @@ -166,6 +179,7 @@ public class UndertowProducer extends DefaultAsyncProducer { public void completed(ClientExchange clientExchange) { LOG.trace("completed: {}", clientExchange); try { + storeCookies(clientExchange); Message message = endpoint.getUndertowHttpBinding().toCamelMessage(clientExchange, camelExchange); if (ExchangeHelper.isOutCapable(camelExchange)) { camelExchange.setOut(message); @@ -217,6 +231,24 @@ public class UndertowProducer extends DefaultAsyncProducer { // make sure to call callback callback.done(false); } + + private void storeCookies(ClientExchange clientExchange) throws URISyntaxException, IOException { + if (endpoint.getCookieHandler() != null) { + // creating the url to use takes 2-steps + String url = UndertowHelper.createURL(camelExchange, getEndpoint()); + URI uri = UndertowHelper.createURI(camelExchange, url, getEndpoint()); + HeaderMap headerMap = clientExchange.getResponse().getResponseHeaders(); + Map<String, List<String>> m = new HashMap<String, List<String>>(); + for (HttpString headerName : headerMap.getHeaderNames()) { + List<String> headerValue = new LinkedList<String>(); + for (int i = 0; i < headerMap.count(headerName); i++) { + headerValue.add(headerMap.get(headerName, i)); + } + m.put(headerName.toString(), headerValue); + } + endpoint.getCookieHandler().storeCookies(camelExchange, uri, m); + } + } } }
