Repository: camel Updated Branches: refs/heads/camel-2.14.x 2902cf5a8 -> dc370b570
CAMEL-8810 Camel CXF may propagate wrong Content-Length headers Thanks to Stephan Siano Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/dc370b57 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/dc370b57 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/dc370b57 Branch: refs/heads/camel-2.14.x Commit: dc370b570b779af562bd8f700c82d6edc32b4c8b Parents: 2902cf5 Author: Franz Forsthofer <franz.forstho...@sap.com> Authored: Sat Jun 13 12:56:58 2015 +0200 Committer: Franz Forsthofer <franz.forstho...@sap.com> Committed: Sat Jun 13 12:56:58 2015 +0200 ---------------------------------------------------------------------- .../camel/component/cxf/DefaultCxfBinding.java | 6 +- .../cxf/CxfPayloadRouterContentLengthTest.java | 179 +++++++++++++++++++ .../cxf/CxfPayloadRouterContentLengthBeans.xml | 53 ++++++ 3 files changed, 237 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/dc370b57/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/DefaultCxfBinding.java ---------------------------------------------------------------------- diff --git a/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/DefaultCxfBinding.java b/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/DefaultCxfBinding.java index 7616568..71ed7d5 100644 --- a/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/DefaultCxfBinding.java +++ b/components/camel-cxf/src/main/java/org/apache/camel/component/cxf/DefaultCxfBinding.java @@ -690,7 +690,11 @@ public class DefaultCxfBinding implements CxfBinding, HeaderFilterStrategyAware if (transportHeaders.size() > 0) { cxfContext.put(Message.PROTOCOL_HEADERS, transportHeaders); - } + } else { + // no propagated transport headers does really mean no headers, not the ones + // from the previous request or response propagated with the invocation context + cxfContext.remove(Message.PROTOCOL_HEADERS); + } } protected static Object getContentFromCxf(Message message, DataFormat dataFormat) { http://git-wip-us.apache.org/repos/asf/camel/blob/dc370b57/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfPayloadRouterContentLengthTest.java ---------------------------------------------------------------------- diff --git a/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfPayloadRouterContentLengthTest.java b/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfPayloadRouterContentLengthTest.java new file mode 100644 index 0000000..fc55c95 --- /dev/null +++ b/components/camel-cxf/src/test/java/org/apache/camel/component/cxf/CxfPayloadRouterContentLengthTest.java @@ -0,0 +1,179 @@ +/** + * 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.cxf; + +import java.io.IOException; +import java.io.PrintWriter; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.camel.CamelContext; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.spring.SpringCamelContext; +import org.apache.camel.test.AvailablePortFinder; +import org.apache.camel.test.junit4.CamelTestSupport; +import org.apache.camel.util.IOHelper; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.AbstractHandler; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.context.support.AbstractXmlApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +public class CxfPayloadRouterContentLengthTest extends CamelTestSupport { + + /* + * The response message is generated directly. The issue here is that the + * xsi and xs namespaces are defined on the SOAP envelope but are used + * within the payload. This can cause issues with some type conversions in + * PAYLOAD mode, as the Camel-CXF endpoint will return some kind of window + * within the StAX parsing (and the namespace definitions are outside). + * + * If some CXF implementation bean is used as the service the namespaces + * will be defined within the payload (and everything works fine). + */ + private static final String RESPONSE_STRING = "This is the response string"; + private static final String RESPONSE_MESSAGE = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>" + + "<ns0:payload xmlns:ns0=\"http://schema.apache.org/test\"><ns0:response>" + + RESPONSE_STRING + + "</ns0:response></ns0:payload>" + "</s:Body></s:Envelope>"; + private static final String REQUEST_MESSAGE = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>" + + "<ns0:payload xmlns:ns0=\"http://schema.apache.org/test\"><ns0:request>foo</ns0:request></ns0:payload>" + + "</s:Body></s:Envelope>"; + + // The Camel-Test with CXF will re-use jetty instances, so the ports1 to 6 are already blocked + private static final int JETTY_PORT = AvailablePortFinder.getNextAvailable(); + private Server server; + private AbstractXmlApplicationContext applicationContext; + + static { + System.setProperty("CXFTestSupport.jettyPort", Integer.toString(JETTY_PORT)); + } + + @Override + protected CamelContext createCamelContext() throws Exception { + return SpringCamelContext.springCamelContext(applicationContext); + } + + @Before + public void setUp() throws Exception { + /* + * We start a Jetty for the service in order to have better control over + * the response The response must contain only a Content-Type and a + * Content-Length but no other header + */ + log.info("Starting jetty server at port {}", JETTY_PORT); + server = new Server(JETTY_PORT); + server.setHandler(new AbstractHandler() { + @Override + public void handle(String target, Request baseRequest, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + response.setContentType("text/xml"); + // the Content-Length is correct for this response message + response.setContentLength(RESPONSE_MESSAGE.length()); + response.setStatus(HttpServletResponse.SC_OK); + baseRequest.setHandled(true); + PrintWriter pw = response.getWriter(); + pw.write(RESPONSE_MESSAGE); + pw.close(); + } + }); + // Do not send a Server header + server.setSendServerVersion(false); + server.start(); + // Load the CXF endpoints for the route + log.info("Start Routing Scenario at port {}", CXFTestSupport.getPort1()); + applicationContext = new ClassPathXmlApplicationContext( + "org/apache/camel/component/cxf/CxfPayloadRouterContentLengthBeans.xml"); + super.setUp(); + assertNotNull("Should have created a valid spring context", applicationContext); + } + + @After + public void tearDown() throws Exception { + // close the spring context + IOHelper.close(applicationContext); + // stop the jetty server + if (server != null && server.isRunning()) { + server.stop(); + server.join(); + } + super.tearDown(); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + public void configure() { + from("cxf:bean:proxyEndpoint?dataFormat=PAYLOAD") // + .removeHeaders(".*") + // call an external Web service in payload mode + .to("cxf:bean:serviceEndpoint?dataFormat=PAYLOAD"); + } + }; + } + + @Test + public void testInvokeRouter() throws IOException { + CloseableHttpClient httpclient = HttpClients.createDefault(); + long contentLength = 0; + boolean isChunked = false; + String receivedContent = null; + try { + HttpPost httppost = new HttpPost("http://localhost:" + CXFTestSupport.getPort1() + "/TEST/PROXY"); + StringEntity reqEntity = new StringEntity(REQUEST_MESSAGE, ContentType.TEXT_XML); + reqEntity.setChunked(false); + httppost.setEntity(reqEntity); + CloseableHttpResponse response = httpclient.execute(httppost); + try { + HttpEntity respEntity = response.getEntity(); + contentLength = respEntity.getContentLength(); + isChunked = respEntity.isChunked(); + receivedContent = EntityUtils.toString(respEntity); + EntityUtils.consume(response.getEntity()); + } finally { + response.close(); + } + } finally { + httpclient.close(); + } + assertNotNull(receivedContent); + // chunked encoding is fine, we don't need to check the content length + if (!isChunked) { + assertEquals(receivedContent.length(), contentLength); + } + assertTrue("[" + receivedContent + "] does not contain [" + RESPONSE_STRING + "]", + receivedContent.contains(RESPONSE_STRING)); + // check whether the response was cut off by the client because the + // Content-Length was wrong + assertTrue("[" + receivedContent + "] does not contain the closing Envelope tag.", + receivedContent.matches(".*\\</.*:Envelope\\>")); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/dc370b57/components/camel-cxf/src/test/resources/org/apache/camel/component/cxf/CxfPayloadRouterContentLengthBeans.xml ---------------------------------------------------------------------- diff --git a/components/camel-cxf/src/test/resources/org/apache/camel/component/cxf/CxfPayloadRouterContentLengthBeans.xml b/components/camel-cxf/src/test/resources/org/apache/camel/component/cxf/CxfPayloadRouterContentLengthBeans.xml new file mode 100644 index 0000000..ccc13cc --- /dev/null +++ b/components/camel-cxf/src/test/resources/org/apache/camel/component/cxf/CxfPayloadRouterContentLengthBeans.xml @@ -0,0 +1,53 @@ +<?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. +--> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:cxf="http://camel.apache.org/schema/cxf" + xmlns:cxfcore="http://cxf.apache.org/core" + xmlns:p1="http://camel.apache.org/schema/test" + xmlns:http-conf="http://cxf.apache.org/transports/http/configuration" + + xsi:schemaLocation=" + http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd + http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd + http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd + http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd + "> + + <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/> + + <cxf:cxfEndpoint id="serviceEndpoint" address="http://localhost:${CXFTestSupport.jettyPort}/TEST/SERVICE" + endpointName="p1:generatedName"> + </cxf:cxfEndpoint> + <cxf:cxfEndpoint id="proxyEndpoint" address="http://localhost:${CXFTestSupport.port1}/TEST/PROXY" + endpointName="p1:anotherName"> + </cxf:cxfEndpoint> + + <!-- + <cxfcore:bus> + <cxfcore:features> + <cxfcore:logging/> + </cxfcore:features> + </cxfcore:bus> + --> + + <http-conf:conduit name="*/.*"> + <http-conf:client AllowChunking="false"/> + </http-conf:conduit> +</beans> \ No newline at end of file