KNOX-2262 - Accessing hbase logs through knox exposes hbase endpoint url instead of routing through knox

Closes #278

Signed-off-by: Kevin Risden <krisden@apache.org>
diff --git a/gateway-service-definitions/src/main/resources/services/hbaseui/2.1.0/rewrite.xml b/gateway-service-definitions/src/main/resources/services/hbaseui/2.1.0/rewrite.xml
index f9d290d..ee8e9ac 100644
--- a/gateway-service-definitions/src/main/resources/services/hbaseui/2.1.0/rewrite.xml
+++ b/gateway-service-definitions/src/main/resources/services/hbaseui/2.1.0/rewrite.xml
@@ -174,6 +174,15 @@
   <rule dir="IN" name="HBASEUI/hbase/inbound/logs/files" pattern="*://*:*/**/hbase/webui/logs/{**}?{host}?{port}">
     <rewrite template="{$serviceScheme[HBASEUI]}://{$hostmap(host)}:{port}/logs/{**}"/>
   </rule>
+  <filter name="HBASEUI/hbase/outbound/logs/headers">
+    <content type="application/x-http-headers">
+      <apply path="Location" rule="HBASEUI/hbase/outbound/logs/headers-redirect"/>
+    </content>
+  </filter>
+  <rule dir="OUT" name="HBASEUI/hbase/outbound/logs/headers-redirect">
+    <match pattern="{$serviceScheme[HBASEUI]}://{host}:{port}/logs/{dir=**}/?{**}"/>
+    <rewrite template="{$frontend[url]}/hbase/webui/logs/{dir=**}/?host={$inboundurl[host]}?port={$inboundurl[port]}"/>
+  </rule>
   <rule dir="OUT" name="HBASEUI/hbase/outbound/logs" pattern="/logs">
     <rewrite template="{$frontend[url]}/hbase/webui/logs?host={$inboundurl[host]}?port={$inboundurl[port]}"/>
   </rule>
diff --git a/gateway-service-definitions/src/main/resources/services/hbaseui/2.1.0/service.xml b/gateway-service-definitions/src/main/resources/services/hbaseui/2.1.0/service.xml
index 1063f33..d23bc6f 100644
--- a/gateway-service-definitions/src/main/resources/services/hbaseui/2.1.0/service.xml
+++ b/gateway-service-definitions/src/main/resources/services/hbaseui/2.1.0/service.xml
@@ -38,12 +38,14 @@
           <rewrite apply="HBASEUI/hbase/inbound/static" to="request.url"/>
         </route>
         <!-- Define explicit routes for endpoints shared across master/regionserver -->
-        <route path="/hbase/webui/logs?{**}">
-            <rewrite apply="HBASEUI/hbase/inbound/logs" to="request.url"/>
-            <rewrite apply="HBASEUI/hbase/outbound/logs" to="response.body"/>
+        <route path="/hbase/webui/logs?">
+           <rewrite apply="HBASEUI/hbase/inbound/logs" to="request.url"/>
+           <rewrite apply="HBASEUI/hbase/outbound/logs" to="response.body"/>
         </route>
-        <route path="/hbase/webui/logs/{**}">
-            <rewrite apply="HBASEUI/hbase/inbound/logs/files" to="request.url"/>
+        <route path="/hbase/webui/logs/**">
+          <rewrite apply="HBASEUI/hbase/outbound/logs" to="response.body"/>
+          <rewrite apply="HBASEUI/hbase/inbound/logs/files" to="request.url"/>
+          <rewrite apply="HBASEUI/hbase/outbound/logs/headers" to="response.headers"/>
         </route>
         <route path="/hbase/webui/logLevel?{**}">
           <rewrite apply="HBASEUI/hbase/inbound/loglevel" to="request.url"/>
@@ -95,5 +97,5 @@
           <rewrite apply="HBASEUI/hbase/outbound/regionserver/children" to="response.body"/>
         </route>
     </routes>
-    <dispatch classname="org.apache.knox.gateway.hbase.HBaseDispatch"/>
+    <dispatch classname="org.apache.knox.gateway.hbase.HBaseUIDispatch" ha-classname="org.apache.knox.gateway.hbase.HBaseUIHaDispatch"/>
 </service>
diff --git a/gateway-service-hbase/pom.xml b/gateway-service-hbase/pom.xml
index 40917b1..ecd456f 100644
--- a/gateway-service-hbase/pom.xml
+++ b/gateway-service-hbase/pom.xml
@@ -38,12 +38,18 @@
             <groupId>org.apache.knox</groupId>
             <artifactId>gateway-provider-rewrite</artifactId>
         </dependency>
-
+        <dependency>
+            <groupId>org.apache.knox</groupId>
+            <artifactId>gateway-provider-ha</artifactId>
+        </dependency>
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
         </dependency>
-
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.apache.knox</groupId>
             <artifactId>gateway-test-utils</artifactId>
diff --git a/gateway-service-hbase/src/main/java/org/apache/knox/gateway/hbase/HBaseDispatch.java b/gateway-service-hbase/src/main/java/org/apache/knox/gateway/hbase/HBaseDispatch.java
index 42fa1bb..cc47414 100644
--- a/gateway-service-hbase/src/main/java/org/apache/knox/gateway/hbase/HBaseDispatch.java
+++ b/gateway-service-hbase/src/main/java/org/apache/knox/gateway/hbase/HBaseDispatch.java
@@ -17,10 +17,7 @@
  */
 package org.apache.knox.gateway.hbase;
 
