You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by km...@apache.org on 2015/09/23 10:00:42 UTC

knox git commit: KNOX-599: Template with {**} in queries are expanded with =null for query params without a value

Repository: knox
Updated Branches:
  refs/heads/master e20e5b06e -> f165a6d19


KNOX-599: Template with {**} in queries are expanded with =null for query params without a value


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

Branch: refs/heads/master
Commit: f165a6d1901bb5023faba2d61eb5d256012b34eb
Parents: e20e5b0
Author: Kevin Minder <ke...@hortonworks.com>
Authored: Wed Sep 23 04:00:33 2015 -0400
Committer: Kevin Minder <ke...@hortonworks.com>
Committed: Wed Sep 23 04:00:33 2015 -0400

----------------------------------------------------------------------
 CHANGES                                         |   1 +
 .../pom.xml                                     |   4 +
 ...entityAsserterHttpServletRequestWrapper.java |  10 +-
 .../apache/hadoop/gateway/util/HttpUtils.java   |  99 +++++++++
 .../hadoop/gateway/util/HttpUtilsTest.java      | 203 +++++++++++++++++++
 .../gateway/util/urltemplate/Expander.java      |  21 +-
 .../gateway/util/urltemplate/ExpanderTest.java  |  54 +++++
 .../gateway/util/urltemplate/MatcherTest.java   |  25 +++
 .../gateway/util/urltemplate/ParserTest.java    |  19 ++
 9 files changed, 423 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/knox/blob/f165a6d1/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index c91d83d..7b33de1 100644
--- a/CHANGES
+++ b/CHANGES
@@ -21,6 +21,7 @@ Release Notes - Apache Knox - Version 0.7.0
     * [KNOX-554] - Fixed support for gateway.path change + added support for X-Forward-* headers in admin topology API.
     * [KNOX-581] - Hive dispatch not propagating effective principal name
     * [KNOX-598] - Concurrent JDBC clients via KNOX to Kerberized HiveServer2 causes HTTP 401 error (due to Kerberos Replay attack error)
+    * [KNOX-599] - Template with {**} in queries are expanded with =null for query params without a value
 
 ------------------------------------------------------------------------------
 Release Notes - Apache Knox - Version 0.6.0

http://git-wip-us.apache.org/repos/asf/knox/blob/f165a6d1/gateway-provider-identity-assertion-common/pom.xml
----------------------------------------------------------------------
diff --git a/gateway-provider-identity-assertion-common/pom.xml b/gateway-provider-identity-assertion-common/pom.xml
index 0926e20..5a02373 100644
--- a/gateway-provider-identity-assertion-common/pom.xml
+++ b/gateway-provider-identity-assertion-common/pom.xml
@@ -44,6 +44,10 @@
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
         </dependency>
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>${gateway-group}</groupId>

http://git-wip-us.apache.org/repos/asf/knox/blob/f165a6d1/gateway-provider-identity-assertion-common/src/main/java/org/apache/hadoop/gateway/identityasserter/common/filter/IdentityAsserterHttpServletRequestWrapper.java
----------------------------------------------------------------------
diff --git a/gateway-provider-identity-assertion-common/src/main/java/org/apache/hadoop/gateway/identityasserter/common/filter/IdentityAsserterHttpServletRequestWrapper.java b/gateway-provider-identity-assertion-common/src/main/java/org/apache/hadoop/gateway/identityasserter/common/filter/IdentityAsserterHttpServletRequestWrapper.java
index 50e9e60..d32cc58 100644
--- a/gateway-provider-identity-assertion-common/src/main/java/org/apache/hadoop/gateway/identityasserter/common/filter/IdentityAsserterHttpServletRequestWrapper.java
+++ b/gateway-provider-identity-assertion-common/src/main/java/org/apache/hadoop/gateway/identityasserter/common/filter/IdentityAsserterHttpServletRequestWrapper.java
@@ -21,6 +21,7 @@ import org.apache.commons.io.IOUtils;
 import org.apache.hadoop.gateway.SpiGatewayMessages;
 import org.apache.hadoop.gateway.config.GatewayConfig;
 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
+import org.apache.hadoop.gateway.util.HttpUtils;
 
 import javax.servlet.ServletInputStream;
 import javax.servlet.http.HttpServletRequest;
