camel-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From acosent...@apache.org
Subject camel git commit: Adding a new endpoint property to the HttpCommonEndpoint which allows preserving the Host header in reverse proxy applications, this class is ued by the Http, Http4, and Jetty producers. Updated the HttpProducer (Jetty/HTTP4) to set the
Date Fri, 08 Apr 2016 07:44:02 GMT
Repository: camel
Updated Branches:
  refs/heads/camel-2.17.x 8ebe5d68a -> 1749f36c4


Adding a new endpoint property to the HttpCommonEndpoint which allows preserving the Host
header in reverse proxy applications, this class is ued by the Http, Http4, and Jetty producers.
Updated the HttpProducer (Jetty/HTTP4) to set the Host header when this flag is enabled. 
The older HTTP component does not readily let us override the Host header, this component
will not support this parameter
Updated the Integration Test to validate the behavior for both when the new parameter is set,
and unset.


Project: http://git-wip-us.apache.org/repos/asf/camel/repo
Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/1749f36c
Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/1749f36c
Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/1749f36c

Branch: refs/heads/camel-2.17.x
Commit: 1749f36c451540c2bd0090a6228d44b2ce66ce97
Parents: 8ebe5d6
Author: Edward Welch <ed@edjusted.com>
Authored: Thu Apr 7 07:48:16 2016 -0400
Committer: Andrea Cosentino <ancosen@gmail.com>
Committed: Fri Apr 8 09:37:20 2016 +0200

----------------------------------------------------------------------
 .../camel/http/common/HttpCommonEndpoint.java   | 18 ++++
 .../camel/component/http4/HttpProducer.java     | 12 ++-
 .../component/jetty/JettyHttpProducer.java      | 12 ++-
 .../jetty/JettyBridgeHostHeaderIssueTest.java   | 90 ++++++++++++++++++--
 4 files changed, 122 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/camel/blob/1749f36c/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpCommonEndpoint.java
----------------------------------------------------------------------
diff --git a/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpCommonEndpoint.java
b/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpCommonEndpoint.java
index d6cea0a..40e6d5b 100644
--- a/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpCommonEndpoint.java
+++ b/components/camel-http-common/src/main/java/org/apache/camel/http/common/HttpCommonEndpoint.java
@@ -46,6 +46,11 @@ public abstract class HttpCommonEndpoint extends DefaultEndpoint implements
Head
             description = "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.")
     boolean bridgeEndpoint;
+    @UriParam(label = "producer",
+            description = "If the option is true, HttpProducer will set the Host header to
the value contained in the current exchange Host header, " +
+                    "useful in reverse proxy applications where you want the Host header
received by the downstream server to reflect the URL called by the upstream client, " +
+                    "this allows applications which use the Host header to generate accurate
URL's for a proxied service")
+    boolean preserveHostHeader;
     @UriParam(label = "consumer",
             description = "Whether or not the consumer should try to find a target consumer
by matching the URI prefix if no exact match is found.")
     boolean matchOnUriPrefix;
@@ -246,6 +251,19 @@ public abstract class HttpCommonEndpoint extends DefaultEndpoint implements
Head
         this.bridgeEndpoint = bridge;
     }
 
+    public boolean isPreserveHostHeader() {
+        return preserveHostHeader;
+    }
+
+    /**
+     * If the option is true, HttpProducer will set the Host header to the value contained
in the current exchange Host header,
+     * useful in reverse proxy applications where you want the Host header received by the
downstream server to reflect the URL called by the upstream client,
+     * this allows applications which use the Host header to generate accurate URL's for
a proxied service
+     */
+    public void setPreserveHostHeader(boolean preserveHostHeader) {
+        this.preserveHostHeader = preserveHostHeader;
+    }
+
     public boolean isMatchOnUriPrefix() {
         return matchOnUriPrefix;
     }

http://git-wip-us.apache.org/repos/asf/camel/blob/1749f36c/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 c59c3b8..3b38911 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
@@ -106,8 +106,6 @@ public class HttpProducer extends DefaultProducer {
             if (queryString != null) {
                 skipRequestHeaders = URISupport.parseQuery(queryString, false, true);
             }
-            // Need to remove the Host key as it should be not used
-            exchange.getIn().getHeaders().remove("host");
         }
         HttpRequestBase httpRequest = createMethod(exchange);
         Message in = exchange.getIn();
@@ -156,6 +154,16 @@ public class HttpProducer extends DefaultProducer {
             }
         }
 
