You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by mo...@apache.org on 2017/09/01 13:17:21 UTC

[23/64] [partial] knox git commit: KNOX-998 - Refactoring save 1

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormFilterReader.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormFilterReader.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormFilterReader.java
new file mode 100644
index 0000000..7d82633
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormFilterReader.java
@@ -0,0 +1,105 @@
+/**
+ * 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.filter.rewrite.impl.form;
+
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+import org.apache.knox.gateway.filter.rewrite.i18n.UrlRewriteMessages;
+import org.apache.knox.gateway.filter.rewrite.impl.UrlRewriteUtil;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringWriter;
+
+public class FormFilterReader extends Reader {
+
+  private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class );
+
+  private int offset;
+  private StringWriter writer;
+  private StringBuffer buffer;
+  private Reader reader;
+  private FormReader parser;
+  private FormWriter generator;
+  private UrlRewriteFilterContentDescriptor config;
+
+  public FormFilterReader( Reader reader, UrlRewriteFilterContentDescriptor config ) throws IOException {
+    this.reader = reader;
+    this.config = config;
+    parser = new FormReader( reader );
+    writer = new StringWriter();
+    buffer = writer.getBuffer();
+    offset = 0;
+    generator = new FormWriter( writer );
+  }
+
+  @Override
+  public int read( char[] destBuffer, int destOffset, int destCount ) throws IOException {
+    int count = 0;
+    int available = buffer.length() - offset;
+
+    if( available == 0 ) {
+      FormPair pair = parser.getNextPair();
+      if( pair == null ) {
+        count = -1;
+      } else {
+        processPair();
+        available = buffer.length() - offset;
+      }
+    }
+
+    if( available > 0 ) {
+      count = Math.min( destCount, available );
+      buffer.getChars( offset, offset+count, destBuffer, destOffset );
+      offset += count;
+      if( offset == buffer.length() ) {
+        offset = 0;
+        buffer.setLength( 0 );
+      }
+    }
+
+    return count;
+  }
+
+  private void processPair() throws IOException {
+    FormPair pair = parser.getCurrentPair();
+    String name = pair.getName();
+    String value = pair.getValue();
+    String rule = UrlRewriteUtil.pickFirstRuleWithEqualsIgnoreCasePathMatch( config, name );
+    try {
+      value = filterValue( name, pair.getValue(), rule );
+      pair.setValue( value );
+    } catch( Exception e ) {
+      LOG.failedToFilterValue( pair.getValue(), rule, e );
+      // Write original value.
+    }
+    generator.writePair( pair );
+  }
+
+  protected String filterValue( String name, String value, String rule ) {
+    return value;
+  }
+
+  @Override
+  public void close() throws IOException {
+    writer.close();
+    reader.close();
+  }
+
+}
+

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormPair.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormPair.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormPair.java
new file mode 100644
index 0000000..95c896b
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormPair.java
@@ -0,0 +1,51 @@
+/**
+ * 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.filter.rewrite.impl.form;
+
+public class FormPair {
+
+  String name;
+  String value;
+
+  public FormPair() {
+    this.name = null;
+    this.value = null;
+  }
+
+  public FormPair( String name, String value ) {
+    this.name = name;
+    this.value = value;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public void setName( String name ) {
+    this.name = name;
+  }
+
+  public String getValue() {
+    return value;
+  }
+
+  public void setValue( String value ) {
+    this.value = value;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormReader.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormReader.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormReader.java
new file mode 100644
index 0000000..af34088
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormReader.java
@@ -0,0 +1,96 @@
+/**
+ * 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.filter.rewrite.impl.form;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+public class FormReader {
+
+  private static final String DEFFAULT_FORM_ENCODING = "UTF-8";
+
+  private static final int DEFAULT_BUFFER_SIZE = 1024;
+
+  private Reader reader;
+  private FormPair current;
+  private StringBuilder buffer;
+  private int sepIndex;
+
+  public FormReader( Reader reader ) {
+    this.reader = reader;
+    this.current = null;
+    this.buffer = new StringBuilder( DEFAULT_BUFFER_SIZE );
+    this.sepIndex = -1;
+  }
+
+  public FormPair getNextPair() throws IOException {
+    while( true ) {
+      int c = reader.read();
+      switch( c ) {
+        case '=':
+          sepIndex = buffer.length();
+          break;
+        case '&':
+          // Ignore adjacent &s.
+          if( buffer.length() == 0 ) {
+            sepIndex = -1;
+            continue;
+          } else {
+            return createCurrentPair();
+          }
+        case -1:
+          // Ignore adjacent &s.
+          if( buffer.length() == 0 ) {
+            sepIndex = -1;
+            return null;
+          } else {
+            return createCurrentPair();
+          }
+        default:
+          buffer.append( (char)c );
+          break;
+      }
+    }
+  }
+
+  private FormPair createCurrentPair() throws UnsupportedEncodingException {
+    String name;
+    String value;
+    if( sepIndex >= 0 ) {
+      name = buffer.substring( 0, sepIndex );
+      value = buffer.substring( sepIndex );
+    } else {
+      name = buffer.toString();
+      value = "";
+    }
+    name = URLDecoder.decode( name, DEFFAULT_FORM_ENCODING );
+    value = URLDecoder.decode( value, DEFFAULT_FORM_ENCODING );
+    FormPair pair = new FormPair( name, value );
+    current = pair;
+    buffer.setLength( 0 );
+    sepIndex = -1;
+    return pair;
+  }
+
+  public FormPair getCurrentPair() {
+    return current;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormUrlRewriteFilterReader.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormUrlRewriteFilterReader.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormUrlRewriteFilterReader.java
new file mode 100644
index 0000000..d8f8ba4
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormUrlRewriteFilterReader.java
@@ -0,0 +1,60 @@
+/**
+ * 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.filter.rewrite.impl.form;
+
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriter;
+import org.apache.knox.gateway.filter.rewrite.i18n.UrlRewriteMessages;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.util.urltemplate.Parser;
+import org.apache.knox.gateway.util.urltemplate.Resolver;
+import org.apache.knox.gateway.util.urltemplate.Template;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.net.URISyntaxException;
+
+public class FormUrlRewriteFilterReader extends FormFilterReader {
+
+  private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class );
+
+  private Resolver resolver;
+  private UrlRewriter rewriter;
+  private UrlRewriter.Direction direction;
+
+  public FormUrlRewriteFilterReader( Reader reader, UrlRewriter rewriter, Resolver resolver, UrlRewriter.Direction direction, UrlRewriteFilterContentDescriptor config  ) throws IOException {
+    super( reader, config );
+    this.resolver = resolver;
+    this.rewriter = rewriter;
+    this.direction = direction;
+  }
+
+  //TODO: Need to limit which values are attempted to be filtered by the name.
+  @Override
+  protected String filterValue( String name, String value, String rule ) {
+    try {
+      Template input = Parser.parseLiteral( value );
+      Template output = rewriter.rewrite( resolver, input, direction, rule );
+      value = output.getPattern();
+    } catch( URISyntaxException e ) {
+      LOG.failedToParseValueForUrlRewrite( value );
+    }
+    return value;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormUrlRewriteStreamFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormUrlRewriteStreamFilter.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormUrlRewriteStreamFilter.java
new file mode 100644
index 0000000..d75868d
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormUrlRewriteStreamFilter.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.filter.rewrite.impl.form;
+
+import org.apache.commons.io.input.ReaderInputStream;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriter;
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteStreamFilter;
+import org.apache.knox.gateway.util.urltemplate.Resolver;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class FormUrlRewriteStreamFilter implements UrlRewriteStreamFilter {
+
+  private static String[] TYPES = new String[]{ "application/x-www-form-urlencoded", "*/x-www-form-urlencoded" };
+  private static String[] NAMES = new String[]{ null };
+
+  @Override
+  public String[] getTypes() {
+    return TYPES;
+  }
+
+  @Override
+  public String[] getNames() {
+    return NAMES;
+  }
+
+  @Override
+  public InputStream filter(
+      InputStream stream,
+      String encoding,
+      UrlRewriter rewriter,
+      Resolver resolver,
+      UrlRewriter.Direction direction,
+      UrlRewriteFilterContentDescriptor config )
+          throws IOException {
+    return new ReaderInputStream(
+        new FormUrlRewriteFilterReader(
+            new InputStreamReader( stream, encoding ), rewriter, resolver, direction, config ), encoding );
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormWriter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormWriter.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormWriter.java
new file mode 100644
index 0000000..b59d4d4
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/form/FormWriter.java
@@ -0,0 +1,47 @@
+/**
+ * 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.filter.rewrite.impl.form;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URLEncoder;
+
+public class FormWriter {
+
+  private static final String DEFFAULT_FORM_ENCODING = "UTF-8";
+
+  private Writer writer;
+  boolean first;
+
+  public FormWriter( Writer writer ) {
+    this.writer = writer;
+    this.first = true;
+  }
+
+  public void writePair( FormPair pair ) throws IOException {
+    if( first ) {
+      first = false;
+    } else {
+      writer.write( "&" );
+    }
+    writer.write( URLEncoder.encode( pair.getName(), DEFFAULT_FORM_ENCODING ) );
+    writer.write( "=" );
+    writer.write( URLEncoder.encode( pair.getValue(), DEFFAULT_FORM_ENCODING ) );
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReader.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReader.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReader.java
new file mode 100644
index 0000000..c141fa6
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReader.java
@@ -0,0 +1,61 @@
+/**
+ * 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.filter.rewrite.impl.html;
+
+import javax.xml.namespace.QName;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+
+import java.io.IOException;
+import java.io.Reader;
+
+//map.put( "meta", buildTagPattern( ".*url\\s*=\\s*['\"]?(.*?)[;\\s'\"\\/>].*" ) );
+//map.put( "link", buildTagPattern( ".*href\\s*=\\s*['\"]?(.*?)['\"\\>].*" ) );
+//map.put( "a", buildTagPattern( ".*href\\s*=\\s*['\"]?(.*?)['\"\\>].*" ) );
+//map.put( "th", buildTagPattern( ".*window.document.location\\s*=\\s*['\"]?(.*?)['\"\\>].*" ) );
+//    assertMatch( pattern, "<meta HTTP-EQUIV=\"REFRESH\" content=\"0;url=dfshealth.jsp\"/>", "meta" );
+//String markup = "<link href=\"/static/org.apache.hadoop.css\" rel=\"stylesheet\" type=\"text/css\" >";
+//String markup = "<a href=\"dfsnodelist.jsp?whatNodes=DECOMMISSIONING\">";
+//String markup = "th class=headerASC onClick=\"window.document.location='/dfsnodelist.jsp?whatNodes=LIVE&sorter/field=name&sorter/order=DSC'\" title=\"sort on this column\">";
+
+public abstract class HtmlFilterReader extends HtmlFilterReaderBase {
+
+  public HtmlFilterReader( Reader reader ) throws IOException, ParserConfigurationException {
+    super( reader );
+  }
+
+  public HtmlFilterReader( Reader reader, UrlRewriteFilterContentDescriptor config ) throws IOException, ParserConfigurationException {
+    super( reader, config );
+  }
+
+  protected abstract String filterAttribute( String tagName, String attributeName, String attributeValue, String ruleName );
+
+  protected abstract String filterText( String tagName, String text, String ruleName );
+
+  @Override
+  protected final String filterAttribute( QName elementName, QName attributeName, String attributeValue, String ruleName ) {
+    return filterAttribute( elementName.getLocalPart(), attributeName.getLocalPart(), attributeValue, ruleName );
+  }
+
+  @Override
+  protected final String filterText( QName elementName, String text, String ruleName ) {
+    return filterText( elementName.getLocalPart(), text, ruleName );
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReaderBase.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReaderBase.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReaderBase.java
new file mode 100644
index 0000000..b90771b
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlFilterReaderBase.java
@@ -0,0 +1,327 @@
+/**
+ * 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.filter.rewrite.impl.html;
+
+import net.htmlparser.jericho.Attribute;
+import net.htmlparser.jericho.Attributes;
+import net.htmlparser.jericho.EndTag;
+import net.htmlparser.jericho.Segment;
+import net.htmlparser.jericho.StartTag;
+import net.htmlparser.jericho.StreamedSource;
+import net.htmlparser.jericho.Tag;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterApplyDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterPathDescriptor;
+import org.apache.knox.gateway.filter.rewrite.i18n.UrlRewriteMessages;
+import org.apache.knox.gateway.filter.rewrite.impl.UrlRewriteFilterReader;
+import org.apache.knox.gateway.filter.rewrite.impl.UrlRewriteUtil;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.util.XmlUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.xml.namespace.QName;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Stack;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public abstract class HtmlFilterReaderBase extends Reader implements
+    UrlRewriteFilterReader {
+
+  private static final String SCRIPTTAG = "script";
+  private static final UrlRewriteFilterPathDescriptor.Compiler<Pattern> REGEX_COMPILER = new RegexCompiler();
+
+  private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class );
+
+  private Document document;
+  private Stack<Level> stack;
+  private Reader reader;
+  private StreamedSource parser;
+  private Iterator<Segment> iterator;
+  private int lastSegEnd;
+  private int offset;
+  private StringWriter writer;
+  private StringBuffer buffer;
+  private UrlRewriteFilterContentDescriptor config = null;
+
+  protected HtmlFilterReaderBase( Reader reader ) throws IOException, ParserConfigurationException {
+    this.reader = reader;
+    document = XmlUtils.createDocument( false );
+    stack = new Stack<Level>();
+    parser = new StreamedSource( reader );
+    iterator = parser.iterator();
+    writer = new StringWriter();
+    buffer = writer.getBuffer();
+    offset = 0;
+  }
+
+  protected HtmlFilterReaderBase( Reader reader, UrlRewriteFilterContentDescriptor config ) throws IOException, ParserConfigurationException {
+    this(reader);
+    this.config = config;
+  }
+
+  protected abstract String filterAttribute( QName elementName, QName attributeName, String attributeValue, String ruleName );
+
+  protected abstract String filterText( QName elementName, String text, String ruleName );
+
+  @Override
+  public int read( char[] destBuffer, int destOffset, int destCount ) throws IOException {
+    int count = 0;
+    int available = buffer.length() - offset;
+
+    if( available == 0 ) {
+      if( iterator.hasNext() ) {
+        iterator.next();
+        processCurrentSegment();
+        available = buffer.length() - offset;
+      } else {
+        count = -1;
+      }
+    }
+
+    if( available > 0 ) {
+      count = Math.min( destCount, available );
+      buffer.getChars( offset, offset + count, destBuffer, destOffset );
+      offset += count;
+      if( offset == buffer.length() ) {
+        offset = 0;
+        buffer.setLength( 0 );
+      }
+    }
+
+    return count;
+  }
+
+  private void processCurrentSegment() {
+    Segment segment = parser.getCurrentSegment();
+    // If this tag is inside the previous tag (e.g. a server tag) then
+    // ignore it as it was already output along with the previous tag.
+    if( segment.getEnd() <= lastSegEnd ) {
+      return;
+    }
+    lastSegEnd = segment.getEnd();
+    if( segment instanceof Tag ) {
+      if( segment instanceof StartTag ) {
+        processStartTag( (StartTag)segment );
+      } else if ( segment instanceof EndTag ) {
+        processEndTag( (EndTag)segment );
+      } else {
+        writer.write( segment.toString() );
+      }
+    } else {
+      processText( segment );
+    }
+  }
+
+  private void processEndTag( EndTag tag ) {
+    while( !stack.isEmpty() ) {
+      Level popped = stack.pop();
+      if( popped.getTag().getName().equalsIgnoreCase( tag.getName() ) ) {
+        break;
+      }
+    }
+    writer.write( tag.toString() );
+  }
+
+  private void processStartTag( StartTag tag ) {
+    if( "<".equals( tag.getTagType().getStartDelimiter() ) ) {
+      Element e = document.createElement( tag.getNameSegment().toString() );
+      stack.push( new Level( tag ) );
+      writer.write( "<" );
+      writer.write( tag.getNameSegment().toString() );
+      Attributes attributes = tag.getAttributes();
+      if( !attributes.isEmpty() ) {
+        for( Attribute attribute : attributes ) {
+          processAttribute( attribute );
+        }
+      }
+      if( tag.toString().trim().endsWith( "/>" ) || tag.isEmptyElementTag() ) {
+        stack.pop();
+        writer.write( "/>" );
+      } else {
+        writer.write( ">" );
+      }
+    } else {
+      writer.write( tag.toString() );
+    }
+  }
+
+  private void processAttribute( Attribute attribute ) {
+    writer.write( " " );
+    writer.write( attribute.getName() );
+    if(attribute.hasValue()) {
+      /*
+       * non decoded value, return the raw value of the attribute as it appears
+       * in the source document, without decoding, see KNOX-791.
+       */
+      String inputValue = attribute.getValueSegment().toString();
+      String outputValue = inputValue;
+      try {
+        Level tag = stack.peek();
+        String name = getRuleName(inputValue);
+        outputValue = filterAttribute( tag.getQName(), tag.getQName( attribute.getName() ), inputValue, name );
+        if( outputValue == null ) {
+          outputValue = inputValue;
+        }
+      } catch ( Exception e ) {
+        LOG.failedToFilterAttribute( attribute.getName(), e );
+      }
+      writer.write( "=" );
+      writer.write( attribute.getQuoteChar() );
+      writer.write( outputValue );
+      writer.write( attribute.getQuoteChar() );
+    }
+  }
+
+  private String getRuleName(String inputValue) {
+    if( config != null && !config.getSelectors().isEmpty() ) {
+      for( UrlRewriteFilterPathDescriptor selector : config.getSelectors() ) {
+        if ( selector instanceof UrlRewriteFilterApplyDescriptor) {
+          UrlRewriteFilterApplyDescriptor apply = (UrlRewriteFilterApplyDescriptor)selector;
+          Matcher matcher = apply.compiledPath( REGEX_COMPILER ).matcher( inputValue );
+            if (matcher.matches()) {
+              return apply.rule();
+            }
+          }
+        }
+      }
+      return null;
+    }
+
+  private void processText( Segment segment ) {
+    String inputValue = segment.toString();
+    String outputValue = inputValue;
+    try {
+      if( stack.isEmpty() ) {
+        // This can happen for whitespace outside of the root element.
+        //outputValue = filterText( null, inputValue );
+      } else {
+        String tagName = stack.peek().getTag().getName();
+        if (SCRIPTTAG.equals(tagName) && config != null && !config.getSelectors().isEmpty() ) {
+          // embedded javascript content
+          outputValue = UrlRewriteUtil.filterJavaScript( inputValue, config, this, REGEX_COMPILER );
+        } else {
+          outputValue = filterText( stack.peek().getQName(), inputValue, getRuleName(inputValue) );
+        }
+      }
+      if( outputValue == null ) {
+        outputValue = inputValue;
+      }
+    } catch ( Exception e ) {
+      LOG.failedToFilterValue( inputValue, null, e );
+    }
+    writer.write( outputValue );
+  }
+
+  @Override
+  public void close() throws IOException {
+    parser.close();
+    reader.close();
+    writer.close();
+    stack.clear();
+  }
+
+  private String getNamespace( String prefix ) {
+    String namespace = null;
+    for( Level level : stack ) {
+      namespace = level.getNamespace( prefix );
+      if( namespace != null ) {
+        break;
+      }
+    }
+    return namespace;
+  }
+
+  private static class Level {
+    private StartTag tag;
+    private QName name;
+    private Map<String,String> namespaces;
+
+    private Level( StartTag tag ) {
+      this.tag = tag;
+      this.name = null;
+      this.namespaces = null;
+    }
+
+    private StartTag getTag() {
+      return tag;
+    }
+
+    private QName getQName() {
+      if( name == null ) {
+        name = getQName( tag.getName() );
+      }
+      return name;
+    }
+
+    private String getNamespace( String prefix ) {
+      return getNamespaces().get( prefix );
+    }
+
+    private QName getQName( String name ) {
+      String prefix;
+      String local;
+      int colon = ( name == null ? -1 : name.indexOf( ':' ) );
+      if( colon < 0 ) {
+        prefix = "";
+        local = name;
+      } else {
+        prefix = name.substring( 0, colon );
+        local = ( colon + 1 < name.length() ? name.substring( colon + 1 ) : "" );
+      }
+      String namespace = getNamespace( prefix );
+      return new QName( namespace, local, prefix );
+    }
+
+    private Map<String,String> getNamespaces() {
+      if( namespaces == null ) {
+        namespaces = new HashMap<>();
+        parseNamespaces();
+      }
+      return namespaces;
+    }
+
+    private void parseNamespaces() {
+      Attributes attributes = tag.getAttributes();
+      if( attributes != null ) {
+        for( Attribute attribute : tag.getAttributes() ) {
+          String name = attribute.getName();
+          if( name.toLowerCase().startsWith( "xmlns" ) ) {
+            int colon = name.indexOf( ":", 5 );
+            String prefix;
+            if( colon <= 0 ) {
+              prefix = "";
+            } else {
+              prefix = name.substring( colon );
+            }
+            namespaces.put( prefix, attribute.getValue() );
+          }
+        }
+      }
+    }
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlImportFunctionDescriptor.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlImportFunctionDescriptor.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlImportFunctionDescriptor.java
new file mode 100644
index 0000000..ee11b7f
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlImportFunctionDescriptor.java
@@ -0,0 +1,31 @@
+/**
+ * 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.knox.gateway.filter.rewrite.impl.html;
+
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFunctionDescriptor;
+
+public class HtmlImportFunctionDescriptor implements
+    UrlRewriteFunctionDescriptor<HtmlImportFunctionDescriptor> {
+
+  public static final String FUNCTION_NAME = "import";
+
+  @Override
+  public String name() {
+    return FUNCTION_NAME;
+  }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlImportFunctionProcessor.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlImportFunctionProcessor.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlImportFunctionProcessor.java
new file mode 100644
index 0000000..280f1b5
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlImportFunctionProcessor.java
@@ -0,0 +1,90 @@
+/**
+ * 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.knox.gateway.filter.rewrite.impl.html;
+
+import org.apache.knox.gateway.filter.rewrite.api.FrontendFunctionDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteEnvironment;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFunctionDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFunctionDescriptorFactory;
+import org.apache.knox.gateway.filter.rewrite.impl.UrlRewriteFunctionProcessorFactory;
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteContext;
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteFunctionProcessor;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This function enhances the 'frontend' function with the ability to add a prefix to the rewritten frontend portion
+ * along with the '@import' literal. This is a workaround for the requirement to provide the ability to rewrite
+ * a portion of html content that contains a tag like the following
+ *
+ * <head> <style type=\"text/css\">@import "pretty.css";</style></head>
+ *
+ * and needs to be rewritten to something like
+ *
+ * <head> <style type=\"text/css\">@import "http://localhost:8443/sandbox/service/pretty.css";</style></head>
+ *
+ * The rewrite rule could then contain the $import function that would delegate to the frontend function.
+ *
+ * If there are more than one params passed, the first one is used as a prefix to the value of the frontend function.
+ *
+ */
+public class HtmlImportFunctionProcessor implements UrlRewriteFunctionProcessor<HtmlImportFunctionDescriptor> {
+
+  private static final String IMPORT_LITERAL = "@import";
+
+  private UrlRewriteFunctionProcessor frontend;
+
+  @Override
+  public void initialize(UrlRewriteEnvironment environment, HtmlImportFunctionDescriptor descriptor) throws Exception {
+    UrlRewriteFunctionDescriptor frontendDescriptor = UrlRewriteFunctionDescriptorFactory
+        .create(FrontendFunctionDescriptor.FUNCTION_NAME);
+    frontend = UrlRewriteFunctionProcessorFactory.create(FrontendFunctionDescriptor.FUNCTION_NAME, frontendDescriptor);
+    frontend.initialize(environment, frontendDescriptor);
+  }
+
+  @Override
+  public void destroy() throws Exception {
+    frontend.destroy();
+  }
+
+  @Override
+  public List<String> resolve(UrlRewriteContext context, List<String> parameters) throws Exception {
+    String prefix = "";
+    if ( parameters != null && parameters.size() > 1 ) {
+      prefix = parameters.get(0);
+      parameters = parameters.subList(1, parameters.size());
+    }
+    List<String> frontendValues = frontend.resolve(context, parameters);
+    StringBuffer buffer = new StringBuffer(IMPORT_LITERAL);
+    buffer.append(" ");
+    buffer.append(prefix);
+    if ( frontendValues != null && frontendValues.size() > 0 ) {
+      for ( String value : frontendValues ) {
+        buffer.append(value);
+      }
+    }
+    return Arrays.asList(buffer.toString());
+  }
+
+  @Override
+  public String name() {
+    return HtmlImportFunctionDescriptor.FUNCTION_NAME;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlPrefixDescriptor.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlPrefixDescriptor.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlPrefixDescriptor.java
new file mode 100644
index 0000000..0f37b50
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlPrefixDescriptor.java
@@ -0,0 +1,48 @@
+package org.apache.knox.gateway.filter.rewrite.impl.html;
+
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFunctionDescriptor;
+
+/**
+ * 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.
+ */
+
+/**
+ * {@link UrlRewriteFunctionDescriptor} for the variable {@link
+ * HtmlPrefixDescriptor#FUNCTION_NAME}
+ *
+ * @since 0.14.0
+ */
+public class HtmlPrefixDescriptor
+    implements UrlRewriteFunctionDescriptor<HtmlPrefixDescriptor> {
+
+  /**
+   * variable name used in rewrite.xml
+   */
+  public static final String FUNCTION_NAME = "prefix";
+
+  /**
+   * Create an instance
+   */
+  public HtmlPrefixDescriptor() {
+    super();
+  }
+
+  @Override
+  public String name() {
+    return FUNCTION_NAME;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlPrefixProcessor.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlPrefixProcessor.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlPrefixProcessor.java
new file mode 100644
index 0000000..d7983ef
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlPrefixProcessor.java
@@ -0,0 +1,104 @@
+/**
+ * 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.knox.gateway.filter.rewrite.impl.html;
+
+import org.apache.knox.gateway.filter.rewrite.api.FrontendFunctionDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteEnvironment;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFunctionDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFunctionDescriptorFactory;
+import org.apache.knox.gateway.filter.rewrite.impl.UrlRewriteFunctionProcessorFactory;
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteContext;
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteFunctionProcessor;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This function enhances the 'frontend' function with the ability to add a
+ * prefix to the rewritten frontend portion along with the literals
+ * provided as an argument.
+ * <p>
+ * <div ng-include src=\"'components/navbar/navbar.html?v=1498928142479'\"></div>
+ * <p>
+ * and needs to be rewritten to something like
+ * <p>
+ * <div ng-include src=\"'http://localhost:8443/sandbox/service/components/navbar/navbar.html?v=1498928142479'\"></div>
+ * <p>
+ * The rewrite rule could then contain the $prefix function that would delegate
+ * to the frontend function.
+ * <p>
+ * The parameter to the function would be the symbol used as a prefix.
+ */
+
+public class HtmlPrefixProcessor
+    implements UrlRewriteFunctionProcessor<HtmlPrefixDescriptor> {
+
+  private UrlRewriteFunctionProcessor frontend;
+
+  /**
+   * Create an instance
+   */
+  public HtmlPrefixProcessor() {
+    super();
+  }
+
+  @Override
+  public void initialize(final UrlRewriteEnvironment environment,
+      final HtmlPrefixDescriptor descriptor) throws Exception {
+
+    final UrlRewriteFunctionDescriptor frontendDescriptor = UrlRewriteFunctionDescriptorFactory
+        .create(FrontendFunctionDescriptor.FUNCTION_NAME);
+
+    frontend = UrlRewriteFunctionProcessorFactory
+        .create(FrontendFunctionDescriptor.FUNCTION_NAME, frontendDescriptor);
+
+    frontend.initialize(environment, frontendDescriptor);
+  }
+
+  @Override
+  public String name() {
+    return HtmlPrefixDescriptor.FUNCTION_NAME;
+  }
+
+  @Override
+  public void destroy() throws Exception {
+    frontend.destroy();
+  }
+
+  @Override
+  public List<String> resolve(UrlRewriteContext context,
+      List<String> parameters) throws Exception {
+    String prefix = "";
+
+    if ((parameters != null) && (parameters.size() > 1)) {
+      prefix = parameters.get(0);
+      parameters = parameters.subList(1, parameters.size());
+    }
+
+    final List<String> frontendValues = frontend.resolve(context, parameters);
+
+    final StringBuffer buffer = new StringBuffer();
+    buffer.append(prefix);
+    if (frontendValues != null && frontendValues.size() > 0) {
+      for (final String value : frontendValues) {
+        buffer.append(value);
+      }
+    }
+
+    return Arrays.asList(buffer.toString());
+  }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlUrlRewriteFilterReader.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlUrlRewriteFilterReader.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlUrlRewriteFilterReader.java
new file mode 100644
index 0000000..faade4f
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlUrlRewriteFilterReader.java
@@ -0,0 +1,74 @@
+/**
+ * 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.filter.rewrite.impl.html;
+
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriter;
+import org.apache.knox.gateway.filter.rewrite.i18n.UrlRewriteMessages;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.util.urltemplate.Parser;
+import org.apache.knox.gateway.util.urltemplate.Resolver;
+import org.apache.knox.gateway.util.urltemplate.Template;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.Reader;
+import java.net.URISyntaxException;
+
+public class HtmlUrlRewriteFilterReader extends HtmlFilterReader {
+
+  private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class );
+  
+  private Resolver resolver;
+  private UrlRewriter rewriter;
+  private UrlRewriter.Direction direction;
+
+  public HtmlUrlRewriteFilterReader( Reader reader, UrlRewriter rewriter, Resolver resolver, UrlRewriter.Direction direction, UrlRewriteFilterContentDescriptor config )
+      throws IOException, ParserConfigurationException {
+    super( reader, config );
+    this.resolver = resolver;
+    this.rewriter = rewriter;
+    this.direction = direction;
+  }
+
+  //TODO: Need to limit which values are attempted to be filtered by the name.
+  @Override
+  public String filterValueString( String name, String value, String rule ) {
+    try {
+      Template input = Parser.parseLiteral( value );
+      Template output = rewriter.rewrite( resolver, input, direction, rule );
+      if( output != null ) {
+        value = output.getPattern();
+      }
+    } catch( URISyntaxException e ) {
+      LOG.failedToParseValueForUrlRewrite( value );
+    }
+    return value;
+  }
+
+  @Override
+  protected String filterAttribute( String tagName, String attributeName, String attributeValue, String ruleName ) {
+    return filterValueString( attributeName, attributeValue, ruleName );
+  }
+
+  @Override
+  protected String filterText( String tagName, String text, String ruleName ) {
+    return filterValueString( tagName, text, ruleName );
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlUrlRewriteStreamFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlUrlRewriteStreamFilter.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlUrlRewriteStreamFilter.java
new file mode 100644
index 0000000..543c323
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/html/HtmlUrlRewriteStreamFilter.java
@@ -0,0 +1,64 @@
+/**
+ * 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.filter.rewrite.impl.html;
+
+import org.apache.commons.io.input.ReaderInputStream;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriter;
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteStreamFilter;
+import org.apache.knox.gateway.util.urltemplate.Resolver;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class HtmlUrlRewriteStreamFilter implements UrlRewriteStreamFilter {
+
+  private static String[] TYPES = new String[]{ "application/html", "text/html", "*/html" };
+  private static String[] NAMES = new String[]{ null };
+
+  @Override
+  public String[] getTypes() {
+    return TYPES;
+  }
+
+  @Override
+  public String[] getNames() {
+    return NAMES;
+  }
+
+  @Override
+  public InputStream filter(
+      InputStream stream,
+      String encoding,
+      UrlRewriter rewriter,
+      Resolver resolver,
+      UrlRewriter.Direction direction,
+      UrlRewriteFilterContentDescriptor config )
+          throws IOException {
+    try {
+      return new ReaderInputStream(
+          new HtmlUrlRewriteFilterReader(
+              new InputStreamReader( stream, encoding ), rewriter, resolver, direction, config ), encoding );
+    } catch( ParserConfigurationException e ) {
+      throw new IOException( e );
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptFilterReader.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptFilterReader.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptFilterReader.java
new file mode 100644
index 0000000..89c5eaf
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptFilterReader.java
@@ -0,0 +1,91 @@
+/**
+ * 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.filter.rewrite.impl.javascript;
+
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterPathDescriptor;
+import org.apache.knox.gateway.filter.rewrite.i18n.UrlRewriteMessages;
+import org.apache.knox.gateway.filter.rewrite.impl.UrlRewriteFilterReader;
+import org.apache.knox.gateway.filter.rewrite.impl.UrlRewriteUtil;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.util.regex.Pattern;
+
+public abstract class JavaScriptFilterReader extends Reader implements UrlRewriteFilterReader {
+
+  private static final UrlRewriteFilterPathDescriptor.Compiler<Pattern> REGEX_COMPILER = new RegexCompiler();
+
+  private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class );
+
+  private BufferedReader reader;
+  private int offset;
+  private StringWriter writer;
+  private StringBuffer buffer;
+  private UrlRewriteFilterContentDescriptor config;
+
+  protected JavaScriptFilterReader( Reader reader, UrlRewriteFilterContentDescriptor config ) throws IOException {
+    this.reader = new BufferedReader( reader );
+    this.config = config;
+    writer = new StringWriter();
+    buffer = writer.getBuffer();
+    offset = 0;
+  }
+
+  @Override
+  public abstract String filterValueString( String name, String value, String rule );
+
+  @Override
+  public int read( char[] destBuffer, int destOffset, int destCount ) throws IOException {
+    int count = 0;
+    int available = buffer.length() - offset;
+    String cbuff;
+    if( available == 0 ) {
+      cbuff = reader.readLine();
+      if( cbuff != null ) {
+        count = cbuff.length();
+        writer.write( UrlRewriteUtil.filterJavaScript( cbuff, config, this, REGEX_COMPILER ) );
+        writer.write( '\n' );
+        available = buffer.length() - offset;
+      } else {
+        count = -1;
+      }
+    }
+
+    if( available > 0 ) {
+      count = Math.min( destCount, available );
+      buffer.getChars( offset, offset + count, destBuffer, destOffset );
+      offset += count;
+      if( offset == buffer.length() ) {
+        offset = 0;
+        buffer.setLength( 0 );
+      }
+    }
+
+    return count;
+  }
+
+  @Override
+  public void close() throws IOException {
+    reader.close();
+    writer.close();
+  }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptUrlRewriteFilterReader.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptUrlRewriteFilterReader.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptUrlRewriteFilterReader.java
new file mode 100644
index 0000000..da1963b
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptUrlRewriteFilterReader.java
@@ -0,0 +1,62 @@
+/**
+ * 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.filter.rewrite.impl.javascript;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.net.URISyntaxException;
+
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriter;
+import org.apache.knox.gateway.filter.rewrite.i18n.UrlRewriteMessages;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.util.urltemplate.Parser;
+import org.apache.knox.gateway.util.urltemplate.Resolver;
+import org.apache.knox.gateway.util.urltemplate.Template;
+
+public class JavaScriptUrlRewriteFilterReader extends JavaScriptFilterReader {
+
+  private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class );
+
+  private Resolver resolver;
+  private UrlRewriter rewriter;
+  private UrlRewriter.Direction direction;
+
+  public JavaScriptUrlRewriteFilterReader( Reader reader, UrlRewriter rewriter, Resolver resolver, UrlRewriter.Direction direction, UrlRewriteFilterContentDescriptor config )
+      throws IOException {
+    super( reader, config );
+    this.resolver = resolver;
+    this.rewriter = rewriter;
+    this.direction = direction;
+  }
+
+  @Override
+  public String filterValueString( String name, String value, String rule ) {
+    try {
+      Template input = Parser.parseLiteral( value );
+      Template output = rewriter.rewrite( resolver, input, direction, rule );
+      if( output != null ) {
+        value = output.getPattern();
+      }
+    } catch( URISyntaxException e ) {
+      LOG.failedToParseValueForUrlRewrite( value );
+    }
+    return value;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptUrlRewriteStreamFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptUrlRewriteStreamFilter.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptUrlRewriteStreamFilter.java
new file mode 100644
index 0000000..b93d6d3
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/javascript/JavaScriptUrlRewriteStreamFilter.java
@@ -0,0 +1,65 @@
+/**
+ * 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.filter.rewrite.impl.javascript;
+
+import org.apache.commons.io.input.ReaderInputStream;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriter;
+import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteStreamFilter;
+import org.apache.knox.gateway.util.urltemplate.Resolver;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+public class JavaScriptUrlRewriteStreamFilter implements
+    UrlRewriteStreamFilter {
+
+  private static String[] TYPES = new String[]{ "application/javascript", "text/javascript", "*/javascript",
+      "application/x-javascript", "text/x-javascript", "*/x-javascript" };
+  private static String[] NAMES = new String[]{ null };
+
+  @Override
+  public String[] getTypes() {
+    return TYPES;
+  }
+
+  @Override
+  public String[] getNames() {
+    return NAMES;
+  }
+
+  @Override
+  public InputStream filter(
+      InputStream stream,
+      String encoding,
+      UrlRewriter rewriter,
+      Resolver resolver,
+      UrlRewriter.Direction direction,
+      UrlRewriteFilterContentDescriptor config )
+          throws IOException {
+
+    if ( config != null ) {
+      return new ReaderInputStream(
+          new JavaScriptUrlRewriteFilterReader(
+              new InputStreamReader( stream, encoding ), rewriter, resolver, direction, config ), encoding );
+    } else {
+      return stream;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/json/JsonFilterReader.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/json/JsonFilterReader.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/json/JsonFilterReader.java
new file mode 100644
index 0000000..8286dbc
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/json/JsonFilterReader.java
@@ -0,0 +1,644 @@
+/**
+ * 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.filter.rewrite.impl.json;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.fasterxml.jackson.databind.node.TextNode;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterApplyDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterBufferDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterDetectDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterGroupDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterPathDescriptor;
+import org.apache.knox.gateway.filter.rewrite.i18n.UrlRewriteMessages;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.util.JsonPath;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.Stack;
+import java.util.regex.Pattern;
+
+class JsonFilterReader extends Reader {
+
+  private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class );
+
+  private static final UrlRewriteFilterPathDescriptor.Compiler<JsonPath.Expression> JPATH_COMPILER = new JsonPathCompiler();
+  private static final UrlRewriteFilterPathDescriptor.Compiler<Pattern> REGEX_COMPILER = new RegexCompiler();
+
+  private JsonFactory factory;
+  private JsonParser parser;
+  private JsonGenerator generator;
+  private ObjectMapper mapper;
+
+  private Reader reader;
+  private int offset;
+  private StringWriter writer;
+  private StringBuffer buffer;
+  private Stack<Level> stack;
+  private Level bufferingLevel;
+  private UrlRewriteFilterBufferDescriptor bufferingConfig;
+  private UrlRewriteFilterGroupDescriptor config;
+
+
+  public JsonFilterReader( Reader reader, UrlRewriteFilterContentDescriptor config ) throws IOException {
+    this.reader = reader;
+    factory = new JsonFactory();
+    mapper = new ObjectMapper();
+    parser = factory.createParser( reader );
+    writer = new StringWriter();
+    buffer = writer.getBuffer();
+    offset = 0;
+    generator = factory.createGenerator( writer );
+    stack = new Stack<Level>();
+    bufferingLevel = null;
+    bufferingConfig = null;
+    this.config = config;
+  }
+
+  @Override
+  public int read( char[] destBuffer, int destOffset, int destCount ) throws IOException {
+    int count = 0;
+    int available = buffer.length() - offset;
+
+    if( available == 0 ) {
+      JsonToken token = parser.nextToken();
+      if( token == null ) {
+        count = -1;
+      } else {
+        processCurrentToken();
+        available = buffer.length() - offset;
+      }
+    }
+
+    if( available > 0 ) {
+      count = Math.min( destCount, available );
+      buffer.getChars( offset, offset+count, destBuffer, destOffset );
+      offset += count;
+      if( offset == buffer.length() ) {
+        offset = 0;
+        buffer.setLength( 0 );
+      }
+    }
+
+    return count;
+  }
+
+  private void processCurrentToken() throws IOException {
+    switch( parser.getCurrentToken() ) {
+      case START_OBJECT:
+        processStartObject();
+        break;
+      case END_OBJECT:
+        processEndObject();
+        break;
+      case START_ARRAY:
+        processStartArray();
+        break;
+      case END_ARRAY:
+        processEndArray();
+        break;
+      case FIELD_NAME:
+        processFieldName(); // Could be the name of an object, array or value.
+        break;
+      case VALUE_STRING:
+        processValueString();
+        break;
+      case VALUE_NUMBER_INT:
+      case VALUE_NUMBER_FLOAT:
+        processValueNumber();
+        break;
+      case VALUE_TRUE:
+      case VALUE_FALSE:
+        processValueBoolean();
+        break;
+      case VALUE_NULL:
+        processValueNull();
+        break;
+      case NOT_AVAILABLE:
+        // Ignore it.
+        break;
+    }
+    generator.flush();
+  }
+
+  private Level pushLevel( String field, JsonNode node, JsonNode scopeNode, UrlRewriteFilterGroupDescriptor scopeConfig ) {
+    if( !stack.isEmpty() ) {
+      Level top = stack.peek();
+      if( scopeNode == null ) {
+        scopeNode = top.scopeNode;
+        scopeConfig = top.scopeConfig;
+      }
+    }
+    Level level = new Level( field, node, scopeNode, scopeConfig );
+    stack.push( level );
+    return level;
+  }
+
+  private void processStartObject() throws IOException {
+    JsonNode node;
+    Level child;
+    Level parent;
+    if( stack.isEmpty() ) {
+      node = mapper.createObjectNode();
+      child = pushLevel( null, node, node, config );
+    } else {
+      child = stack.peek();
+      if( child.node == null ) {
+        child.node = mapper.createObjectNode();
+        parent = stack.get( stack.size()-2 );
+        switch( parent.node.asToken() ) {
+          case START_ARRAY:
+            ((ArrayNode)parent.node ).add( child.node );
+            break;
+          case START_OBJECT:
+            ((ObjectNode)parent.node ).put( child.field, child.node );
+            break;
+          default:
+            throw new IllegalStateException();
+        }
+      } else if( child.isArray() ) {
+        parent = child;
+        node = mapper.createObjectNode();
+        child = pushLevel( null, node, null, null );
+        ((ArrayNode)parent.node ).add( child.node );
+      } else {
+        throw new IllegalStateException();
+      }
+    }
+    if( bufferingLevel == null ) {
+      if( !startBuffering( child ) ) {
+        generator.writeStartObject();
+      }
+    }
+  }
+
+  private void processEndObject() throws IOException {
+    Level child;
+    Level parent;
+    child = stack.pop();
+    if( bufferingLevel == child ) {
+      filterBufferedNode( child );
+      mapper.writeTree( generator, child.node );
+      bufferingLevel = null;
+      bufferingConfig = null;
+    } else if( bufferingLevel == null ) {
+      generator.writeEndObject();
+      if( !stack.isEmpty() ) {
+        parent = stack.peek();
+        switch( parent.node.asToken() ) {
+          case START_ARRAY:
+            ((ArrayNode)parent.node ).removeAll();
+            break;
+          case START_OBJECT:
+            ((ObjectNode)parent.node ).removeAll();
+            break;
+          default:
+            throw new IllegalStateException();
+        }
+      }
+    }
+  }
+
+  private void processStartArray() throws IOException {
+    JsonNode node;
+    Level child;
+    Level parent;
+    if( stack.isEmpty() ) {
+      node = mapper.createArrayNode();
+      child = pushLevel( null, node, node, config );
+    } else {
+      child = stack.peek();
+      if( child.node == null ) {
+        child.node = mapper.createArrayNode();
+        parent = stack.get( stack.size() - 2 );
+        switch( parent.node.asToken() ) {
+          case START_ARRAY:
+            ((ArrayNode)parent.node ).add( child.node );
+            break;
+          case START_OBJECT:
+            ((ObjectNode)parent.node ).put( child.field, child.node );
+            break;
+          default:
+            throw new IllegalStateException();
+        }
+      } else if( child.isArray() ) {
+        parent = child;
+        child = pushLevel( null, mapper.createArrayNode(), null, null );
+        ((ArrayNode)parent.node ).add( child.node );
+      } else {
+        throw new IllegalStateException();
+      }
+    }
+    if( bufferingLevel == null ) {
+      if( !startBuffering( child ) ) {
+        generator.writeStartArray();
+      }
+    }
+  }
+
+  private void processEndArray() throws IOException {
+    Level child;
+    Level parent;
+    child = stack.pop();
+    if( bufferingLevel == child ) {
+      filterBufferedNode( child );
+      mapper.writeTree( generator, child.node );
+      bufferingLevel = null;
+      bufferingConfig = null;
+    } else if( bufferingLevel == null ) {
+      generator.writeEndArray();
+      if( !stack.isEmpty() ) {
+        parent = stack.peek();
+        switch( parent.node.asToken() ) {
+          case START_ARRAY:
+            ((ArrayNode)parent.node ).removeAll();
+            break;
+          case START_OBJECT:
+            ((ObjectNode)parent.node ).removeAll();
+            break;
+          default:
+            throw new IllegalStateException();
+        }
+      }
+    }
+  }
+
+  private void processFieldName() throws IOException {
+    Level child = pushLevel( parser.getCurrentName(), null, null, null );
+    try {
+      child.field = filterFieldName( child.field );
+    } catch( Exception e ) {
+      LOG.failedToFilterFieldName( child.field, e );
+      // Write original name.
+    }
+    if( bufferingLevel == null ) {
+      generator.writeFieldName( child.field );
+    }
+  }
+
+  private void processValueString() throws IOException {
+    Level child;
+    Level parent;
+    String value = null;
+    parent = stack.peek();
+    if( parent.isArray() ) {
+      ArrayNode array = (ArrayNode)parent.node;
+      array.add( parser.getText() );
+      if( bufferingLevel == null ) {
+        value = filterStreamValue( parent );
+        array.set( array.size()-1, new TextNode( value ) );
+      } else {
+        array.removeAll();
+      }
+    } else {
+      child = stack.pop();
+      parent = stack.peek();
+      ((ObjectNode)parent.node ).put( child.field, parser.getText() );
+      if( bufferingLevel == null ) {
+        child.node = parent.node; // Populate the JsonNode of the child for filtering.
+        value = filterStreamValue( child );
+      }
+    }
+    if( bufferingLevel == null ) {
+      if( parent.node.isArray() ) {
+        ((ArrayNode)parent.node).removeAll();
+      } else {
+        ((ObjectNode)parent.node).removeAll();
+      }
+      generator.writeString( value );
+    }
+  }
+
+  private void processValueNumber() throws IOException {
+    Level child;
+    Level parent;
+    parent = stack.peek();
+    if( parent.isArray() ) {
+      if( bufferingLevel != null ) {
+        ArrayNode array = (ArrayNode)parent.node;
+        processBufferedArrayValueNumber( array );
+      }
+    } else {
+      child = stack.pop();
+      if( bufferingLevel != null ) {
+        parent = stack.peek();
+        ObjectNode object = (ObjectNode)parent.node;
+        processBufferedFieldValueNumber( child, object );
+      }
+    }
+    if( bufferingLevel == null ) {
+      processedUnbufferedValueNumber();
+    }
+  }
+
+  private void processedUnbufferedValueNumber() throws IOException {
+    switch( parser.getNumberType() ) {
+      case INT:
+        generator.writeNumber( parser.getIntValue() );
+        break;
+      case LONG:
+        generator.writeNumber( parser.getLongValue() );
+        break;
+      case BIG_INTEGER:
+        generator.writeNumber( parser.getBigIntegerValue() );
+        break;
+      case FLOAT:
+        generator.writeNumber( parser.getFloatValue() );
+        break;
+      case DOUBLE:
+        generator.writeNumber( parser.getDoubleValue() );
+        break;
+      case BIG_DECIMAL:
+        generator.writeNumber( parser.getDecimalValue() );
+        break;
+    }
+  }
+
+  private void processBufferedFieldValueNumber( Level child, ObjectNode object ) throws IOException {
+    //object.put( child.field, parser.getDecimalValue() );
+    switch( parser.getNumberType() ) {
+      case INT:
+        object.put( child.field, parser.getIntValue() );
+        break;
+      case LONG:
+        object.put( child.field, parser.getLongValue() );
+        break;
+      case BIG_INTEGER:
+        object.put( child.field, parser.getDecimalValue() );
+        break;
+      case FLOAT:
+        object.put( child.field, parser.getFloatValue() );
+        break;
+      case DOUBLE:
+        object.put( child.field, parser.getDoubleValue() );
+        break;
+      case BIG_DECIMAL:
+        object.put( child.field, parser.getDecimalValue() );
+        break;
+    }
+  }
+
+  private void processBufferedArrayValueNumber( ArrayNode array ) throws IOException {
+    //array.add( parser.getDecimalValue() );
+    switch( parser.getNumberType() ) {
+      case INT:
+        array.add( parser.getIntValue() );
+        break;
+      case LONG:
+        array.add( parser.getLongValue() );
+        break;
+      case BIG_INTEGER:
+        array.add( parser.getDecimalValue() );
+        break;
+      case FLOAT:
+        array.add( parser.getFloatValue() );
+        break;
+      case DOUBLE:
+        array.add( parser.getDoubleValue() );
+        break;
+      case BIG_DECIMAL:
+        array.add( parser.getDecimalValue() );
+        break;
+    }
+  }
+
+  private void processValueBoolean() throws IOException {
+    Level child;
+    Level parent;
+    parent = stack.peek();
+    if( parent.isArray() ) {
+      ((ArrayNode)parent.node ).add( parser.getBooleanValue() );
+      //dump();
+      if( bufferingLevel == null ) {
+        ((ArrayNode)parent.node ).removeAll();
+      }
+    } else {
+      child = stack.pop();
+      parent = stack.peek();
+      ((ObjectNode)parent.node ).put( child.field, parser.getBooleanValue() );
+      //dump();
+      if( bufferingLevel == null ) {
+        ((ObjectNode)parent.node ).remove( child.field );
+      }
+    }
+    if( bufferingLevel == null ) {
+      generator.writeBoolean( parser.getBooleanValue() );
+    }
+  }
+
+  private void processValueNull() throws IOException {
+    Level child;
+    Level parent = stack.peek();
+    if( parent.isArray() ) {
+      ((ArrayNode)parent.node ).addNull();
+      //dump();
+      if( bufferingLevel == null ) {
+        ((ArrayNode)parent.node ).removeAll();
+      }
+    } else {
+      child = stack.pop();
+      parent = stack.peek();
+      ((ObjectNode)parent.node ).putNull( child.field );
+      //dump();
+      if( bufferingLevel == null ) {
+        ((ObjectNode)parent.node ).remove( child.field );
+      }
+    }
+    if( bufferingLevel == null ) {
+      generator.writeNull();
+    }
+  }
+
+  protected boolean startBuffering( Level node ) {
+    boolean buffered = false;
+    UrlRewriteFilterGroupDescriptor scope = node.scopeConfig;
+    if( scope != null ) {
+      for( UrlRewriteFilterPathDescriptor selector : scope.getSelectors() ) {
+        JsonPath.Expression path = (JsonPath.Expression)selector.compiledPath( JPATH_COMPILER );
+        List<JsonPath.Match> matches = path.evaluate( node.scopeNode );
+        if( matches != null && matches.size() > 0 ) {
+          if( selector instanceof UrlRewriteFilterBufferDescriptor ) {
+            bufferingLevel = node;
+            bufferingConfig = (UrlRewriteFilterBufferDescriptor)selector;
+            buffered = true;
+          }
+          break;
+        }
+      }
+    }
+    return buffered;
+  }
+
+  protected String filterStreamValue( Level node ) {
+    String value;
+    if( node.isArray() ) {
+      value = node.node.get( 0 ).asText();
+    } else {
+      value = node.node.get( node.field ).asText();
+    }
+    String rule = null;
+    UrlRewriteFilterGroupDescriptor scope = node.scopeConfig;
+    //TODO: Scan the top level apply rules for the first match.
+    if( scope != null ) {
+      for( UrlRewriteFilterPathDescriptor selector : scope.getSelectors() ) {
+        JsonPath.Expression path = (JsonPath.Expression)selector.compiledPath( JPATH_COMPILER );
+        List<JsonPath.Match> matches = path.evaluate( node.scopeNode );
+        if( matches != null && matches.size() > 0 ) {
+          JsonPath.Match match = matches.get( 0 );
+          if( match.getNode().isTextual() ) {
+            if( selector instanceof UrlRewriteFilterApplyDescriptor ) {
+              UrlRewriteFilterApplyDescriptor apply = (UrlRewriteFilterApplyDescriptor)selector;
+              rule = apply.rule();
+              break;
+            }
+          }
+        }
+      }
+    }
+    try {
+      value = filterValueString( node.field, value, rule );
+      if( node.isArray() ) {
+        ((ArrayNode)node.node).set( 0, new TextNode( value ) );
+      } else {
+        ((ObjectNode)node.node).put( node.field, value );
+      }
+    } catch( Exception e ) {
+      LOG.failedToFilterValue( value, rule, e );
+    }
+    return value;
+  }
+
+  private void filterBufferedNode( Level node ) {
+    for( UrlRewriteFilterPathDescriptor selector : bufferingConfig.getSelectors() ) {
+      JsonPath.Expression path = (JsonPath.Expression)selector.compiledPath( JPATH_COMPILER );
+      List<JsonPath.Match> matches = path.evaluate( node.node );
+      for( JsonPath.Match match : matches ) {
+        if( selector instanceof UrlRewriteFilterApplyDescriptor ) {
+          if( match.getNode().isTextual() ) {
+            filterBufferedValue( match, (UrlRewriteFilterApplyDescriptor)selector );
+          }
+        } else if( selector instanceof UrlRewriteFilterDetectDescriptor ) {
+          UrlRewriteFilterDetectDescriptor detectConfig = (UrlRewriteFilterDetectDescriptor)selector;
+          JsonPath.Expression detectPath = (JsonPath.Expression)detectConfig.compiledPath( JPATH_COMPILER );
+          List<JsonPath.Match> detectMatches = detectPath.evaluate( node.node );
+          for( JsonPath.Match detectMatch : detectMatches ) {
+            if( detectMatch.getNode().isTextual() ) {
+              String detectValue = detectMatch.getNode().asText();
+              Pattern detectPattern = detectConfig.compiledValue( REGEX_COMPILER );
+              if( detectPattern.matcher( detectValue ).matches() ) {
+                filterBufferedValues( node, detectConfig.getSelectors() );
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  private void filterBufferedValues( Level node, List<UrlRewriteFilterPathDescriptor> selectors ) {
+    for( UrlRewriteFilterPathDescriptor selector : selectors ) {
+      JsonPath.Expression path = (JsonPath.Expression)selector.compiledPath( JPATH_COMPILER );
+      List<JsonPath.Match> matches = path.evaluate( node.node );
+      for( JsonPath.Match match : matches ) {
+        if( match.getNode().isTextual() ) {
+          if( selector instanceof UrlRewriteFilterApplyDescriptor ) {
+            filterBufferedValue( match, (UrlRewriteFilterApplyDescriptor)selector );
+          }
+        }
+      }
+    }
+  }
+
+  private void filterBufferedValue( JsonPath.Match match, UrlRewriteFilterApplyDescriptor apply ) {
+    String field = match.getField();
+    String value = match.getNode().asText();
+    try {
+      value = filterValueString( field, value, apply.rule() );
+      ((ObjectNode)match.getParent().getNode()).put( field, value );
+    } catch( Exception e ) {
+      LOG.failedToFilterValue( value, apply.rule(), e );
+    }
+  }
+
+  protected String filterFieldName( String field ) {
+    return field;
+  }
+
+  protected String filterValueString( String name, String value, String rule ) {
+    return value;
+  }
+
+  @Override
+  public void close() throws IOException {
+    generator.close();
+    writer.close();
+    parser.close();
+    reader.close();
+  }
+
+  private static class Level {
+    String field;
+    JsonNode node;
+    JsonNode scopeNode;
+    UrlRewriteFilterGroupDescriptor scopeConfig;
+    private Level( String field, JsonNode node, JsonNode scopeNode, UrlRewriteFilterGroupDescriptor scopeConfig ) {
+      this.field = field;
+      this.node = node;
+      this.scopeNode = scopeNode;
+      this.scopeConfig = scopeConfig;
+    }
+    public boolean isArray() {
+      return node != null && node.isArray();
+    }
+  }
+
+  private static class JsonPathCompiler implements UrlRewriteFilterPathDescriptor.Compiler<JsonPath.Expression> {
+    @Override
+    public JsonPath.Expression compile( String expression, JsonPath.Expression compiled ) {
+      return JsonPath.compile( expression );
+    }
+  }
+
+  private static class RegexCompiler implements UrlRewriteFilterPathDescriptor.Compiler<Pattern> {
+    @Override
+    public Pattern compile( String expression, Pattern compiled ) {
+      if( compiled != null ) {
+        return compiled;
+      } else {
+        return Pattern.compile( expression );
+      }
+    }
+  }
+
+//  private void dump() throws IOException {
+//    mapper.writeTree( factory.createGenerator( System.out ), stack.get( 0 ).node );
+//    System.out.println();
+//  }
+
+}
+

http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/json/JsonUrlRewriteFilterReader.java
----------------------------------------------------------------------
diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/json/JsonUrlRewriteFilterReader.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/json/JsonUrlRewriteFilterReader.java
new file mode 100644
index 0000000..463987f
--- /dev/null
+++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/json/JsonUrlRewriteFilterReader.java
@@ -0,0 +1,64 @@
+/**
+ * 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.filter.rewrite.impl.json;
+
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor;
+import org.apache.knox.gateway.filter.rewrite.api.UrlRewriter;
+import org.apache.knox.gateway.filter.rewrite.i18n.UrlRewriteMessages;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
+import org.apache.knox.gateway.util.urltemplate.Parser;
+import org.apache.knox.gateway.util.urltemplate.Resolver;
+import org.apache.knox.gateway.util.urltemplate.Template;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.net.URISyntaxException;
+
+public class JsonUrlRewriteFilterReader extends JsonFilterReader {
+
+  private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class );
+
+  private Resolver resolver;
+  private UrlRewriter rewriter;
+  private UrlRewriter.Direction direction;
+
+  public JsonUrlRewriteFilterReader(
+      Reader reader,
+      UrlRewriter rewriter,
+      Resolver resolver,
+      UrlRewriter.Direction direction,
+      UrlRewriteFilterContentDescriptor config )
+          throws IOException {
+    super( reader, config );
+    this.resolver = resolver;
+    this.rewriter = rewriter;
+    this.direction = direction;
+  }
+
+  protected String filterValueString( String name, String value, String rule ) {
+    try {
+      Template input = Parser.parseLiteral( value );
+      Template output = rewriter.rewrite( resolver, input, direction, rule );
+      value = output.getPattern();
+    } catch( URISyntaxException e ) {
+      LOG.failedToParseValueForUrlRewrite( value );
+    }
+    return value;
+  }
+
+}