@@ -92,7 +93,7 @@ public class IdentityAsserterHttpServletRequestWrapper extends HttpServletReques
     Map<String, String[]> params = null;
     if (getMethod().equals("GET")) {
       if (qString != null && qString.length() > 0) {
-        params = parseQueryString(qString);
+        params = HttpUtils.parseQueryString( qString );
       }
       else {
         params = new HashMap<String, String[]>();
@@ -103,7 +104,7 @@ public class IdentityAsserterHttpServletRequestWrapper extends HttpServletReques
         return null;
       }
       else {
-        params = parseQueryString(qString);
+        params = HttpUtils.parseQueryString( qString );
       }
     }  
     return params;
@@ -213,11 +214,6 @@ public class IdentityAsserterHttpServletRequestWrapper extends HttpServletReques
     return sb.toString();
   }
 
-  @SuppressWarnings({ "deprecation", "unchecked" })
-  private static Map<String,String[]> parseQueryString( String queryString ) {
-    return javax.servlet.http.HttpUtils.parseQueryString( queryString );
-  }
-  
   private class ServletInputStreamWrapper extends ServletInputStream {
 
     private InputStream stream;

http://git-wip-us.apache.org/repos/asf/knox/blob/f165a6d1/gateway-util-common/src/main/java/org/apache/hadoop/gateway/util/HttpUtils.java
----------------------------------------------------------------------
diff --git a/gateway-util-common/src/main/java/org/apache/hadoop/gateway/util/HttpUtils.java b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/util/HttpUtils.java
new file mode 100644
index 0000000..6a80a41
--- /dev/null
+++ b/gateway-util-common/src/main/java/org/apache/hadoop/gateway/util/HttpUtils.java
@@ -0,0 +1,99 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.util;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+public class HttpUtils {
+
+  public static Map<String,String[]> parseQueryString( String queryString ) {
+    Map<String,String[]> map = new HashMap<String,String[]>();
+    if( queryString != null && !queryString.isEmpty() ) {
+      StringTokenizer parser = new StringTokenizer( queryString, "&?;=", true );
+      String name = null;
+      String value = null;
+      while( parser.hasMoreTokens() ) {
+        String token = parser.nextToken();
+        String ttoken = token.trim();
+        if( ttoken.length() == 1 ) {
+          char c = ttoken.charAt( 0 );
+          switch( c ) {
+            case '&':
+            case '?':
+            case ';':
+              addQueryStringParam( map, name, value );
+              name = null;
+              value = null;
+              continue;
+            case '=':
+              if( name == null ) {
+                name = "";
+                value = "";
+              } else if( name.isEmpty() ) {
+                addQueryStringParam( map, name, value );
+                name = "";
+                value = "";
+              } else {
+                value = "";
+              }
+              continue;
+          }
+        }
+        if( name == null ) {
+          name = token;
+        } else {
+          value = token;
+        }
+      } // while
+      if( name != null ) {
+        addQueryStringParam( map, name, value );
+      }
+    }
+    return map;
+  }
+
+  private final static String urlDecodeUtf8( String s ) {
+    if( s != null ) {
+      try {
+        s = URLDecoder.decode( s, "UTF-8" );
+      } catch( UnsupportedEncodingException e ) {
+        // Ignore it.
+      }
+    }
+    return s;
+  }
+
+  final static void addQueryStringParam( final Map<String,String[]> map, String name, String value ) {
+    name = urlDecodeUtf8( name );
+    value = urlDecodeUtf8( value );
+    String[] values = map.get( name );
+    if( values == null ) {
+      values = new String[]{ value };
+    } else {
+      values = Arrays.copyOf( values, values.length + 1 );
+      values[ values.length-1 ] = value;
+    }
+    map.put( name, values );
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/f165a6d1/gateway-util-common/src/test/java/org/apache/hadoop/gateway/util/HttpUtilsTest.java
----------------------------------------------------------------------
diff --git a/gateway-util-common/src/test/java/org/apache/hadoop/gateway/util/HttpUtilsTest.java b/gateway-util-common/src/test/java/org/apache/hadoop/gateway/util/HttpUtilsTest.java
new file mode 100644
index 0000000..0e86a3b
--- /dev/null
+++ b/gateway-util-common/src/test/java/org/apache/hadoop/gateway/util/HttpUtilsTest.java
@@ -0,0 +1,203 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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.util;
+
+import org.junit.Test;
+
+import java.util.Map;
+
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+
+public class HttpUtilsTest {
+
+  @Test
+  public void testParseQueryString_BugKnox599() {
+
+    Map<String,String[]> map;
+
+    map = HttpUtils.parseQueryString( null );
+    assertThat( map, notNullValue() );
+    assertThat( map.isEmpty(), is( true ) );
+
+    map = HttpUtils.parseQueryString( "" );
+    assertThat( map, notNullValue() );
+    assertThat( map.isEmpty(), is( true ) );
+
+    map = HttpUtils.parseQueryString( "test-name=test-value" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( "test-name" ), is( true ) );
+    assertThat( map.get( "test-name" ).length, is( 1 ) );
+    assertThat( map.get( "test-name" )[0], is( "test-value" ) );
+
+    map = HttpUtils.parseQueryString( "test-name-one=test-value-one&test-name-two=two=test-value-two" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 2 ) );
+    assertThat( map.containsKey( "test-name-one" ), is( true ) );
+    assertThat( map.get( "test-name-one" ).length, is( 1 ) );
+    assertThat( map.get( "test-name-one" )[0], is( "test-value-one" ) );
+    assertThat( map.containsKey( "test-name-two" ), is( true ) );
+    assertThat( map.get( "test-name-two" ).length, is( 1 ) );
+    assertThat( map.get( "test-name-two" )[0], is( "test-value-two" ) );
+
+    map = HttpUtils.parseQueryString( "test-name-one=test-value-one?test-name-two=two=test-value-two" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 2 ) );
+    assertThat( map.containsKey( "test-name-one" ), is( true ) );
+    assertThat( map.get( "test-name-one" ).length, is( 1 ) );
+    assertThat( map.get( "test-name-one" )[0], is( "test-value-one" ) );
+    assertThat( map.containsKey( "test-name-two" ), is( true ) );
+    assertThat( map.get( "test-name-two" ).length, is( 1 ) );
+    assertThat( map.get( "test-name-two" )[0], is( "test-value-two" ) );
+
+    map = HttpUtils.parseQueryString( "test-name-one=test-value-one;test-name-two=two=test-value-two" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 2 ) );
+    assertThat( map.containsKey( "test-name-one" ), is( true ) );
+    assertThat( map.get( "test-name-one" ).length, is( 1 ) );
+    assertThat( map.get( "test-name-one" )[0], is( "test-value-one" ) );
+    assertThat( map.containsKey( "test-name-two" ), is( true ) );
+    assertThat( map.get( "test-name-two" ).length, is( 1 ) );
+    assertThat( map.get( "test-name-two" )[0], is( "test-value-two" ) );
+
+    map = HttpUtils.parseQueryString( "test-name=test-value-one?test-name=test-value-two" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( "test-name" ), is( true ) );
+    assertThat( map.get( "test-name" ).length, is( 2 ) );
+    assertThat( map.get( "test-name" )[0], is( "test-value-one" ) );
+    assertThat( map.get( "test-name" )[1], is( "test-value-two" ) );
+
+    map = HttpUtils.parseQueryString( "test-name=test-value-one&test-name=test-value-two" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( "test-name" ), is( true ) );
+    assertThat( map.get( "test-name" ).length, is( 2 ) );
+    assertThat( map.get( "test-name" )[0], is( "test-value-one" ) );
+    assertThat( map.get( "test-name" )[1], is( "test-value-two" ) );
+
+    map = HttpUtils.parseQueryString( "test-name=test-value-one;test-name=test-value-two" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( "test-name" ), is( true ) );
+    assertThat( map.get( "test-name" ).length, is( 2 ) );
+    assertThat( map.get( "test-name" )[0], is( "test-value-one" ) );
+    assertThat( map.get( "test-name" )[1], is( "test-value-two" ) );
+
+    map = HttpUtils.parseQueryString( "test-name=" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( "test-name" ), is( true ) );
+    assertThat( map.get( "test-name" ).length, is( 1 ) );
+    assertThat( map.get( "test-name" )[0], is( "" ) );
+
+    map = HttpUtils.parseQueryString( "test-name" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( "test-name" ), is( true ) );
+    assertThat( map.get( "test-name" ).length, is( 1 ) );
+    assertThat( map.get( "test-name" )[0], nullValue() );
+
+    map = HttpUtils.parseQueryString( "=test-value" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( "" ), is( true ) );
+    assertThat( map.get( "" ).length, is( 1 ) );
+    assertThat( map.get( "" )[0], is( "test-value" ) );
+
+    map = HttpUtils.parseQueryString( "=" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( "" ), is( true ) );
+    assertThat( map.get( "" ).length, is( 1 ) );
+    assertThat( map.get( "" )[0], is( "" ) );
+
+    map = HttpUtils.parseQueryString( "==" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( "" ), is( true ) );
+    assertThat( map.get( "" ).length, is( 2 ) );
+    assertThat( map.get( "" )[0], is( "" ) );
+    assertThat( map.get( "" )[1], is( "" ) );
+
+    map = HttpUtils.parseQueryString( "&" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( null ), is( true ) );
+    assertThat( map.get( null ).length, is( 1 ) );
+    assertThat( map.get( null )[0], nullValue() );
+
+    map = HttpUtils.parseQueryString( "?" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( null ), is( true ) );
+    assertThat( map.get( null ).length, is( 1 ) );
+    assertThat( map.get( null )[0], nullValue() );
+
+    map = HttpUtils.parseQueryString( ";" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( null ), is( true ) );
+    assertThat( map.get( null ).length, is( 1 ) );
+    assertThat( map.get( null )[0], nullValue() );
+
+    map = HttpUtils.parseQueryString( "&=" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 2 ) );
+    assertThat( map.containsKey( "" ), is( true ) );
+    assertThat( map.get( "" ).length, is( 1 ) );
+    assertThat( map.get( "" )[0], is( "" ) );
+    assertThat( map.containsKey( null ), is( true ) );
+    assertThat( map.get( null ).length, is( 1 ) );
+    assertThat( map.get( null )[0], nullValue() );
+
+    map = HttpUtils.parseQueryString( "=&" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( "" ), is( true ) );
+    assertThat( map.get( "" ).length, is( 1 ) );
+    assertThat( map.get( "" )[0], is( "" ) );
+
+    map = HttpUtils.parseQueryString( "&&" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( null ), is( true ) );
+    assertThat( map.get( null ).length, is( 2 ) );
+    assertThat( map.get( null )[0], nullValue() );
+    assertThat( map.get( null )[1], nullValue() );
+
+    map = HttpUtils.parseQueryString( "test+name=test+value" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( "test name" ), is( true ) );
+    assertThat( map.get( "test name" ).length, is( 1 ) );
+    assertThat( map.get( "test name" )[0], is( "test value" ) );
+
+    map = HttpUtils.parseQueryString( "test%26name=test%3Dvalue" );
+    assertThat( map, notNullValue() );
+    assertThat( map.size(), is( 1 ) );
+    assertThat( map.containsKey( "test&name" ), is( true ) );
+    assertThat( map.get( "test&name" ).length, is( 1 ) );
+    assertThat( map.get( "test&name" )[0], is( "test=value" ) );
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/f165a6d1/gateway-util-urltemplate/src/main/java/org/apache/hadoop/gateway/util/urltemplate/Expander.java
----------------------------------------------------------------------
diff --git a/gateway-util-urltemplate/src/main/java/org/apache/hadoop/gateway/util/urltemplate/Expander.java b/gateway-util-urltemplate/src/main/java/org/apache/hadoop/gateway/util/urltemplate/Expander.java
index 4337f7a..ad6dc82 100644
--- a/gateway-util-urltemplate/src/main/java/org/apache/hadoop/gateway/util/urltemplate/Expander.java
+++ b/gateway-util-urltemplate/src/main/java/org/apache/hadoop/gateway/util/urltemplate/Expander.java
@@ -213,8 +213,10 @@ public class Expander {
               builder.append( "&" );
             }
             builder.append( name );
-            builder.append( "=" );
-            builder.append( value );
+            if( value != null ) {
+              builder.append( "=" );
+              builder.append( value );
+            }
           }
         }
       }
