You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by su...@apache.org on 2015/05/18 23:14:43 UTC
knox git commit: KNOX-476 implementation for X-Forwarded-* headers
support and population
Repository: knox
Updated Branches:
refs/heads/master bd26abb21 -> dd16033d3
KNOX-476 implementation for X-Forwarded-* headers support and population
Project: http://git-wip-us.apache.org/repos/asf/knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/dd16033d
Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/dd16033d
Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/dd16033d
Branch: refs/heads/master
Commit: dd16033d34346c61b9666e614f5872f7c93a7b39
Parents: bd26abb
Author: Sumit Gupta <su...@apache.org>
Authored: Mon May 18 17:13:26 2015 -0400
Committer: Sumit Gupta <su...@apache.org>
Committed: Mon May 18 17:13:26 2015 -0400
----------------------------------------------------------------------
.../filter/rewrite/impl/UrlRewriteResponse.java | 36 +++-
gateway-server-xforwarded-filter/pom.xml | 67 +++++++
.../gateway/filter/CompositeEnumeration.java | 56 ++++++
.../gateway/filter/XForwardedHeaderFilter.java | 32 ++++
.../filter/XForwardedHeaderRequestWrapper.java | 143 ++++++++++++++
.../filter/CompositeEnumerationTest.java | 117 ++++++++++++
.../gateway/filter/TestFilterAdapter.java | 45 +++++
.../hadoop/gateway/filter/TestFilterChain.java | 35 ++++
.../filter/XForwardHeaderFilterTest.java | 108 +++++++++++
gateway-server/pom.xml | 4 +
.../gateway/config/impl/GatewayConfigImpl.java | 7 +
.../ServiceDefinitionDeploymentContributor.java | 17 +-
.../hadoop/gateway/config/GatewayConfig.java | 2 +
.../hadoop/test/mock/MockRequestMatcher.java | 18 +-
.../hadoop/gateway/GatewayBasicFuncTest.java | 190 ++++++++++++++++++-
.../hadoop/gateway/GatewayTestConfig.java | 10 +
.../deploy/DeploymentFactoryFuncTest.java | 17 +-
pom.xml | 6 +
18 files changed, 893 insertions(+), 17 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteResponse.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteResponse.java b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteResponse.java
index d9ab6f7..9fbc68a 100644
--- a/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteResponse.java
+++ b/gateway-provider-rewrite/src/main/java/org/apache/hadoop/gateway/filter/rewrite/impl/UrlRewriteResponse.java
@@ -80,6 +80,9 @@ public class UrlRewriteResponse extends GatewayResponseWrapper implements Params
private UrlRewriteFilterContentDescriptor headersFilterConfig;
private String cookiesFilterName;
private UrlRewriteFilterContentDescriptor cookiesFilterConfig;
+ private String xForwardedHostname;
+ private String xForwardedPort;
+ private String xForwardedScheme;
public UrlRewriteResponse( FilterConfig config, HttpServletRequest request, HttpServletResponse response )
throws IOException {
@@ -89,6 +92,7 @@ public class UrlRewriteResponse extends GatewayResponseWrapper implements Params
this.request = request;
this.response = response;
this.output = null;
+ getXForwardedHeaders();
this.bodyFilterName = config.getInitParameter( UrlRewriteServletFilter.RESPONSE_BODY_FILTER_PARAM );
this.headersFilterName = config.getInitParameter( UrlRewriteServletFilter.RESPONSE_HEADERS_FILTER_PARAM );
this.headersFilterConfig = getRewriteFilterConfig( rewriter.getConfig(), headersFilterName, UrlRewriteServletFilter.HEADERS_MIME_TYPE );
@@ -193,15 +197,15 @@ public class UrlRewriteResponse extends GatewayResponseWrapper implements Params
private String getGatewayParam( String name ) {
if( "url".equals( name ) ) {
- return request.getScheme() + "://" + getRequestLocalHostName() + ":" + request.getLocalPort() + request.getContextPath();
+ return xForwardedScheme + "://" + xForwardedHostname + ":" + xForwardedPort + request.getContextPath();
} else if( "scheme".equals( name ) ) {
- return request.getScheme();
+ return xForwardedScheme;
} else if( "host".equals( name ) ) {
- return getRequestLocalHostName();
+ return xForwardedHostname;
} else if( "port".equals( name ) ) {
- return Integer.toString( request.getLocalPort() );
+ return xForwardedPort;
} else if( "addr".equals( name ) || "address".equals( name ) ) {
- return getRequestLocalHostName() + ":" + request.getLocalPort();
+ return xForwardedHostname + ":" + xForwardedPort;
} else if( "path".equals( name ) ) {
return request.getContextPath();
} else {
@@ -252,4 +256,26 @@ public class UrlRewriteResponse extends GatewayResponseWrapper implements Params
throw new UnsupportedOperationException();
}
+ private void getXForwardedHeaders() {
+ xForwardedHostname = request.getHeader( "X-Forwarded-Host" );
+ xForwardedPort = request.getHeader( "X-Forwarded-Port" );
+ xForwardedScheme = request.getHeader( "X-Forwarded-Proto" );
+ if ( xForwardedScheme == null ) {
+ xForwardedScheme = request.getScheme();
+ }
+ if ( xForwardedHostname != null ) {
+ int separator = xForwardedHostname.indexOf( ":" );
+ if ( separator > 0 ) {
+ //a specific port in the forwarded host wins
+ xForwardedPort = xForwardedHostname.substring(separator + 1, xForwardedHostname.length());
+ xForwardedHostname = xForwardedHostname.substring( 0, separator );
+ }
+ } else {
+ xForwardedHostname = getRequestLocalHostName();
+ }
+ if (xForwardedPort == null) {
+ xForwardedPort = Integer.toString( request.getLocalPort() );
+ }
+ }
+
}
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-server-xforwarded-filter/pom.xml
----------------------------------------------------------------------
diff --git a/gateway-server-xforwarded-filter/pom.xml b/gateway-server-xforwarded-filter/pom.xml
new file mode 100644
index 0000000..fb18df2
--- /dev/null
+++ b/gateway-server-xforwarded-filter/pom.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>gateway</artifactId>
+ <groupId>org.apache.knox</groupId>
+ <version>0.7.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>gateway-server-xforwarded-filter</artifactId>
+ <name>gateway-server-xforwarded-filter</name>
+ <description>Filter for x-forwarded headers</description>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.knox</groupId>
+ <artifactId>gateway-spi</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>${gateway-group}</groupId>
+ <artifactId>gateway-test-utils</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-library</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-server-xforwarded-filter/src/main/java/org/apache/hadoop/gateway/filter/CompositeEnumeration.java
----------------------------------------------------------------------
diff --git a/gateway-server-xforwarded-filter/src/main/java/org/apache/hadoop/gateway/filter/CompositeEnumeration.java b/gateway-server-xforwarded-filter/src/main/java/org/apache/hadoop/gateway/filter/CompositeEnumeration.java
new file mode 100644
index 0000000..c5190e6
--- /dev/null
+++ b/gateway-server-xforwarded-filter/src/main/java/org/apache/hadoop/gateway/filter/CompositeEnumeration.java
@@ -0,0 +1,56 @@
+/**
+ * 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.hadoop.gateway.filter;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+public class CompositeEnumeration<T> implements Enumeration<T> {
+
+ private int index = 0;
+ private Enumeration<T>[] array;
+
+ public CompositeEnumeration(Enumeration<T>... enumerations) {
+ if( enumerations == null ) {
+ throw new IllegalArgumentException( "enumerations==null" );
+ }
+ this.array = enumerations;
+ }
+
+ @Override
+ public boolean hasMoreElements() {
+ while( array.length > 0 && index < array.length ) {
+ if( array[index].hasMoreElements() ) {
+ return true;
+ } else {
+ index++;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public T nextElement() {
+ if( hasMoreElements() ) {
+ return array[index].nextElement();
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-server-xforwarded-filter/src/main/java/org/apache/hadoop/gateway/filter/XForwardedHeaderFilter.java
----------------------------------------------------------------------
diff --git a/gateway-server-xforwarded-filter/src/main/java/org/apache/hadoop/gateway/filter/XForwardedHeaderFilter.java b/gateway-server-xforwarded-filter/src/main/java/org/apache/hadoop/gateway/filter/XForwardedHeaderFilter.java
new file mode 100644
index 0000000..4b46db3
--- /dev/null
+++ b/gateway-server-xforwarded-filter/src/main/java/org/apache/hadoop/gateway/filter/XForwardedHeaderFilter.java
@@ -0,0 +1,32 @@
+/**
+ * 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.hadoop.gateway.filter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public class XForwardedHeaderFilter extends AbstractGatewayFilter {
+
+ @Override
+ protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
+ chain.doFilter( new XForwardedHeaderRequestWrapper( request ), response );
+ }
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-server-xforwarded-filter/src/main/java/org/apache/hadoop/gateway/filter/XForwardedHeaderRequestWrapper.java
----------------------------------------------------------------------
diff --git a/gateway-server-xforwarded-filter/src/main/java/org/apache/hadoop/gateway/filter/XForwardedHeaderRequestWrapper.java b/gateway-server-xforwarded-filter/src/main/java/org/apache/hadoop/gateway/filter/XForwardedHeaderRequestWrapper.java
new file mode 100644
index 0000000..230854a
--- /dev/null
+++ b/gateway-server-xforwarded-filter/src/main/java/org/apache/hadoop/gateway/filter/XForwardedHeaderRequestWrapper.java
@@ -0,0 +1,143 @@
+/**
+ * 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.hadoop.gateway.filter;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+public class XForwardedHeaderRequestWrapper extends GatewayRequestWrapper {
+
+ private static String X_FORWARDED_FOR = "X-Forwarded-For";
+ private static String X_FORWARDED_FOR_LOWER = X_FORWARDED_FOR.toLowerCase();
+ private static String X_FORWARDED_PROTO = "X-Forwarded-Proto";
+ private static String X_FORWARDED_PROTO_LOWER = X_FORWARDED_PROTO.toLowerCase();
+ private static String X_FORWARDED_PORT = "X-Forwarded-Port";
+ private static String X_FORWARDED_PORT_LOWER = X_FORWARDED_PORT.toLowerCase();
+ private static String X_FORWARDED_HOST = "X-Forwarded-Host";
+ private static String X_FORWARDED_HOST_LOWER = X_FORWARDED_HOST.toLowerCase();
+ private static String X_FORWARDED_SERVER = "X-Forwarded-Server";
+ private static String X_FORWARDED_SERVER_LOWER = X_FORWARDED_SERVER.toLowerCase();
+ private static String X_FORWARDED_CONTEXT = "X-Forwarded-Context";
+ private static String X_FORWARDED_CONTEXT_LOWER = X_FORWARDED_CONTEXT.toLowerCase();
+ private static ArrayList<String> headerNames = new ArrayList<>();
+
+ static {
+ headerNames.add(X_FORWARDED_FOR);
+ headerNames.add(X_FORWARDED_PROTO);
+ headerNames.add(X_FORWARDED_PORT);
+ headerNames.add(X_FORWARDED_HOST);
+ headerNames.add(X_FORWARDED_SERVER);
+ headerNames.add(X_FORWARDED_CONTEXT);
+ }
+
+ Hashtable<String,String> proxyHeaders = new Hashtable<String, String>();
+
+ public XForwardedHeaderRequestWrapper(HttpServletRequest request) {
+ super( request );
+ setHeader( X_FORWARDED_FOR_LOWER, getForwardedFor( request ) );
+ setHeader( X_FORWARDED_PROTO_LOWER, getForwardedProto( request ) );
+ setHeader( X_FORWARDED_PORT_LOWER, getForwardedPort( request ) );
+ setHeader( X_FORWARDED_HOST_LOWER, getForwardedHost( request ) );
+ setHeader( X_FORWARDED_SERVER_LOWER, getForwardedServer( request ) );
+ setHeader( X_FORWARDED_CONTEXT_LOWER, getForwardedContext( request ) );
+ }
+
+ @Override
+ public Enumeration<String> getHeaderNames() {
+ return new CompositeEnumeration<>( Collections.enumeration(headerNames), super.getHeaderNames() );
+ }
+
+ @Override
+ public Enumeration<String> getHeaders( String name ) {
+ name = name.toLowerCase();
+ Enumeration<String> values;
+ String value = proxyHeaders.get( name );
+ if( value != null ) {
+ values = Collections.enumeration(Arrays.asList(value));
+ } else {
+ values = super.getHeaders( name );
+ }
+ return values;
+ }
+
+ @Override
+ public String getHeader( String name ) {
+ name = name.toLowerCase();
+ String value = proxyHeaders.get( name );
+ if( value == null ) {
+ value = super.getHeader( name );
+ }
+ return value;
+ }
+
+ private void setHeader( String name, String value ) {
+ if( name != null && value != null ) {
+ proxyHeaders.put( name, value );
+ }
+ }
+
+ private static String getForwardedFor( HttpServletRequest request ) {
+ String value;
+ String curr = request.getHeader( X_FORWARDED_FOR );
+ String addr = request.getRemoteAddr();
+ if( curr == null ) {
+ value = addr;
+ } else {
+ value = curr + "," + addr;
+ }
+ return value;
+ }
+
+ private static String getForwardedProto( HttpServletRequest request ) {
+ String value = request.getHeader( X_FORWARDED_PROTO );
+ if( value == null ) {
+ value = request.isSecure() ? "https" : "http";
+ }
+ return value;
+ }
+
+ private static String getForwardedPort( HttpServletRequest request ) {
+ String value = request.getHeader( X_FORWARDED_PORT );
+ if( value == null ) {
+ value = Integer.toString( request.getLocalPort() );
+ }
+ return value;
+ }
+
+ private static String getForwardedHost( HttpServletRequest request ) {
+ String value = request.getHeader( X_FORWARDED_HOST );
+ if( value == null ) {
+ value = request.getHeader( "Host" );
+ }
+ return value;
+ }
+
+ private static String getForwardedServer( HttpServletRequest request ) {
+ return request.getServerName();
+ }
+
+ private static String getForwardedContext( HttpServletRequest request ) {
+ String remote = request.getHeader( X_FORWARDED_CONTEXT );
+ String local = request.getContextPath();
+ return ( remote == null ? "" : remote ) + ( local == null ? "" : local );
+ }
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/CompositeEnumerationTest.java
----------------------------------------------------------------------
diff --git a/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/CompositeEnumerationTest.java b/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/CompositeEnumerationTest.java
new file mode 100644
index 0000000..93029dd
--- /dev/null
+++ b/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/CompositeEnumerationTest.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.hadoop.gateway.filter;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+import static junit.framework.TestCase.fail;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class CompositeEnumerationTest {
+
+ @Test
+ public void testBasics() {
+
+ String[] a = new String[]{ "1", "2" };
+ Enumeration<String> ea = Collections.enumeration( Arrays.asList( a ) );
+
+ String[] b = new String[]{ "3", "4" };
+ Enumeration<String> eb = Collections.enumeration( Arrays.asList( b ) );
+
+ CompositeEnumeration<String> ce = new CompositeEnumeration<String>( ea, eb );
+
+ assertThat( ce.nextElement(), is( "1" ) );
+ assertThat( ce.nextElement(), is( "2" ) );
+ assertThat( ce.nextElement(), is( "3" ) );
+ assertThat( ce.nextElement(), is( "4" ) );
+ assertThat( ce.hasMoreElements(), is( false ) );
+
+ }
+
+ @Test
+ public void testSingleValues() {
+ String[] a = new String[]{ "1" };
+ Enumeration<String> ea = Collections.enumeration( Arrays.asList( a ) );
+
+ String[] b = new String[]{ "2" };
+ Enumeration<String> eb = Collections.enumeration( Arrays.asList( b ) );
+
+ CompositeEnumeration<String> ce = new CompositeEnumeration<String>( ea, eb );
+
+ assertThat( ce.nextElement(), is( "1" ) );
+ assertThat( ce.nextElement(), is( "2" ) );
+ assertThat( ce.hasMoreElements(), is( false ) );
+ }
+
+ @Test
+ public void testEmptyEnumerations() {
+
+ String[] a = new String[]{ "1", "2" };
+ String[] b = new String[]{ "3", "4" };
+ String[] c = new String[]{};
+
+ Enumeration<String> e1 = Collections.enumeration( Arrays.asList( a ) );
+ Enumeration<String> e2 = Collections.enumeration( Arrays.asList( c ) );
+ CompositeEnumeration<String> ce = new CompositeEnumeration<String>( e1, e2 );
+ assertThat( ce.nextElement(), is( "1" ) );
+ assertThat( ce.nextElement(), is( "2" ) );
+ assertThat( ce.hasMoreElements(), is( false ) );
+
+ e1 = Collections.enumeration( Arrays.asList( c ) );
+ e2 = Collections.enumeration( Arrays.asList( a ) );
+ ce = new CompositeEnumeration<String>( e1, e2 );
+ assertThat( ce.nextElement(), is( "1" ) );
+ assertThat( ce.nextElement(), is( "2" ) );
+ assertThat( ce.hasMoreElements(), is( false ) );
+
+ e1 = Collections.enumeration( Arrays.asList( c ) );
+ e2 = Collections.enumeration( Arrays.asList( c ) );
+ ce = new CompositeEnumeration<String>( e1, e2 );
+ assertThat( ce.hasMoreElements(), is( false ) );
+ }
+
+ @Test
+ public void testEmpty() {
+ CompositeEnumeration<String> ce = new CompositeEnumeration<String>();
+ assertThat( ce.hasMoreElements(), is( false ) );
+
+ try {
+ ce.nextElement();
+ fail( "Should have throws NoSuchElementExcpetion" );
+ } catch( NoSuchElementException e ) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void testNulls() {
+ try {
+ CompositeEnumeration<String> ce = new CompositeEnumeration<String>( null );
+ fail( "Expected IllegalArgumentException" );
+ } catch( IllegalArgumentException e ) {
+ // Expected.
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/TestFilterAdapter.java
----------------------------------------------------------------------
diff --git a/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/TestFilterAdapter.java b/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/TestFilterAdapter.java
new file mode 100644
index 0000000..9207625
--- /dev/null
+++ b/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/TestFilterAdapter.java
@@ -0,0 +1,45 @@
+/**
+ * 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.hadoop.gateway.filter;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public abstract class TestFilterAdapter implements Filter {
+
+ public abstract void doFilter( HttpServletRequest request, HttpServletResponse response, FilterChain chain ) throws IOException, ServletException;
+
+ @Override
+ public void init( FilterConfig filterConfig ) throws ServletException {}
+
+ @Override
+ public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException {
+ doFilter( (HttpServletRequest)request, (HttpServletResponse)response, chain );
+ }
+
+ @Override
+ public void destroy() {}
+
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/TestFilterChain.java
----------------------------------------------------------------------
diff --git a/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/TestFilterChain.java b/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/TestFilterChain.java
new file mode 100644
index 0000000..1ab8e22
--- /dev/null
+++ b/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/TestFilterChain.java
@@ -0,0 +1,35 @@
+/**
+ * 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.hadoop.gateway.filter;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import java.io.IOException;
+import java.util.Stack;
+
+public class TestFilterChain extends Stack<Filter> implements FilterChain {
+ @Override
+ public void doFilter( ServletRequest request, ServletResponse response ) throws IOException, ServletException {
+ if( !isEmpty() ) pop().doFilter( request, response, this );
+ }
+}
+
+
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/XForwardHeaderFilterTest.java
----------------------------------------------------------------------
diff --git a/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/XForwardHeaderFilterTest.java b/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/XForwardHeaderFilterTest.java
new file mode 100644
index 0000000..d1d1a99
--- /dev/null
+++ b/gateway-server-xforwarded-filter/src/test/java/org/apache/hadoop/gateway/filter/XForwardHeaderFilterTest.java
@@ -0,0 +1,108 @@
+/**
+ * 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.hadoop.gateway.filter;
+
+import org.easymock.EasyMock;
+import org.junit.Test;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+public class XForwardHeaderFilterTest {
+
+ public static class AssertXForwardedHeaders extends TestFilterAdapter {
+ @Override
+ public void doFilter( HttpServletRequest request, HttpServletResponse response, FilterChain chain ) throws IOException, ServletException {
+ assertThat( request.getHeader( "X-Forwarded-For" ), is( "127.0.0.1" ) );
+ assertThat( request.getHeader( "X-Forwarded-Proto" ), is( "http" ) );
+ assertThat( request.getHeader( "X-Forwarded-Port" ), is( "8888" ) );
+ assertThat( request.getHeader( "X-Forwarded-Host" ), is( "localhost:8888" ) );
+ assertThat( request.getHeader( "X-Forwarded-Server" ), is( "localhost" ) );
+ assertThat( request.getHeader( "X-Forwarded-Context" ), is( "/context" ) );
+ }
+ }
+
+ @Test
+ public void testXForwardHeaders() throws ServletException, IOException {
+ HttpServletRequest request = EasyMock.createNiceMock( HttpServletRequest.class );
+ EasyMock.expect( request.getRemoteAddr() ).andReturn( "127.0.0.1" ).anyTimes();
+ EasyMock.expect( request.isSecure() ).andReturn( false ).anyTimes();
+ EasyMock.expect( request.getLocalPort() ).andReturn( 8888 ).anyTimes();
+ EasyMock.expect( request.getHeader( "Host" ) ).andReturn( "localhost:8888" ).anyTimes();
+ EasyMock.expect( request.getServerName() ).andReturn( "localhost" ).anyTimes();
+ EasyMock.expect( request.getContextPath() ).andReturn( "/context" ).anyTimes();
+ HttpServletResponse response = EasyMock.createNiceMock( HttpServletResponse.class );
+ EasyMock.replay( request, response );
+
+ TestFilterChain chain = new TestFilterChain();
+
+ XForwardedHeaderFilter filter = new XForwardedHeaderFilter();
+
+ chain.push( new AssertXForwardedHeaders() );
+ chain.push( filter );
+ chain.doFilter( request, response );
+ }
+
+ public static class AssertProxiedXForwardedHeaders extends TestFilterAdapter {
+ @Override
+ public void doFilter( HttpServletRequest request, HttpServletResponse response, FilterChain chain ) throws IOException, ServletException {
+ assertThat( request.getHeader( "X-Forwarded-For" ), is( "127.0.0.0,127.0.0.1" ) );
+ assertThat( request.getHeader( "X-Forwarded-Proto" ), is( "https" ) );
+ assertThat( request.getHeader( "X-Forwarded-Port" ), is( "9999" ) );
+ assertThat( request.getHeader( "X-Forwarded-Host" ), is( "remotehost:9999" ) );
+ assertThat( request.getHeader( "X-Forwarded-Server" ), is( "localhost" ) );
+ assertThat( request.getHeader( "X-Forwarded-Context" ), is( "/upstream/context" ) );
+ }
+ }
+
+ @Test
+ public void testProxiedXForwardHeaders() throws ServletException, IOException {
+ HttpServletRequest request = EasyMock.createNiceMock( HttpServletRequest.class );
+
+ EasyMock.expect( request.getHeader( "X-Forwarded-For" ) ).andReturn( "127.0.0.0" ).anyTimes();
+ EasyMock.expect( request.getHeader( "X-Forwarded-Proto" ) ).andReturn( "https" ).anyTimes();
+ EasyMock.expect( request.getHeader( "X-Forwarded-Port" ) ).andReturn( "9999" ).anyTimes();
+ EasyMock.expect( request.getHeader( "X-Forwarded-Host" ) ).andReturn( "remotehost:9999" ).anyTimes();
+ EasyMock.expect( request.getHeader( "X-Forwarded-Server" ) ).andReturn( "remotehost" ).anyTimes();
+ EasyMock.expect( request.getHeader( "X-Forwarded-Context" ) ).andReturn( "/upstream" ).anyTimes();
+
+ EasyMock.expect( request.getRemoteAddr() ).andReturn( "127.0.0.1" ).anyTimes();
+ EasyMock.expect( request.isSecure() ).andReturn( false ).anyTimes();
+ EasyMock.expect( request.getLocalPort() ).andReturn( 8888 ).anyTimes();
+ EasyMock.expect( request.getHeader( "Host" ) ).andReturn( "localhost:8888" ).anyTimes();
+ EasyMock.expect( request.getServerName() ).andReturn( "localhost" ).anyTimes();
+ EasyMock.expect( request.getContextPath() ).andReturn( "/context" ).anyTimes();
+
+ HttpServletResponse response = EasyMock.createNiceMock( HttpServletResponse.class );
+ EasyMock.replay( request, response );
+
+ TestFilterChain chain = new TestFilterChain();
+
+ XForwardedHeaderFilter filter = new XForwardedHeaderFilter();
+
+ chain.push( new AssertProxiedXForwardedHeaders() );
+ chain.push( filter );
+ chain.doFilter( request, response );
+ }
+}
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-server/pom.xml
----------------------------------------------------------------------
diff --git a/gateway-server/pom.xml b/gateway-server/pom.xml
index b3ad1f2..c06cef7 100644
--- a/gateway-server/pom.xml
+++ b/gateway-server/pom.xml
@@ -186,6 +186,10 @@
<groupId>org.apache.knox</groupId>
<artifactId>gateway-provider-rewrite</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.knox</groupId>
+ <artifactId>gateway-server-xforwarded-filter</artifactId>
+ </dependency>
<!-- ********** ********** ********** ********** ********** ********** -->
<!-- ********** Test Dependencies ********** -->
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-server/src/main/java/org/apache/hadoop/gateway/config/impl/GatewayConfigImpl.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/config/impl/GatewayConfigImpl.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/config/impl/GatewayConfigImpl.java
index 336d52e..77fb792 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/config/impl/GatewayConfigImpl.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/config/impl/GatewayConfigImpl.java
@@ -110,6 +110,7 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig {
private static final String TRUSTSTORE_PATH = GATEWAY_CONFIG_FILE_PREFIX + ".truststore.path";
private static final String TRUSTSTORE_TYPE = GATEWAY_CONFIG_FILE_PREFIX + ".truststore.type";
private static final String KEYSTORE_TYPE = GATEWAY_CONFIG_FILE_PREFIX + ".keystore.type";
+ private static final String XFORWARDED_ENABLED = GATEWAY_CONFIG_FILE_PREFIX + ".xforwarded.enabled";
// These config property names are not inline with the convention of using the
// GATEWAY_CONFIG_FILE_PREFIX as is done by those above. These are left for
@@ -429,4 +430,10 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig {
public String getKeystoreType() {
return get( KEYSTORE_TYPE, "JKS");
}
+
+ @Override
+ public boolean isXForwardedEnabled() {
+ String xForwardedEnabled = get( XFORWARDED_ENABLED, "true" );
+ return "true".equals(xForwardedEnabled);
+ }
}
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-server/src/main/java/org/apache/hadoop/gateway/deploy/impl/ServiceDefinitionDeploymentContributor.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/deploy/impl/ServiceDefinitionDeploymentContributor.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/deploy/impl/ServiceDefinitionDeploymentContributor.java
index 1daa72b..55d646f 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/deploy/impl/ServiceDefinitionDeploymentContributor.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/deploy/impl/ServiceDefinitionDeploymentContributor.java
@@ -23,8 +23,13 @@ import org.apache.hadoop.gateway.descriptor.FilterDescriptor;
import org.apache.hadoop.gateway.descriptor.FilterParamDescriptor;
import org.apache.hadoop.gateway.descriptor.ResourceDescriptor;
import org.apache.hadoop.gateway.dispatch.GatewayDispatchFilter;
+import org.apache.hadoop.gateway.filter.XForwardedHeaderFilter;
import org.apache.hadoop.gateway.filter.rewrite.api.UrlRewriteRulesDescriptor;
-import org.apache.hadoop.gateway.service.definition.*;
+import org.apache.hadoop.gateway.service.definition.CustomDispatch;
+import org.apache.hadoop.gateway.service.definition.Policy;
+import org.apache.hadoop.gateway.service.definition.Rewrite;
+import org.apache.hadoop.gateway.service.definition.Route;
+import org.apache.hadoop.gateway.service.definition.ServiceDefinition;
import org.apache.hadoop.gateway.topology.Provider;
import org.apache.hadoop.gateway.topology.Service;
import org.apache.hadoop.gateway.topology.Version;
@@ -43,7 +48,11 @@ public class ServiceDefinitionDeploymentContributor extends ServiceDeploymentCon
private static final String REPLAY_BUFFER_SIZE_PARAM = "replayBufferSize";
- public static final String DEFAULT_REPLAY_BUFFER_SIZE = "8";
+ private static final String DEFAULT_REPLAY_BUFFER_SIZE = "8";
+
+ private static final String XFORWARDED_FILTER_NAME = "XForwardedHeaderFilter";
+
+ private static final String XFORWARDED_FILTER_ROLE = "xforwardedheaders";
private ServiceDefinition serviceDefinition;
@@ -107,6 +116,10 @@ public class ServiceDefinitionDeploymentContributor extends ServiceDeploymentCon
ResourceDescriptor resource = context.getGatewayDescriptor().addResource();
resource.role(service.getRole());
resource.pattern(binding.getPath());
+ //add x-forwarded filter if enabled in config
+ if (context.getGatewayConfig().isXForwardedEnabled()) {
+ resource.addFilter().name(XFORWARDED_FILTER_NAME).role(XFORWARDED_FILTER_ROLE).impl(XForwardedHeaderFilter.class);
+ }
List<Policy> policyBindings = binding.getPolicies();
if ( policyBindings == null ) {
policyBindings = serviceDefinition.getPolicies();
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java
index 966539a..d976324 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/config/GatewayConfig.java
@@ -101,4 +101,6 @@ public interface GatewayConfig {
String getKeystoreType();
String getTruststoreType();
+
+ boolean isXForwardedEnabled();
}
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-test-utils/src/main/java/org/apache/hadoop/test/mock/MockRequestMatcher.java
----------------------------------------------------------------------
diff --git a/gateway-test-utils/src/main/java/org/apache/hadoop/test/mock/MockRequestMatcher.java b/gateway-test-utils/src/main/java/org/apache/hadoop/test/mock/MockRequestMatcher.java
index 3b16770..3ff8ff9 100644
--- a/gateway-test-utils/src/main/java/org/apache/hadoop/test/mock/MockRequestMatcher.java
+++ b/gateway-test-utils/src/main/java/org/apache/hadoop/test/mock/MockRequestMatcher.java
@@ -19,6 +19,8 @@ package org.apache.hadoop.test.mock;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
@@ -51,7 +53,7 @@ public class MockRequestMatcher {
private Set<String> methods = null;
private String pathInfo = null;
private String requestURL = null;
- Map<String,String> headers = null;
+ Map<String,Matcher> headers = null;
Set<Cookie> cookies = null;
private Map<String,Object> attributes = null;
private Map<String,String> queryParams = null;
@@ -98,9 +100,17 @@ public class MockRequestMatcher {
public MockRequestMatcher header( String name, String value ) {
if( headers == null ) {
- headers = new HashMap<String, String>();
+ headers = new HashMap<String, Matcher>();
}
- headers.put( name, value );
+ headers.put( name, Matchers.is(value) );
+ return this;
+ }
+
+ public MockRequestMatcher header( String name, Matcher matcher ) {
+ if( headers == null ) {
+ headers = new HashMap<String, Matcher>();
+ }
+ headers.put( name, matcher );
return this;
}
@@ -207,7 +217,7 @@ public class MockRequestMatcher {
assertThat(
"Request " + request.getMethod() + " " + request.getRequestURL() +
" does not have the expected value for header " + name,
- request.getHeader( name ), is( headers.get( name ) ) );
+ request.getHeader( name ), headers.get(name) );
}
}
if( cookies != null ) {
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayBasicFuncTest.java
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayBasicFuncTest.java b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayBasicFuncTest.java
index 7e830ea..8e2bfe4 100644
--- a/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayBasicFuncTest.java
+++ b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayBasicFuncTest.java
@@ -3197,7 +3197,7 @@ public class GatewayBasicFuncTest {
driver.assertComplete();
}
- private void testPostStormResource(String path) throws IOException {
+ private void testPostStormResource(String path) throws IOException {
String username = "hdfs";
String password = "hdfs-password";
String gatewayPath = driver.getUrl( "STORM" ) + path;
@@ -3223,4 +3223,192 @@ public class GatewayBasicFuncTest {
driver.assertComplete();
}
+
+
+ @Test
+ public void testXForwardHeadersPopulate() throws Exception {
+ String username = "hdfs";
+ String password = "hdfs-password";
+
+ String resourceName = "storm/topology-id.json";
+ String path = "/api/v1/topology/WordCount-1-1424792039";
+ String gatewayPath = driver.getUrl( "STORM" ) + path;
+ InetSocketAddress gatewayAddress = driver.gateway.getAddresses()[0];
+ int gatewayPort = gatewayAddress.getPort();
+ String gatewayHostName = gatewayAddress.getHostName();
+ String gatewayAddrName = InetAddress.getByName( gatewayHostName ).getHostAddress();
+
+ driver.getMock("STORM")
+ .expect()
+ .method("GET")
+ .header("X-Forwarded-Host", Matchers.isOneOf(gatewayHostName + ":" + gatewayPort, gatewayAddrName + ":" + gatewayPort))
+ .header("X-Forwarded-Proto", "http")
+ .header("X-Forwarded-Port", Integer.toString(gatewayPort))
+ .header("X-Forwarded-Context", "/gateway/cluster")
+ .header("X-Forwarded-Server", gatewayHostName)
+ .header("X-Forwarded-For", Matchers.isOneOf(gatewayHostName, gatewayAddrName))
+ .pathInfo(path)
+ .queryParam("user.name", username)
+ .respond()
+ .status(HttpStatus.SC_OK)
+ .content(driver.getResourceBytes(resourceName))
+ .contentType(ContentType.JSON.toString());
+
+ Response response = given()
+ .auth().preemptive().basic(username, password)
+ .header("X-XSRF-Header", "jksdhfkhdsf")
+ .header("Accept", ContentType.JSON.toString())
+ .expect()
+// .log().all()
+ .statusCode(HttpStatus.SC_OK)
+ .contentType( ContentType.JSON.toString() )
+ .when().get( gatewayPath );
+
+
+ String link = response.getBody().jsonPath().getString("spouts[0].errorWorkerLogLink");
+ MatcherAssert.assertThat(link, anyOf(
+ startsWith("http://" + gatewayHostName + ":" + gatewayPort + "/"),
+ startsWith("http://" + gatewayAddrName + ":" + gatewayPort + "/")));
+ MatcherAssert.assertThat( link, containsString("/storm/logviewer") );
+ driver.assertComplete();
+
+ }
+
+
+ @Test
+ public void testXForwardHeadersRewrite() throws Exception {
+ String username = "hdfs";
+ String password = "hdfs-password";
+ String host = "whatsinaname";
+ String port = "8889";
+ String scheme = "https";
+
+ InetSocketAddress gatewayAddress = driver.gateway.getAddresses()[0];
+ String gatewayHostName = gatewayAddress.getHostName();
+
+ //Test rewriting of body with X-Forwarded headers (using storm)
+ String resourceName = "storm/topology-id.json";
+ String path = "/api/v1/topology/WordCount-1-1424792039";
+ String gatewayPath = driver.getUrl( "STORM" ) + path;
+ driver.getMock("STORM")
+ .expect()
+ .method("GET")
+ .header("X-Forwarded-Host", host)
+ .header("X-Forwarded-Proto", scheme)
+ .header("X-Forwarded-Port", port)
+ .header("X-Forwarded-Context", "/gateway/cluster")
+ .header("X-Forwarded-Server", gatewayHostName)
+ .header("X-Forwarded-For", Matchers.containsString("what, boo"))
+ .pathInfo(path)
+ .queryParam("user.name", username)
+ .respond()
+ .status(HttpStatus.SC_OK)
+ .content(driver.getResourceBytes(resourceName))
+ .contentType(ContentType.JSON.toString());
+
+ Response response = given()
+ .auth().preemptive().basic(username, password)
+ .header("X-XSRF-Header", "jksdhfkhdsf")
+ .header("Accept", ContentType.JSON.toString())
+ .header("X-Forwarded-Host", host)
+ .header("X-Forwarded-Proto", scheme)
+ .header("X-Forwarded-Port", port)
+ .header("X-Forwarded-Server", "what")
+ .header("X-Forwarded-For", "what, boo")
+ .expect()
+ .statusCode(HttpStatus.SC_OK)
+ .contentType(ContentType.JSON.toString())
+ .when().get(gatewayPath);
+
+ String link = response.getBody().jsonPath().getString("spouts[0].errorWorkerLogLink");
+ MatcherAssert.assertThat(link, is(
+ startsWith(scheme + "://" + host + ":" + port + "/")));
+ MatcherAssert.assertThat( link, containsString("/storm/logviewer") );
+
+ driver.assertComplete();
+
+ resourceName = "storm/topology-component-id.json";
+ path = "/api/v1/topology/WordCount-1-1424792039/component/spout";
+ gatewayPath = driver.getUrl( "STORM" ) + path;
+ driver.getMock("STORM")
+ .expect()
+ .method("GET")
+ .header("X-Forwarded-Host", host)
+ .header("X-Forwarded-Proto", scheme)
+ .header("X-Forwarded-Port", port)
+ .header("X-Forwarded-Context", "/gateway/cluster")
+ .header("X-Forwarded-Server", gatewayHostName)
+ .header("X-Forwarded-For", Matchers.containsString("what, boo"))
+ .pathInfo(path)
+ .queryParam("user.name", username)
+ .respond()
+ .status(HttpStatus.SC_OK)
+ .content(driver.getResourceBytes(resourceName))
+ .contentType(ContentType.JSON.toString());
+
+ response = given()
+ .auth().preemptive().basic(username, password)
+ .header("X-XSRF-Header", "jksdhfkhdsf")
+ .header("Accept", ContentType.JSON.toString())
+ .header("X-Forwarded-Host", host)
+ .header("X-Forwarded-Proto", scheme)
+ .header("X-Forwarded-Port", port)
+ .header("X-Forwarded-Server", "what")
+ .header("X-Forwarded-For", "what, boo")
+ .expect()
+// .log().all()
+ .statusCode(HttpStatus.SC_OK)
+ .contentType( ContentType.JSON.toString() )
+ .when().get( gatewayPath );
+
+
+ link = response.getBody().jsonPath().getString("executorStats[0].workerLogLink");
+ MatcherAssert.assertThat(link, is(
+ startsWith(scheme + "://" + host + ":" + port + "/")));
+ MatcherAssert.assertThat( link, containsString("/storm/logviewer") );
+ driver.assertComplete();
+
+ //Test header rewrite using webhdfs
+ String root = "/tmp/GatewayBasicFuncTest/testBasicOutboundHeaderUseCase";
+
+ driver.getMock( "WEBHDFS" )
+ .expect()
+ .method( "PUT" )
+ .pathInfo("/v1" + root + "/dir/file")
+ .header("Host", driver.getRealAddr("WEBHDFS"))
+ .header("X-Forwarded-Host", host)
+ .header("X-Forwarded-Proto", scheme)
+ .header("X-Forwarded-Port", port)
+ .header("X-Forwarded-Context", "/gateway/cluster")
+ .header("X-Forwarded-Server", gatewayHostName)
+ .header("X-Forwarded-For", Matchers.containsString("what, boo"))
+ .queryParam("op", "CREATE")
+ .queryParam( "user.name", username )
+ .respond()
+ .status( HttpStatus.SC_TEMPORARY_REDIRECT )
+ .header("Location", driver.getRealUrl("DATANODE") + "/v1" + root + "/dir/file?op=CREATE&user.name=hdfs");
+ response = given()
+ //.log().all()
+ .auth().preemptive().basic(username, password)
+ .header("X-XSRF-Header", "jksdhfkhdsf")
+ .header("X-Forwarded-Host", host)
+ .header("X-Forwarded-Proto", scheme)
+ .header("X-Forwarded-Port", port)
+ .header("X-Forwarded-Server", "what")
+ .header("X-Forwarded-For", "what, boo")
+ .queryParam( "op", "CREATE" )
+ .expect()
+ //.log().ifError()
+ .statusCode( HttpStatus.SC_TEMPORARY_REDIRECT )
+ .when().put( driver.getUrl("WEBHDFS") + "/v1" + root + "/dir/file" );
+ String location = response.getHeader( "Location" );
+ //System.out.println( location );
+ log.debug( "Redirect location: " + response.getHeader( "Location" ) );
+ if( driver.isUseGateway() ) {
+ MatcherAssert.assertThat( location, is(startsWith(scheme + "://" + host + ":" + port + "/")));
+ MatcherAssert.assertThat( location, containsString( "?_=" ) );
+ }
+ MatcherAssert.assertThat(location, not(containsString("host=")));
+ MatcherAssert.assertThat(location, not(containsString("port=")));
+ }
}
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java
index baf206b..3f9fde5 100644
--- a/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java
+++ b/gateway-test/src/test/java/org/apache/hadoop/gateway/GatewayTestConfig.java
@@ -36,6 +36,7 @@ public class GatewayTestConfig implements GatewayConfig {
private boolean kerberosDebugEnabled = false;
private String kerberosLoginConfig = "/etc/knox/conf/krb5JAASLogin.conf";
private String frontendUrl = null;
+ private boolean xForwardedEnabled = true;
public void setGatewayHomeDir( String gatewayHomeDir ) {
this.gatewayHomeDir = gatewayHomeDir;
@@ -239,4 +240,13 @@ public class GatewayTestConfig implements GatewayConfig {
public String getGatewayServicesDir() {
return gatewayHomeDir + "/data/services";
}
+
+ @Override
+ public boolean isXForwardedEnabled() {
+ return xForwardedEnabled;
+ }
+
+ public void setXForwardedEnabled(boolean enabled) {
+ xForwardedEnabled = enabled;
+ }
}
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/gateway-test/src/test/java/org/apache/hadoop/gateway/deploy/DeploymentFactoryFuncTest.java
----------------------------------------------------------------------
diff --git a/gateway-test/src/test/java/org/apache/hadoop/gateway/deploy/DeploymentFactoryFuncTest.java b/gateway-test/src/test/java/org/apache/hadoop/gateway/deploy/DeploymentFactoryFuncTest.java
index 99fd5ab..9be668c 100644
--- a/gateway-test/src/test/java/org/apache/hadoop/gateway/deploy/DeploymentFactoryFuncTest.java
+++ b/gateway-test/src/test/java/org/apache/hadoop/gateway/deploy/DeploymentFactoryFuncTest.java
@@ -120,11 +120,16 @@ public class DeploymentFactoryFuncTest {
Document gateway = parse( war.get( "WEB-INF/gateway.xml" ).getAsset().openStream() );
//dump( gateway );
- assertThat( gateway, hasXPath( "/gateway/resource[1]/filter[1]/role", equalTo( "authentication" ) ) );
- assertThat( gateway, hasXPath( "/gateway/resource[1]/filter[1]/name", equalTo( "generic" ) ) );
- assertThat( gateway, hasXPath( "/gateway/resource[1]/filter[1]/class", equalTo( "org.opensource.ExistingFilter" ) ) );
- assertThat( gateway, hasXPath( "/gateway/resource[1]/filter[1]/param[1]/name", equalTo( "test-param-name" ) ) );
- assertThat( gateway, hasXPath( "/gateway/resource[1]/filter[1]/param[1]/value", equalTo( "test-param-value" ) ) );
+ //by default the first filter will be the X-Forwarded header filter
+ assertThat( gateway, hasXPath( "/gateway/resource[1]/filter[1]/role", equalTo( "xforwardedheaders" ) ) );
+ assertThat( gateway, hasXPath( "/gateway/resource[1]/filter[1]/name", equalTo( "XForwardedHeaderFilter" ) ) );
+ assertThat( gateway, hasXPath( "/gateway/resource[1]/filter[1]/class", equalTo( "org.apache.hadoop.gateway.filter.XForwardedHeaderFilter" ) ) );
+
+ assertThat( gateway, hasXPath( "/gateway/resource[1]/filter[2]/role", equalTo( "authentication" ) ) );
+ assertThat( gateway, hasXPath( "/gateway/resource[1]/filter[2]/name", equalTo( "generic" ) ) );
+ assertThat( gateway, hasXPath( "/gateway/resource[1]/filter[2]/class", equalTo( "org.opensource.ExistingFilter" ) ) );
+ assertThat( gateway, hasXPath( "/gateway/resource[1]/filter[2]/param[1]/name", equalTo( "test-param-name" ) ) );
+ assertThat( gateway, hasXPath( "/gateway/resource[1]/filter[2]/param[1]/value", equalTo( "test-param-value" ) ) );
}
@Test
@@ -185,6 +190,8 @@ public class DeploymentFactoryFuncTest {
@Test
public void testSimpleTopology() throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
GatewayConfig config = new GatewayTestConfig();
+ //Testing without x-forwarded headers filter
+ ((GatewayTestConfig)config).setXForwardedEnabled(false);
File targetDir = new File( System.getProperty( "user.dir" ), "target" );
File gatewayDir = new File( targetDir, "gateway-home-" + UUID.randomUUID() );
gatewayDir.mkdirs();
http://git-wip-us.apache.org/repos/asf/knox/blob/dd16033d/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index f656816..4c1f236 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,6 +47,7 @@
<module>gateway-spi</module>
<module>gateway-server</module>
<module>gateway-server-launcher</module>
+ <module>gateway-server-xforwarded-filter</module>
<module>gateway-provider-rewrite</module>
<module>gateway-provider-rewrite-func-hostmap-static</module>
<module>gateway-provider-rewrite-func-service-registry</module>
@@ -537,6 +538,11 @@
</dependency>
<dependency>
<groupId>${gateway-group}</groupId>
+ <artifactId>gateway-server-xforwarded-filter</artifactId>
+ <version>${gateway-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${gateway-group}</groupId>
<artifactId>knox-cli-launcher</artifactId>
<version>${gateway-version}</version>
</dependency>