-import java.io.UnsupportedEncodingException;
 import java.net.URI;
-import java.net.URLDecoder;
-import java.nio.charset.StandardCharsets;
 import javax.servlet.http.HttpServletRequest;
 
 import org.apache.knox.gateway.dispatch.DefaultDispatch;
@@ -35,18 +32,7 @@
   // KNOX-709: HBase can't handle URL encoded paths.
   @Override
   public URI getDispatchUrl(HttpServletRequest request) {
-    String base = request.getRequestURI();
-    StringBuilder str = new StringBuilder();
-    try {
-      str.append( URLDecoder.decode( base, StandardCharsets.UTF_8.name() ) );
-    } catch( UnsupportedEncodingException e ) {
-      str.append( base );
-    } String query = request.getQueryString();
-    if ( query != null ) {
-      str.append( '?' );
-      str.append( query );
-    }
-    return URI.create( str.toString() );
+    return HBaseRequestUtil.decodeUrl(request);
   }
 }
 
diff --git a/gateway-service-hbase/src/main/java/org/apache/knox/gateway/hbase/HBaseRequestUtil.java b/gateway-service-hbase/src/main/java/org/apache/knox/gateway/hbase/HBaseRequestUtil.java
new file mode 100644
index 0000000..13552d5
--- /dev/null
+++ b/gateway-service-hbase/src/main/java/org/apache/knox/gateway/hbase/HBaseRequestUtil.java
@@ -0,0 +1,59 @@
+/*
+ * 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.knox.gateway.hbase;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.methods.RequestBuilder;
+
+class HBaseRequestUtil {
+  // partial workaround for KNOX-799
+  public static HttpUriRequest fixTrailingSlash(HttpUriRequest outboundRequest,
+                                                HttpServletRequest inboundRequest) {
+    // preserve trailing slash from inbound request in the outbound request
+    if (inboundRequest.getPathInfo().endsWith("/")) {
+      String[] split = outboundRequest.getURI().toString().split("\\?");
+      if (!split[0].endsWith("/")) {
+        outboundRequest = RequestBuilder.copy(outboundRequest)
+            .setUri(split[0] + "/" + (split.length == 2 ? "?" + split[1] : "")).build();
+      }
+    }
+    return outboundRequest;
+  }
+
+  public static URI decodeUrl(HttpServletRequest request) {
+    String base = request.getRequestURI();
+    StringBuilder str = new StringBuilder();
+    try {
+      str.append( URLDecoder.decode( base, StandardCharsets.UTF_8.name() ) );
+    } catch( UnsupportedEncodingException e ) {
+      str.append( base );
+    } String query = request.getQueryString();
+    if ( query != null ) {
+      str.append( '?' );
+      str.append( query );
+    }
+    return URI.create( str.toString() );
+  }
+}
diff --git a/gateway-service-hbase/src/main/java/org/apache/knox/gateway/hbase/HBaseUIDispatch.java b/gateway-service-hbase/src/main/java/org/apache/knox/gateway/hbase/HBaseUIDispatch.java
new file mode 100644
index 0000000..25acebe
--- /dev/null
+++ b/gateway-service-hbase/src/main/java/org/apache/knox/gateway/hbase/HBaseUIDispatch.java
@@ -0,0 +1,36 @@
+/*
+ * 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.knox.gateway.hbase;
+
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.knox.gateway.dispatch.DefaultDispatch;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public class HBaseUIDispatch extends DefaultDispatch {
+  @Override
+  protected void executeRequest(HttpUriRequest outboundRequest, HttpServletRequest inboundRequest,
+      HttpServletResponse outboundResponse) throws IOException {
+    // partial workaround for KNOX-799
+    outboundRequest = HBaseRequestUtil.fixTrailingSlash(outboundRequest, inboundRequest);
+    super.executeRequest(outboundRequest, inboundRequest, outboundResponse);
+  }
+}
+
diff --git a/gateway-service-hbase/src/main/java/org/apache/knox/gateway/hbase/HBaseUIHaDispatch.java b/gateway-service-hbase/src/main/java/org/apache/knox/gateway/hbase/HBaseUIHaDispatch.java
new file mode 100644
index 0000000..15996c0
--- /dev/null
+++ b/gateway-service-hbase/src/main/java/org/apache/knox/gateway/hbase/HBaseUIHaDispatch.java
@@ -0,0 +1,37 @@
+/*
+ * 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.knox.gateway.hbase;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.knox.gateway.ha.dispatch.DefaultHaDispatch;
+
+public class HBaseUIHaDispatch extends DefaultHaDispatch {
+  @Override
+  protected void executeRequest(HttpUriRequest outboundRequest, HttpServletRequest inboundRequest,
+      HttpServletResponse outboundResponse) throws IOException {
+    // partial workaround for KNOX-799
+    outboundRequest = HBaseRequestUtil.fixTrailingSlash(outboundRequest, inboundRequest);
+    super.executeRequest(outboundRequest, inboundRequest, outboundResponse);
+  }
+}
+