@@ -222,6 +224,7 @@ public class Expander {
   }
 
   private static void expandQueryValues( Query segment, String queryName, List<String> values, StringBuilder builder ) {
+    String value;
     if( values == null || values.size() == 0 ) {
       builder.append( queryName );
     } else {
@@ -232,13 +235,19 @@ public class Expander {
             builder.append( "&" );
           }
           builder.append( queryName );
-          builder.append( "=" );
-          builder.append( values.get( i ) );
+          value = values.get( i );
+          if( value != null ) {
+            builder.append( "=" );
+            builder.append( value );
+          }
         }
       } else {
         builder.append( queryName );
-        builder.append( "=" );
-        builder.append( values.get( 0 ) );
+        value = values.get( 0 );
+        if( value != null ) {
+          builder.append( "=" );
+          builder.append( value );
+        }
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/knox/blob/f165a6d1/gateway-util-urltemplate/src/test/java/org/apache/hadoop/gateway/util/urltemplate/ExpanderTest.java
----------------------------------------------------------------------
diff --git a/gateway-util-urltemplate/src/test/java/org/apache/hadoop/gateway/util/urltemplate/ExpanderTest.java b/gateway-util-urltemplate/src/test/java/org/apache/hadoop/gateway/util/urltemplate/ExpanderTest.java
index 5b47b55..4b32979 100644
--- a/gateway-util-urltemplate/src/test/java/org/apache/hadoop/gateway/util/urltemplate/ExpanderTest.java
+++ b/gateway-util-urltemplate/src/test/java/org/apache/hadoop/gateway/util/urltemplate/ExpanderTest.java
@@ -335,4 +335,58 @@ public class ExpanderTest {
         equalTo( "schemeA://host/pathA/pathB?server=host&query=queryA&query=queryB&host=hostA&extra=extraA" ) );
   }
 