+        //In reverse proxy applications it can be desirable for the downstream service to
see the original Host header
+        //if this option is set, and the exchange Host header is not null, we will set it's
current value on the httpRequest
+        if (getEndpoint().isPreserveHostHeader()) {
+            String hostHeader = exchange.getIn().getHeader("Host", String.class);
+            if (hostHeader != null) {
+                //HttpClient 4 will check to see if the Host header is present, and use it
if it is, see org.apache.http.protocol.RequestTargetHost in httpcore
+                httpRequest.setHeader("Host", hostHeader);
+            }
+        }
+
         // lets store the result in the output message.
         HttpResponse httpResponse = null;
         try {

http://git-wip-us.apache.org/repos/asf/camel/blob/1749f36c/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 10f7186..2a01b39 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
@@ -185,8 +185,6 @@ public class JettyHttpProducer extends DefaultAsyncProducer implements
AsyncProc
             if (queryString != null) {
                 skipRequestHeaders = URISupport.parseQuery(queryString, false, true);
             }
-            // Need to remove the Host key as it should be not used 
-            exchange.getIn().getHeaders().remove("host");
         }
 
         // propagate headers as HTTP headers
@@ -228,6 +226,16 @@ public class JettyHttpProducer extends DefaultAsyncProducer implements
AsyncProc
             }
         }
 