+
+  @Test
+  public void testBugKnox599() throws Exception {
+    String text;
+    Template template;
+    MockParams params;
+    URI expanded;
+
+    text = "{scheme}://{host}:{port}/{path=**}?{**}";
+    template = Parser.parse( text );
+    params = new MockParams();
+    params.addValue( "scheme", "http" );
+    params.addValue( "host", "hortonworks.com" );
+    params.addValue( "port", "8888" );
+    params.addValue( "path", "top" );
+    params.addValue( "path", "mid" );
+    params.addValue( "path", "bot" );
+    params.addValue( "path", "file" );
+    params.addValue( "name", "value" );
+    params.addValue( "flag", "" );
+    expanded = Expander.expand( template, params, null );
+    assertThat( expanded.toString(), equalTo( "http://hortonworks.com:8888/top/mid/bot/file?flag=&name=value" ) ) ;
+
+    text = "{scheme}://{host}:{port}/{path=**}?{**}";
+    template = Parser.parse( text );
+    params = new MockParams();
+    params.addValue( "scheme", "http" );
+    params.addValue( "host", "hortonworks.com" );
+    params.addValue( "port", "8888" );
+    params.addValue( "path", "top" );
+    params.addValue( "path", "mid" );
+    params.addValue( "path", "bot" );
+    params.addValue( "path", "file" );
+    params.addValue( "name", "value" );
+    params.addValue( "flag", null );
+    expanded = Expander.expand( template, params, null );
+    assertThat( expanded.toString(), equalTo( "http://hortonworks.com:8888/top/mid/bot/file?flag&name=value" ) ) ;
+
+    text = "{scheme}://{host}:{port}/{path=**}?{name=*}&{**}";
+    template = Parser.parse( text );
+    params = new MockParams();
+    params.addValue( "scheme", "http" );
+    params.addValue( "host", "hortonworks.com" );
+    params.addValue( "port", "8888" );
+    params.addValue( "path", "top" );
+    params.addValue( "path", "mid" );
+    params.addValue( "path", "bot" );
+    params.addValue( "path", "file" );
+    params.addValue( "name", null );
+    params.addValue( "flag", "" );
+    expanded = Expander.expand( template, params, null );
+    assertThat( expanded.toString(), equalTo( "http://hortonworks.com:8888/top/mid/bot/file?name&flag=" ) ) ;
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/knox/blob/f165a6d1/gateway-util-urltemplate/src/test/java/org/apache/hadoop/gateway/util/urltemplate/MatcherTest.java
----------------------------------------------------------------------
diff --git a/gateway-util-urltemplate/src/test/java/org/apache/hadoop/gateway/util/urltemplate/MatcherTest.java b/gateway-util-urltemplate/src/test/java/org/apache/hadoop/gateway/util/urltemplate/MatcherTest.java
index 9cacb55..b172f99 100644
--- a/gateway-util-urltemplate/src/test/java/org/apache/hadoop/gateway/util/urltemplate/MatcherTest.java
+++ b/gateway-util-urltemplate/src/test/java/org/apache/hadoop/gateway/util/urltemplate/MatcherTest.java
@@ -24,6 +24,7 @@ import org.hamcrest.CoreMatchers;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
+import java.net.URI;
 import java.net.URISyntaxException;
 
 import static org.hamcrest.CoreMatchers.equalTo;
@@ -944,4 +945,28 @@ public class MatcherTest {
     assertThat( params.getNames().size(), equalTo( 0 ) );
   }
 
+  @Test
+  public void testBugKnox599() throws Exception {
+    Template template;
+    Template input;
+    Matcher<String> matcher;
+    Matcher<?>.Match match;
+
+    matcher = new Matcher<String>();
+    template = Parser.parse( "*://*:*/**/webhdfs/v1/{path=**}?{**}" );
+    matcher.add( template, "test-value" );
+
+    input = Parser.parse( "http://kminder-os-u14-23-knoxha-150922-1352-2.novalocal:1022/gateway/sandbox/webhdfs/v1/user/hrt_qa/knox-ha/knox_webhdfs_client_dir/test_file?op=CREATE&delegation=XXX&namenoderpcaddress=nameservice&createflag=&createparent=true&overwrite=true" );
+
+    match = matcher.match( input );
+    assertThat( match, notNullValue() );
+    assertThat( (String)match.getValue(), is( "test-value" ) );
+
+    template = Parser.parse( "http://host:42/root/webhdfs/v1/{path=**}?{**}" );
+    URI expanded = Expander.expand( template, match.getParams(), null );
+    assertThat(
+        expanded.toString(),
+        equalTo( "http://host:42/root/webhdfs/v1/user/hrt_qa/knox-ha/knox_webhdfs_client_dir/test_file?delegation=XXX&op=CREATE&namenoderpcaddress=nameservice&createflag=&overwrite=true&createparent=true" ) ) ;
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/knox/blob/f165a6d1/gateway-util-urltemplate/src/test/java/org/apache/hadoop/gateway/util/urltemplate/ParserTest.java
----------------------------------------------------------------------
diff --git a/gateway-util-urltemplate/src/test/java/org/apache/hadoop/gateway/util/urltemplate/ParserTest.java b/gateway-util-urltemplate/src/test/java/org/apache/hadoop/gateway/util/urltemplate/ParserTest.java
index eeabba9..8e14f1f 100644
--- a/gateway-util-urltemplate/src/test/java/org/apache/hadoop/gateway/util/urltemplate/ParserTest.java
+++ b/gateway-util-urltemplate/src/test/java/org/apache/hadoop/gateway/util/urltemplate/ParserTest.java
@@ -1261,4 +1261,23 @@ public class ParserTest {
     assertThat( output.getEffectivePattern(), is( "" ) );
   }
 
+  @Test
+  public void testBugKnox599() throws Exception {
+    Template template;
+    Template input;
+    Matcher<String> matcher;
+
+    matcher = new Matcher<String>();
+    template = Parser.parse( "*://*:*/**/webhdfs/v1/{path=**}?{**}" );
+    matcher.add( template, "test-value" );
+
+    input = Parser.parse( "http://kminder-os-u14-23-knoxha-150922-1352-2.novalocal:1022/gateway/sandbox/webhdfs/v1/user/hrt_qa/knox-ha/knox_webhdfs_client_dir/test_file?op=CREATE&delegation=XXX&namenoderpcaddress=nameservice&createflag=&createparent=true&overwrite=true" );
+
+    assertThat( input.getQuery().get( "createflag" ).getFirstValue().getPattern(), is( "" ) );
+
+    input = Parser.parse( "http://kminder-os-u14-23-knoxha-150922-1352-2.novalocal:1022/gateway/sandbox/webhdfs/v1/user/hrt_qa/knox-ha/knox_webhdfs_client_dir/test_file?op=CREATE&delegation=XXX&namenoderpcaddress=nameservice&createflag&createparent=true&overwrite=true" );
+
+    assertThat( input.getQuery().get( "createflag" ).getFirstValue().getPattern(), nullValue() );
+  }
+
 }