+        //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 httpExchange
+        if (getEndpoint().isPreserveHostHeader()) {
+            String hostHeader = exchange.getIn().getHeader("Host", String.class);
+            if (hostHeader != null) {
+                //HttpClient 4 will check to see if the Host header is present, and use it
if it is, see org.apache.http.protocol.RequestTargetHost in httpcore
+                httpExchange.addRequestHeader("Host", hostHeader);
+            }
+        }
+
         // set the callback, which will handle all the response logic
         if (LOG.isDebugEnabled()) {
             LOG.debug("Sending HTTP request to: {}", httpExchange.getUrl());

http://git-wip-us.apache.org/repos/asf/camel/blob/1749f36c/tests/camel-itest/src/test/java/org/apache/camel/itest/jetty/JettyBridgeHostHeaderIssueTest.java
----------------------------------------------------------------------
diff --git a/tests/camel-itest/src/test/java/org/apache/camel/itest/jetty/JettyBridgeHostHeaderIssueTest.java
b/tests/camel-itest/src/test/java/org/apache/camel/itest/jetty/JettyBridgeHostHeaderIssueTest.java
index 72074db..a55be28 100644
--- a/tests/camel-itest/src/test/java/org/apache/camel/itest/jetty/JettyBridgeHostHeaderIssueTest.java
+++ b/tests/camel-itest/src/test/java/org/apache/camel/itest/jetty/JettyBridgeHostHeaderIssueTest.java
@@ -28,13 +28,21 @@ public class JettyBridgeHostHeaderIssueTest extends CamelTestSupport {
     private int port;
     private int port2;
     private int port3;
+    private int port4;
+    private int port5;
+    private String receivedHostHeaderEndpoint1;
+    private String receivedHostHeaderEndpoint2;
+    private String receivedHostHeaderEndpoint3;
+    private String receivedHostHeaderEndpoint4;
 
     @Test
     public void testHostHeader() throws Exception {
-        // TODO: the host header is removed in bridgeEndpoint in the http4 producer, that
seems wrong
-        // as Camel as a reverse-proxy should update the host header accordingly
 
+        //The first two calls will test http4 producers
 
+        //The first call to our service will hit the first destination in the round robin
load balancer
+        //this destination has the preserveProxyHeader parameter set to true, so we verify
the Host header
+        //received by our downstream instance matches the address and port of the proxied
service
         Exchange reply = template.request("http4:localhost:" + port + "/myapp", new Processor()
{
             @Override
             public void process(Exchange exchange) throws Exception {
@@ -43,7 +51,12 @@ public class JettyBridgeHostHeaderIssueTest extends CamelTestSupport {
         });
         assertNotNull(reply);
         assertEquals("foo", reply.getOut().getBody(String.class));
+        //assert the received Host header is localhost:port (where port matches the /myapp
port)
+        assertEquals("localhost:" + port, receivedHostHeaderEndpoint1);
 
+        //The second call to our service will hit the second destination in the round robin
load balancer
+        //this destination does not have the preserveProxyHeader, so we expect the Host header
received by the destination
+        //to match the url of the destination service itself
         Exchange reply2 = template.request("http4:localhost:" + port + "/myapp", new Processor()
{
             @Override
             public void process(Exchange exchange) throws Exception {
@@ -52,6 +65,35 @@ public class JettyBridgeHostHeaderIssueTest extends CamelTestSupport {
         });
         assertNotNull(reply2);
         assertEquals("bar", reply2.getOut().getBody(String.class));
+        //assert the received Host header is localhost:port3 (where port3 matches the /bar
destination server)
+        assertEquals("localhost:" + port3, receivedHostHeaderEndpoint2);
+
+
+        //The next two calls will use/test the jetty producers in the round robin load balancer
+
+        //The first has the preserveHostHeader option set to true, so we would expect to
receive a Host header matching the /myapp proxied service
+        Exchange reply3 = template.request("http4:localhost:" + port + "/myapp", new Processor()
{
+            @Override
+            public void process(Exchange exchange) throws Exception {
+                exchange.getIn().setBody("Bye JWorld");
+            }
+        });
+        assertNotNull(reply3);
+        assertEquals("jbar", reply3.getOut().getBody(String.class));
+        //assert the received Host header is localhost:port (where port matches the /myapp
destination server)
+        assertEquals("localhost:" + port, receivedHostHeaderEndpoint3);
+
+        //The second does not have a preserveHostHeader (preserveHostHeader=false), we would
expect to see a Host header matching the destination service
+        Exchange reply4 = template.request("http4:localhost:" + port + "/myapp", new Processor()
{
+            @Override
+            public void process(Exchange exchange) throws Exception {
+                exchange.getIn().setBody("JAVA!!!!");
+            }
+        });
+        assertNotNull(reply4);
+        assertEquals("java???", reply4.getOut().getBody(String.class));
+        //assert the received Host header is localhost:port5 (where port3 matches the /jbarf
destination server)
+        assertEquals("localhost:" + port5, receivedHostHeaderEndpoint4);
     }
 
     @Override
@@ -59,18 +101,54 @@ public class JettyBridgeHostHeaderIssueTest extends CamelTestSupport
{
         port = AvailablePortFinder.getNextAvailable(12000);
         port2 = AvailablePortFinder.getNextAvailable(12100);
         port3 = AvailablePortFinder.getNextAvailable(12200);
+        port4 = AvailablePortFinder.getNextAvailable(12300);
+        port5 = AvailablePortFinder.getNextAvailable(12400);
 
         return new RouteBuilder() {
             @Override
             public void configure() throws Exception {
                 from("jetty:http://localhost:" + port + "/myapp?matchOnUriPrefix=true")
                     .loadBalance().roundRobin()
-                        .to("http4://localhost:" + port2 + "/foo?bridgeEndpoint=true&throwExceptionOnFailure=false")
-                        .to("http4://localhost:" + port3 + "/bar?bridgeEndpoint=true&throwExceptionOnFailure=false");
+                        .to("http4://localhost:" + port2 + "/foo?bridgeEndpoint=true&throwExceptionOnFailure=false&preserveHostHeader=true")
+                        .to("http4://localhost:" + port3 + "/bar?bridgeEndpoint=true&throwExceptionOnFailure=false")
+                        .to("jetty:http://localhost:" + port4 + "/jbar?bridgeEndpoint=true&throwExceptionOnFailure=false&preserveHostHeader=true")
+                        .to("jetty:http://localhost:" + port5 + "/jbarf?bridgeEndpoint=true&throwExceptionOnFailure=false");
+
+                from("jetty:http://localhost:" + port2 + "/foo")
+                        .process(new Processor() {
+                            @Override
+                            public void process(Exchange exchange) throws Exception {
+                                receivedHostHeaderEndpoint1 = exchange.getIn().getHeader("Host",
String.class);
+                            }
+                        })
+                        .transform().constant("foo");
+
+                from("jetty:http://localhost:" + port3 + "/bar")
+                        .process(new Processor() {
+                            @Override
+                            public void process(Exchange exchange) throws Exception {
+                                receivedHostHeaderEndpoint2 = exchange.getIn().getHeader("Host",
String.class);
+                            }
+                        })
+                        .transform().constant("bar");
 
-                from("jetty:http://localhost:" + port2 + "/foo").transform().constant("foo");
+                from("jetty:http://localhost:" + port4 + "/jbar")
+                        .process(new Processor() {
+                            @Override
+                            public void process(Exchange exchange) throws Exception {
+                                receivedHostHeaderEndpoint3 = exchange.getIn().getHeader("Host",
String.class);
+                            }
+                        })
+                        .transform().constant("jbar");
 
-                from("jetty:http://localhost:" + port3 + "/bar").transform().constant("bar");
+                from("jetty:http://localhost:" + port5 + "/jbarf")
+                        .process(new Processor() {
+                            @Override
+                            public void process(Exchange exchange) throws Exception {
+                                receivedHostHeaderEndpoint4 = exchange.getIn().getHeader("Host",
String.class);
+                            }
+                        })
+                        .transform().constant("java???");
             }
         };
     }


Mime
View raw message