http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterGroupDescriptorBase.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterGroupDescriptorBase.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterGroupDescriptorBase.java new file mode 100644 index 0000000..bc1dd91 --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterGroupDescriptorBase.java @@ -0,0 +1,52 @@ +/** + * 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; + +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterApplyDescriptor; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterGroupDescriptor; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterPathDescriptor; + +import java.util.ArrayList; +import java.util.List; + +public class UrlRewriteFilterGroupDescriptorBase + extends UrlRewriteFilterSelectorDescriptorBase + implements UrlRewriteFilterGroupDescriptor { + + private List<UrlRewriteFilterPathDescriptor> selectors = new ArrayList<UrlRewriteFilterPathDescriptor>(); + + @Override + public List<UrlRewriteFilterPathDescriptor> getSelectors() { + return selectors; + } + + @Override + public void addSelector( UrlRewriteFilterPathDescriptor selector ) { + this.selectors.add( selector ); + } + + @Override + public UrlRewriteFilterApplyDescriptor addApply( String path, String rule ) { + UrlRewriteFilterApplyDescriptor apply = new UrlRewriteFilterApplyDescriptorImpl(); + apply.path( path ); + apply.rule( rule ); + addSelector( apply ); + return apply; + } + +}
http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterReader.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterReader.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterReader.java new file mode 100644 index 0000000..b512a8c --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterReader.java @@ -0,0 +1,39 @@ +/** + * 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; + +import java.util.regex.Pattern; + +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterPathDescriptor; + + +public interface UrlRewriteFilterReader { + + public String filterValueString( String name, String value, String rule ); + + public 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 ); + } + } + } +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterScopeDescriptorImpl.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterScopeDescriptorImpl.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterScopeDescriptorImpl.java new file mode 100644 index 0000000..bc64870 --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterScopeDescriptorImpl.java @@ -0,0 +1,25 @@ +/** + * 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; + +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterScopeDescriptor; + +public class UrlRewriteFilterScopeDescriptorImpl + extends UrlRewriteFilterGroupDescriptorBase + implements UrlRewriteFilterScopeDescriptor { +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterSelectorDescriptorBase.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterSelectorDescriptorBase.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterSelectorDescriptorBase.java new file mode 100644 index 0000000..3994128 --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFilterSelectorDescriptorBase.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; + +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterPathDescriptor; + +public class UrlRewriteFilterSelectorDescriptorBase<T> implements UrlRewriteFilterPathDescriptor<T> { + + private String path; + private Object compiledPath; + + @Override + public String path() { + return path; + } + + @Override + public T path( String path ) { + this.path = path; + return (T)this; + } + + public void setPath( String path ) { + this.path = path; + } + + public String getPath() { + return path; + } + + @Override + public <C> C compiledPath() { + return (C)compiledPath; + } + + @Override + @SuppressWarnings("unchecked") + public T compiledPath( Object compiledPath ) { + this.compiledPath = compiledPath; + return (T)this; + } + + @Override + public <C> C compiledPath( Compiler<C> compiler ) { + compiledPath = compiler.compile( path, (C)compiledPath ); + return (C)compiledPath; + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFunctionProcessorFactory.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFunctionProcessorFactory.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFunctionProcessorFactory.java new file mode 100644 index 0000000..def7a24 --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteFunctionProcessorFactory.java @@ -0,0 +1,113 @@ +/** + * 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; + +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.spi.UrlRewriteFunctionProcessor; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import java.util.ServiceLoader; + +public abstract class UrlRewriteFunctionProcessorFactory { + + private static final Map<Class<? extends UrlRewriteFunctionDescriptor>,Map<String,Class<? extends UrlRewriteFunctionProcessor>>> MAP + = loadProcessors(); + + private UrlRewriteFunctionProcessorFactory() { + } + + public static UrlRewriteFunctionProcessor create( String name, UrlRewriteFunctionDescriptor descriptor ) + throws IllegalAccessException, InstantiationException { + UrlRewriteFunctionProcessor processor; + if( descriptor == null ) { + descriptor = UrlRewriteFunctionDescriptorFactory.create( name ); + } + Map<String,Class<? extends UrlRewriteFunctionProcessor>> typeMap; + typeMap = MAP.get( descriptor.getClass() ); + if( typeMap == null ) { + Class<? extends UrlRewriteFunctionDescriptor> descriptorInterface = getDescriptorInterface( descriptor ); + typeMap = MAP.get( descriptorInterface ); + } + if( typeMap == null ) { + throw new IllegalArgumentException( descriptor.getClass().getName() ); + } else { + Class<? extends UrlRewriteFunctionProcessor> processorClass = typeMap.get( name ); + if( processorClass == null ) { + throw new IllegalArgumentException( name ); + } else { + processor = processorClass.newInstance(); + } + } + return processor; + } + + private static Map<Class<? extends UrlRewriteFunctionDescriptor>,Map<String,Class<? extends UrlRewriteFunctionProcessor>>> loadProcessors() { + Map<Class<? extends UrlRewriteFunctionDescriptor>,Map<String,Class<? extends UrlRewriteFunctionProcessor>>> descriptorMap + = new HashMap<>(); + ServiceLoader<UrlRewriteFunctionProcessor> processors = ServiceLoader.load( UrlRewriteFunctionProcessor.class ); + for( UrlRewriteFunctionProcessor processor : processors ) { + Class<? extends UrlRewriteFunctionDescriptor> descriptorInterface = getDescriptorInterface( processor ); + Map<String,Class<? extends UrlRewriteFunctionProcessor>> typeMap = descriptorMap.get( descriptorInterface ); + if( typeMap == null ) { + typeMap = new HashMap<>(); + descriptorMap.put( descriptorInterface, typeMap ); + } + String functionName = processor.name(); + typeMap.put( functionName, processor.getClass() ); + } + return descriptorMap; + } + + private static Class<? extends UrlRewriteFunctionDescriptor> getDescriptorInterface( + UrlRewriteFunctionDescriptor descriptor ) { + Class<? extends UrlRewriteFunctionDescriptor> descriptorClass = null; + for( Type interfaceType : descriptor.getClass().getGenericInterfaces() ) { + Class genericClass = (Class)interfaceType; + if( UrlRewriteFunctionDescriptor.class.isAssignableFrom( genericClass ) ) { + descriptorClass = uncheckedDescriptorClassCast( genericClass ); + break; + } + } + return descriptorClass; + } + + private static Class<? extends UrlRewriteFunctionDescriptor> getDescriptorInterface( + UrlRewriteFunctionProcessor processor ) { + Class<? extends UrlRewriteFunctionDescriptor> descriptorClass = null; + Class<? extends UrlRewriteFunctionProcessor> processorClass = processor.getClass(); + for( Type interfaceType : processorClass.getGenericInterfaces() ) { + if( UrlRewriteFunctionProcessor.class.isAssignableFrom( + (Class)((ParameterizedType)interfaceType).getRawType() ) ) { + ParameterizedType interfaceClass = (ParameterizedType)interfaceType; + descriptorClass = uncheckedDescriptorClassCast( interfaceClass.getActualTypeArguments()[ 0 ] ); + break; + } + } + return descriptorClass; + } + + @SuppressWarnings("unchecked") + private static Class<? extends UrlRewriteFunctionDescriptor> uncheckedDescriptorClassCast( Type type ) { + return (Class<? extends UrlRewriteFunctionDescriptor>)type; + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequest.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequest.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequest.java new file mode 100644 index 0000000..f91035c --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequest.java @@ -0,0 +1,265 @@ +/** + * 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; + +import org.apache.knox.gateway.filter.AbstractGatewayFilter; +import org.apache.knox.gateway.filter.GatewayRequestWrapper; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterDescriptor; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRulesDescriptor; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteServletContextListener; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteServletFilter; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteStreamFilterFactory; +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.MimeTypes; +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.activation.MimeType; +import javax.servlet.FilterConfig; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.URISyntaxException; +import java.net.URLDecoder; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.List; + +import static org.apache.knox.gateway.filter.rewrite.impl.UrlRewriteUtil.pickFirstRuleWithEqualsIgnoreCasePathMatch; + +public class UrlRewriteRequest extends GatewayRequestWrapper implements Resolver { + + private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class ); + private static final String[] EMPTY_STRING_ARRAY = new String[]{}; + + private FilterConfig config; + private UrlRewriter rewriter; + private String urlRuleName; + private String bodyFilterName; + private String headersFilterName; + private UrlRewriteFilterContentDescriptor headersFilterConfig; + private String cookiesFilterName; + + /** + * Constructs a request object wrapping the given request. + * + * @throws IllegalArgumentException if the request is null + */ + public UrlRewriteRequest( FilterConfig config, HttpServletRequest request ) throws IOException { + super( request ); + this.config = config; + this.rewriter = UrlRewriteServletContextListener.getUrlRewriter( config.getServletContext() ); + this.urlRuleName = config.getInitParameter( UrlRewriteServletFilter.REQUEST_URL_RULE_PARAM ); + this.bodyFilterName = config.getInitParameter( UrlRewriteServletFilter.REQUEST_BODY_FILTER_PARAM ); + this.headersFilterName = config.getInitParameter( UrlRewriteServletFilter.REQUEST_HEADERS_FILTER_PARAM ); + this.headersFilterConfig = getRewriteFilterConfig( headersFilterName, UrlRewriteServletFilter.HEADERS_MIME_TYPE ); + this.cookiesFilterName = config.getInitParameter( UrlRewriteServletFilter.REQUEST_COOKIES_FILTER_PARAM ); + } + + private Template getSourceUrl() { + Template urlTemplate; + //KNOX-439[ + //StringBuffer urlString = super.getRequestURL(); + StringBuffer urlString = new StringBuffer( 128 ); + urlString.append( getScheme() ); + urlString.append( "://" ); + urlString.append( getServerName() ); + urlString.append( ":" ); + urlString.append( getServerPort() ); + urlString.append( super.getRequestURI() ); + //] + String queryString = super.getQueryString(); + if( queryString != null ) { + urlString.append( '?' ); + urlString.append( queryString ); + } + try { + urlTemplate = Parser.parseLiteral( urlString.toString() ); + } catch( URISyntaxException e ) { + LOG.failedToParseValueForUrlRewrite( urlString.toString() ); + // Shouldn't be possible given that the URL is constructed from parts of an existing URL. + urlTemplate = null; + } + return urlTemplate; + } + + // Note: Source url was added to the request attributes by the GatewayFilter doFilter method. + private Template getTargetUrl() { + boolean rewriteRequestUrl = true; + Template targetUrl; + if( rewriteRequestUrl ) { + targetUrl = (Template)getAttribute( AbstractGatewayFilter.TARGET_REQUEST_URL_ATTRIBUTE_NAME ); + if( targetUrl == null ) { + Template sourceUrl = getSourceUrl(); + targetUrl = rewriter.rewrite( this, sourceUrl, UrlRewriter.Direction.IN, urlRuleName ); + setAttribute( AbstractGatewayFilter.TARGET_REQUEST_URL_ATTRIBUTE_NAME, targetUrl ); + } + } else { + targetUrl = (Template)getAttribute( AbstractGatewayFilter.SOURCE_REQUEST_URL_ATTRIBUTE_NAME ); + } + return targetUrl; + } + + private String[] splitTargetUrl( Template url ) { + if( url == null ) { + return EMPTY_STRING_ARRAY; + } else { + String s = url.toString(); + return s.split( "\\?" ); + } + } + + @Override + public StringBuffer getRequestURL() { + return new StringBuffer( getRequestURI() ); + } + + //TODO: I think this method is implemented wrong based on the HttpServletRequest.getRequestURI docs. + // It should not include the scheme or authority parts. + @Override + public String getRequestURI() { + String[] split = splitTargetUrl( getTargetUrl() ); + if( split.length > 0 ) { + return split[0]; + } else { + return ""; + } + } + + @Override + public String getQueryString() { + String[] split = splitTargetUrl( getTargetUrl() ); + if( split.length > 1 ) { + try { + return URLDecoder.decode(split[1], "UTF-8"); + } catch ( UnsupportedEncodingException e ) { + LOG.failedToDecodeQueryString(split[1], e); + return split[1]; + } + } else { + return null; + } + } + + private String rewriteValue( UrlRewriter rewriter, String value, String rule ) { + try { + Template input = Parser.parseLiteral( value ); + Template output = rewriter.rewrite( this, input, UrlRewriter.Direction.IN, rule ); + value = output.getPattern(); + } catch( URISyntaxException e ) { + LOG.failedToParseValueForUrlRewrite( value ); + } + return value; + } + + @Override + public String getHeader( String name ) { + String value = super.getHeader( name ); + if( value != null ) { + value = rewriteValue( rewriter, super.getHeader( name ), pickFirstRuleWithEqualsIgnoreCasePathMatch( headersFilterConfig, name ) ); + } + return value; + } + + @SuppressWarnings("unchecked") + public Enumeration getHeaders( String name ) { + return new EnumerationRewriter( rewriter, super.getHeaders( name ), pickFirstRuleWithEqualsIgnoreCasePathMatch( headersFilterConfig, name ) ); + } + + @Override + public List<String> resolve( String name ) { + return Arrays.asList( config.getInitParameter( name ) ); + } + + private class EnumerationRewriter implements Enumeration<String> { + + private UrlRewriter rewriter; + private Enumeration<String> delegate; + private String rule; + + private EnumerationRewriter( UrlRewriter rewriter, Enumeration<String> delegate, String rule ) { + this.rewriter = rewriter; + this.delegate = delegate; + this.rule = rule; + } + + @Override + public boolean hasMoreElements() { + return delegate.hasMoreElements(); + } + + @Override + public String nextElement() { + return rewriteValue( rewriter, delegate.nextElement(), rule ); + } + } + + @Override + public ServletInputStream getInputStream() throws IOException { + ServletInputStream input = super.getInputStream(); + if( getContentLength() != 0 ) { + MimeType mimeType = getMimeType(); + UrlRewriteFilterContentDescriptor filterContentConfig = getRewriteFilterConfig( bodyFilterName, mimeType ); + if (filterContentConfig != null) { + String asType = filterContentConfig.asType(); + if ( asType != null && asType.trim().length() > 0 ) { + mimeType = MimeTypes.create(asType, getCharacterEncoding()); + } + } + InputStream stream = UrlRewriteStreamFilterFactory.create( mimeType, null, input, rewriter, this, UrlRewriter.Direction.IN, filterContentConfig ); + input = new UrlRewriteRequestStream( stream ); + } + return input; + } + + @Override + public BufferedReader getReader() throws IOException { + return new BufferedReader( new InputStreamReader( getInputStream(), getCharacterEncoding() ) ); + } + + @Override + public int getContentLength() { + // The rewrite might change the content length so return the default of -1 to indicate the length is unknown. + int contentLength = super.getContentLength(); + if( contentLength > 0 ) { + contentLength = -1; + } + return contentLength; + } + + private UrlRewriteFilterContentDescriptor getRewriteFilterConfig( String filterName, MimeType mimeType ) { + UrlRewriteFilterContentDescriptor filterContentConfig = null; + UrlRewriteRulesDescriptor rewriteConfig = rewriter.getConfig(); + if( rewriteConfig != null ) { + UrlRewriteFilterDescriptor filterConfig = rewriteConfig.getFilter( filterName ); + if( filterConfig != null ) { + filterContentConfig = filterConfig.getContent( mimeType ); + } + } + return filterContentConfig; + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequestStream.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequestStream.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequestStream.java new file mode 100644 index 0000000..5995317 --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRequestStream.java @@ -0,0 +1,40 @@ +/** + * 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; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.knox.gateway.servlet.SynchronousServletInputStreamAdapter; + +//TODO: This needs to be coded much more efficiently! +public class UrlRewriteRequestStream extends + SynchronousServletInputStreamAdapter { + + private InputStream stream; + + public UrlRewriteRequestStream( InputStream stream ) { + this.stream = stream; + } + + @Override + public int read() throws IOException { + return stream.read(); + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponse.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponse.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponse.java new file mode 100644 index 0000000..d451c26 --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponse.java @@ -0,0 +1,331 @@ +/** + * 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; + +import org.apache.knox.gateway.filter.GatewayResponseWrapper; +import org.apache.knox.gateway.filter.ResponseStreamer; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterContentDescriptor; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteServletContextListener; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteServletFilter; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteStreamFilterFactory; +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.MimeTypes; +import org.apache.knox.gateway.util.Urls; +import org.apache.knox.gateway.util.urltemplate.Params; +import org.apache.knox.gateway.util.urltemplate.Parser; +import org.apache.knox.gateway.util.urltemplate.Template; +import org.apache.commons.io.IOUtils; + +import javax.activation.MimeType; +import javax.servlet.FilterConfig; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.URISyntaxException; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import java.util.zip.ZipException; + +import static org.apache.knox.gateway.filter.rewrite.impl.UrlRewriteUtil.getRewriteFilterConfig; +import static org.apache.knox.gateway.filter.rewrite.impl.UrlRewriteUtil.pickFirstRuleWithEqualsIgnoreCasePathMatch; + +/** + * + */ +public class UrlRewriteResponse extends GatewayResponseWrapper implements Params, + ResponseStreamer { + + private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class ); + + // An 8K buffer better matches the underlying buffer sizes. + // Testing with 16K made no appreciable difference. + private static final int STREAM_BUFFER_SIZE = 8 * 1024; + + private static final Set<String> IGNORE_HEADER_NAMES = new HashSet<>(); + static { + IGNORE_HEADER_NAMES.add( "Content-Length" ); + } + + private static final String REQUEST_PARAM_PREFIX = "request."; + private static final String CLUSTER_PARAM_PREFIX = "cluster."; + private static final String GATEWAY_PARAM_PREFIX = "gateway."; + public static final String INBOUND_QUERY_PARAM_PREFIX = "query.param."; + + private UrlRewriter rewriter; + private FilterConfig config; + private HttpServletRequest request; + private HttpServletResponse response; + private ServletOutputStream output; + private String bodyFilterName; + private String headersFilterName; + private UrlRewriteFilterContentDescriptor headersFilterConfig; + private String cookiesFilterName; + private String xForwardedHostname; + private String xForwardedPort; + private String xForwardedScheme; + + public UrlRewriteResponse( FilterConfig config, HttpServletRequest request, HttpServletResponse response ) + throws IOException { + super( response ); + this.rewriter = UrlRewriteServletContextListener.getUrlRewriter( config.getServletContext() ); + this.config = config; + this.request = request; + this.response = response; + this.output = null; + getXForwardedHeaders(); + this.bodyFilterName = config.getInitParameter( UrlRewriteServletFilter.RESPONSE_BODY_FILTER_PARAM ); + this.headersFilterName = config.getInitParameter( UrlRewriteServletFilter.RESPONSE_HEADERS_FILTER_PARAM ); + this.headersFilterConfig = getRewriteFilterConfig( rewriter.getConfig(), headersFilterName, UrlRewriteServletFilter.HEADERS_MIME_TYPE ); + this.cookiesFilterName = config.getInitParameter( UrlRewriteServletFilter.RESPONSE_COOKIES_FILTER_PARAM ); + } + + protected boolean ignoreHeader( String name ) { + return IGNORE_HEADER_NAMES.contains( name ); + } + + private String rewriteValue( String value, String rule ) { + try { + Template input = Parser.parseLiteral( value ); + Template output = rewriter.rewrite( this, input, UrlRewriter.Direction.OUT, rule ); + if( output != null ) { + value = output.toString(); + } + } catch( URISyntaxException e ) { + LOG.failedToParseValueForUrlRewrite( value ); + } + return value; + } + + // Ignore the Content-Length from the dispatch respond since the respond body may be rewritten. + @Override + public void setHeader( String name, String value ) { + if( !ignoreHeader( name) ) { + value = rewriteValue( value, pickFirstRuleWithEqualsIgnoreCasePathMatch( headersFilterConfig, name ) ); + super.setHeader( name, value ); + } + } + + // Ignore the Content-Length from the dispatch respond since the respond body may be rewritten. + @Override + public void addHeader( String name, String value ) { + if( !ignoreHeader( name ) ) { + String rule = pickFirstRuleWithEqualsIgnoreCasePathMatch( headersFilterConfig, name ); + value = rewriteValue( value, rule ); + super.addHeader( name, value ); + } + } + + @Override + public OutputStream getRawOutputStream() throws IOException { + return response.getOutputStream(); + } + + @Override + public void streamResponse( InputStream input, OutputStream output ) throws IOException { + InputStream inStream; + OutputStream outStream; + boolean isGzip = false; + BufferedInputStream inBuffer = new BufferedInputStream(input); + try { + // Use this way to check whether the input stream is gzip compressed, in case + // the content encoding header is unknown, as it could be unset in inbound response + inBuffer.mark(STREAM_BUFFER_SIZE); + inStream = new GZIPInputStream(inBuffer); + isGzip = true; + } catch (ZipException e) { + inBuffer.reset(); + inStream = inBuffer; + } catch (IOException e) { + inBuffer.reset(); + inStream = inBuffer; + } + + MimeType mimeType = getMimeType(); + UrlRewriteFilterContentDescriptor filterContentConfig = + getRewriteFilterConfig( rewriter.getConfig(), bodyFilterName, mimeType ); + if (filterContentConfig != null) { + String asType = filterContentConfig.asType(); + if ( asType != null && asType.trim().length() > 0 ) { + mimeType = MimeTypes.create(asType, getCharacterEncoding()); + } + } + InputStream filteredInput = UrlRewriteStreamFilterFactory.create( + mimeType, null, inStream, rewriter, this, UrlRewriter.Direction.OUT, filterContentConfig ); + outStream = (isGzip) ? new GZIPOutputStream(output) : output; + IOUtils.copyLarge( filteredInput, outStream, new byte[STREAM_BUFFER_SIZE] ); + //KNOX-685: outStream.flush(); + outStream.close(); + } + + //TODO: Need to buffer the output here and when it is closed, rewrite it and then write the result to the stream. + // This should only happen if the caller isn't using the streaming model. + @Override + public ServletOutputStream getOutputStream() throws IOException { + if( output == null ) { + output = new UrlRewriteResponseStream( this ); + } + return output; + } + + @Override + public Set<String> getNames() { + return Collections.emptySet(); + } + + @Override + @SuppressWarnings( "unchecked" ) + public List<String> resolve( String name ) { + if( name.startsWith( REQUEST_PARAM_PREFIX ) ) { + return Arrays.asList( getRequestParam( name.substring( REQUEST_PARAM_PREFIX.length() ) ) ); + } else if ( name.startsWith( GATEWAY_PARAM_PREFIX ) ) { + return Arrays.asList( getGatewayParam( name.substring( GATEWAY_PARAM_PREFIX.length() ) ) ); + } else if ( name.startsWith( CLUSTER_PARAM_PREFIX ) ) { + return Arrays.asList( getClusterParam( name.substring( GATEWAY_PARAM_PREFIX.length() ) ) ); + } else if ( name.startsWith( INBOUND_QUERY_PARAM_PREFIX ) ) { + return getInboundQueryParam(name.substring(INBOUND_QUERY_PARAM_PREFIX.length())); + } else { + return Arrays.asList( config.getInitParameter( name ) ); + } + } + + // KNOX-464: Doing this because Jetty only returns the string version of the IP address for request.getLocalName(). + // Hopefully the local hostname will be cached so this will not be a significant performance hit. + // Previously this was an inline request.getServerName() but this ended up mixing the hostname from the Host header + // and the local port which was making load balancer configuration difficult if not impossible. + private String getRequestLocalHostName() { + String hostName = request.getLocalName(); + try { + hostName = InetAddress.getByName( hostName ).getHostName(); + } catch( UnknownHostException e ) { + // Ignore it and use the original hostname. + } + return hostName; + } + + private String getGatewayParam( String name ) { + if( "url".equals( name ) ) { + if( xForwardedPort == null ) { + return xForwardedScheme + "://" + xForwardedHostname + request.getContextPath(); + } else { + return xForwardedScheme + "://" + xForwardedHostname + ":" + xForwardedPort + request.getContextPath(); + } + } else if( "scheme".equals( name ) ) { + return xForwardedScheme; + } else if( "host".equals( name ) ) { + return xForwardedHostname; + } else if( "port".equals( name ) ) { + return xForwardedPort; + } else if( "addr".equals( name ) || "address".equals( name ) ) { + if( xForwardedPort == null ) { + return xForwardedHostname; + } else { + return xForwardedHostname + ":" + xForwardedPort; + } + } else if( "path".equals( name ) ) { + return request.getContextPath(); + } else { + return null; + } + } + + private String getClusterParam( String name ) { + if( "name".equals( name ) ) { + return config.getServletContext().getServletContextName(); + } else { + return null; + } + } + + private List <String> getInboundQueryParam(String name ){ + List <String> inboundHosts = null; + if( this.request!=null ) + inboundHosts = + Arrays.asList( this.request.getParameterValues(name)); + return inboundHosts; + } + + private String getRequestParam( String name ) { + if( "host".equals( name ) ) { + return request.getServerName(); + } else if ( "port".equals( name ) ) { + return Integer.toString( request.getLocalPort() ); + } else if ( "scheme".equals( name ) ) { + return request.getScheme(); + } else if ( "context-path".equals( name ) ) { + return Urls.stripLeadingSlash( request.getContextPath() ); + } else { + config.getServletContext().getServletContextName(); + return null; + } + } + + @SuppressWarnings("deprecation") + public String encodeUrl( String url ) { + return this.encodeURL( url ); + } + + //TODO: Route these through the rewriter. + public String encodeURL( String url ) { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + public String encodeRedirectUrl( String url ) { + return this.encodeRedirectURL( url ); + } + + //TODO: Route these through the rewriter. + public String encodeRedirectURL( String url ) { + throw new UnsupportedOperationException(); + } + + private void getXForwardedHeaders() { + xForwardedHostname = request.getHeader( "X-Forwarded-Host" ); + xForwardedPort = request.getHeader( "X-Forwarded-Port" ); + xForwardedScheme = request.getHeader( "X-Forwarded-Proto" ); + if ( xForwardedScheme == null ) { + xForwardedScheme = request.getScheme(); + } + if ( xForwardedHostname != null ) { + int separator = xForwardedHostname.indexOf( ":" ); + if ( separator > 0 ) { + //a specific port in the forwarded host wins + xForwardedPort = xForwardedHostname.substring(separator + 1, xForwardedHostname.length()); + xForwardedHostname = xForwardedHostname.substring( 0, separator ); + } + } else { + xForwardedHostname = getRequestLocalHostName(); + xForwardedPort = Integer.toString( request.getLocalPort() ); + } + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponseStream.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponseStream.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponseStream.java new file mode 100644 index 0000000..300a8b4 --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteResponseStream.java @@ -0,0 +1,54 @@ +/** + * 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; + +import org.apache.knox.gateway.filter.GatewayResponse; +import org.apache.knox.gateway.servlet.SynchronousServletOutputStreamAdapter; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +//TODO: This needs to be coded much more efficiently! +public class UrlRewriteResponseStream extends + SynchronousServletOutputStreamAdapter { + + private static final int DEFAULT_BUFFER_SIZE = 1024; + + private GatewayResponse response; + private ByteArrayOutputStream buffer; + + public UrlRewriteResponseStream( GatewayResponse response ) { + this.response = response; + this.buffer = new ByteArrayOutputStream( DEFAULT_BUFFER_SIZE ); + } + + @Override + public void write( int b ) throws IOException { + buffer.write( b ); + } + + @Override + public void close() throws IOException { + InputStream stream = new ByteArrayInputStream( buffer.toByteArray() ); + response.streamResponse( stream ) ; + stream.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/UrlRewriteRuleDescriptorImpl.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleDescriptorImpl.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleDescriptorImpl.java new file mode 100644 index 0000000..d86c670 --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleDescriptorImpl.java @@ -0,0 +1,195 @@ +/** + * 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; + +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRuleDescriptor; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteStepDescriptor; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriter; +import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteFlowDescriptorBase; +import org.apache.knox.gateway.util.urltemplate.Parser; +import org.apache.knox.gateway.util.urltemplate.Template; + +import java.net.URISyntaxException; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; + +public class UrlRewriteRuleDescriptorImpl extends UrlRewriteFlowDescriptorBase<UrlRewriteRuleDescriptor> + implements UrlRewriteRuleDescriptor { + + private String name; + private String scope; + private String pattern; + private Template template; + private EnumSet<UrlRewriter.Direction> directions; + + public UrlRewriteRuleDescriptorImpl() { + super( "rule" ); + } + + @Override + public String name() { + return this.name; + } + + @Override + public UrlRewriteRuleDescriptor name( String name ) { + this.name = name; + return this; + } + + public void setName( String name ) { + name( name ); + } + + public String getName() { + return name; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + scope( scope ); + } + + @Override + public String scope() { + return scope; + } + + @Override + public UrlRewriteStepDescriptor scope( String scope ) { + this.scope = scope; + return this; + } + + @Override + public EnumSet<UrlRewriter.Direction> directions() { + return directions; + } + + @Override + public UrlRewriteRuleDescriptor directions( String directions ) { + this.directions = parseDirections( directions ); + return this; + } + + public void setDirections( String directions ) { + directions( directions ); + } + + public void setDirection( String directions ) { + directions( directions ); + } + + public void setDir( String directions ) { + directions( directions ); + } + + public String getDir() { + String s = null; + if( directions != null ) { + StringBuilder sb = new StringBuilder(); + for( UrlRewriter.Direction direction: directions ) { + if( sb.length() > 0 ) { + sb.append( ',' ); + } + sb.append( direction.toString() ); + } + s = sb.toString(); + } + return s; + } + + @Override + public UrlRewriteRuleDescriptor directions( UrlRewriter.Direction... directions ) { + return this; + } + + @Override + public String pattern() { + return pattern; + } + + @Override + public UrlRewriteRuleDescriptor pattern( String pattern ) throws URISyntaxException { + this.pattern = pattern; + this.template = Parser.parseTemplate( pattern ); + return this; + } + + public void setPattern( String pattern ) throws URISyntaxException { + pattern( pattern ); + } + + public void setUrl( String pattern ) throws URISyntaxException { + pattern( pattern ); + } + + public String getPattern() { + return pattern(); + } + + @Override + public Template template() { + return template; + } + + @Override + public UrlRewriteRuleDescriptor template( Template template ) { + this.template = template; + this.pattern = template.toString(); + return this; + } + + private static EnumSet<UrlRewriter.Direction> parseDirections( String directions ) { + EnumSet<UrlRewriter.Direction> set = EnumSet.noneOf( UrlRewriter.Direction.class ); + StringTokenizer parser = new StringTokenizer( directions, " ,;:/|+" ); + while( parser.hasMoreTokens() ) { + UrlRewriter.Direction direction = parseDirection( parser.nextToken() ); + if( direction != null ) { + set.add( direction ); + } + } + return set; + } + + private static UrlRewriter.Direction parseDirection( String direction ) { + direction = direction.trim().toLowerCase(); + return directionNameMap.get( direction ); + } + + private static Map<String,UrlRewriter.Direction> directionNameMap = new HashMap<>(); + static { + directionNameMap.put( "inbound", UrlRewriter.Direction.IN ); + directionNameMap.put( "in", UrlRewriter.Direction.IN ); + directionNameMap.put( "i", UrlRewriter.Direction.IN ); + directionNameMap.put( "request", UrlRewriter.Direction.IN ); + directionNameMap.put( "req", UrlRewriter.Direction.IN ); + + directionNameMap.put( "outbound", UrlRewriter.Direction.OUT ); + directionNameMap.put( "out", UrlRewriter.Direction.OUT ); + directionNameMap.put( "o", UrlRewriter.Direction.OUT ); + directionNameMap.put( "response", UrlRewriter.Direction.OUT ); + directionNameMap.put( "res", UrlRewriter.Direction.OUT ); + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorHolder.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorHolder.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorHolder.java new file mode 100644 index 0000000..1c3fb11 --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorHolder.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; + +import org.apache.knox.gateway.config.GatewayConfig; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteEnvironment; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRuleDescriptor; +import org.apache.knox.gateway.filter.rewrite.ext.ScopedMatcher; + +import java.util.List; + +public class UrlRewriteRuleProcessorHolder extends UrlRewriteStepProcessorHolder { + + private String ruleName; + + private String scope; + + public void initialize( UrlRewriteEnvironment environment, UrlRewriteRuleDescriptor descriptor ) throws Exception { + super.initialize( environment, descriptor ); + ruleName = descriptor.name(); + //if a scope is set in the rewrite file, use that + if (descriptor.scope() != null) { + scope = descriptor.scope(); + } else { + //by convention the name of the rules start with ROLENAME/servicename/direction + //use the first part of the name to determine the scope, therefore setting the scope of a rule to + //be local to that service + int slashIndex = ruleName.indexOf('/'); + if (slashIndex > 0) { + scope = ruleName.substring( 0, slashIndex ); + } + //check config to see if the is an override configuration for a given service to have all its rules set to global + GatewayConfig gatewayConfig = environment.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE); + if (gatewayConfig != null) { + List<String> globalRulesServices = gatewayConfig.getGlobalRulesServices(); + if ( globalRulesServices.contains(scope) ) { + scope = ScopedMatcher.GLOBAL_SCOPE; + } + } + } + } + + public String getRuleName() { + return ruleName; + } + + public String getScope() { + return scope; + } +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorImpl.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorImpl.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorImpl.java new file mode 100644 index 0000000..e02ad23 --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRuleProcessorImpl.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; + +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteEnvironment; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRuleDescriptor; +import org.apache.knox.gateway.filter.rewrite.ext.UrlRewriteMatchDescriptor; +import org.apache.knox.gateway.filter.rewrite.ext.UrlRewriteMatchDescriptorExt; +import org.apache.knox.gateway.filter.rewrite.ext.UrlRewriteMatchProcessorExt; +import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteContext; +import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteStepProcessor; +import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteStepStatus; + +public class UrlRewriteRuleProcessorImpl implements + UrlRewriteStepProcessor<UrlRewriteRuleDescriptor> { + + private UrlRewriteMatchProcessorExt matchProcessor; + + @Override + public String getType() { + return "rule"; + } + + @Override + public void initialize( UrlRewriteEnvironment environment, UrlRewriteRuleDescriptor descriptor ) throws Exception { + UrlRewriteMatchDescriptor matchDescriptor = new UrlRewriteMatchDescriptorExt(); + matchDescriptor.operation( "matches" ); + matchDescriptor.flow( descriptor.flow() ); + matchDescriptor.template( descriptor.template() ); + matchProcessor = new UrlRewriteMatchProcessorExt(); + matchProcessor.initialize( environment, matchDescriptor ); + } + + @Override + public UrlRewriteStepStatus process( UrlRewriteContext context ) throws Exception { + return matchProcessor.process( context ); + } + + @Override + public void destroy() { + matchProcessor.destroy(); + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRulesDescriptorImpl.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRulesDescriptorImpl.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRulesDescriptorImpl.java new file mode 100644 index 0000000..86dbc2a --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteRulesDescriptorImpl.java @@ -0,0 +1,143 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.knox.gateway.filter.rewrite.impl; + +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterDescriptor; +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.api.UrlRewriteRuleDescriptor; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRulesDescriptor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class UrlRewriteRulesDescriptorImpl implements UrlRewriteRulesDescriptor { + + private Map<String,UrlRewriteFunctionDescriptor> funcMap = new HashMap<>(); + private List<UrlRewriteFunctionDescriptor> funcList = new ArrayList<UrlRewriteFunctionDescriptor>(); + private List<UrlRewriteRuleDescriptor> ruleList = new ArrayList<UrlRewriteRuleDescriptor>(); + private Map<String,UrlRewriteRuleDescriptor> ruleMap = new HashMap<>(); + private List<UrlRewriteFilterDescriptor> filterList = new ArrayList<UrlRewriteFilterDescriptor>(); + private Map<String,UrlRewriteFilterDescriptor> filterMap = new HashMap<>(); + + @Override + public void addRules( UrlRewriteRulesDescriptor rules ) { + for( UrlRewriteRuleDescriptor rule : rules.getRules() ) { + addRule( rule ); + } + for( UrlRewriteFilterDescriptor filter : rules.getFilters() ) { + addFilter( filter ); + } + } + + @Override + public UrlRewriteRuleDescriptor getRule( String name ) { + return ruleMap.get( name ); + } + + @Override + public List<UrlRewriteRuleDescriptor> getRules() { + return ruleList; + } + + @Override + public UrlRewriteRuleDescriptor addRule( String name ) { + UrlRewriteRuleDescriptor rule = newRule(); + rule.name( name ); + addRule( rule ); + return rule; + } + + @Override + public UrlRewriteRuleDescriptor newRule() { + return new UrlRewriteRuleDescriptorImpl(); + } + + @Override + public void addRule( UrlRewriteRuleDescriptor rule ) { + ruleList.add( rule ); + String name = rule.name(); + if( name != null && name.length() > 0 ) { + ruleMap.put( rule.name(), rule ); + } + } + + @Override + public List<UrlRewriteFunctionDescriptor> getFunctions() { + return funcList; + } + + @Override + @SuppressWarnings("unchecked") + public <T extends UrlRewriteFunctionDescriptor<?>> T getFunction( String name ) { + T descriptor = (T)funcMap.get( name ); + return (T)descriptor; + } + + @Override + @SuppressWarnings("unchecked") + public <T extends UrlRewriteFunctionDescriptor<?>> T addFunction( String name ) { + T descriptor = (T)newFunction( name ); + addFunction( descriptor ); + return (T)descriptor; + } + + @SuppressWarnings("unchecked") + protected <T extends UrlRewriteFunctionDescriptor<?>> T newFunction( String name ) { + T descriptor = (T)UrlRewriteFunctionDescriptorFactory.create( name ); + return (T)descriptor; + } + + protected void addFunction( UrlRewriteFunctionDescriptor descriptor ) { + funcList.add( descriptor ); + funcMap.put( descriptor.name(), descriptor ); + } + + + @Override + public List<UrlRewriteFilterDescriptor> getFilters() { + return filterList; + } + + @Override + public UrlRewriteFilterDescriptor getFilter( String name ) { + return filterMap.get( name ); + } + + @Override + public UrlRewriteFilterDescriptor newFilter() { + return new UrlRewriteFilterDescriptorImpl(); + } + + @Override + public UrlRewriteFilterDescriptor addFilter( String name ) { + UrlRewriteFilterDescriptor filter = newFilter(); + filter.name( name ); + addFilter( filter ); + return filter; + } + + @Override + public void addFilter( UrlRewriteFilterDescriptor descriptor ) { + filterList.add( descriptor ); + filterMap.put( descriptor.name(), descriptor ); + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorFactory.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorFactory.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorFactory.java new file mode 100644 index 0000000..7d88f8b --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorFactory.java @@ -0,0 +1,106 @@ +/** + * 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; + +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteStepDescriptor; +import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteStepProcessor; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import java.util.ServiceLoader; + +public abstract class UrlRewriteStepProcessorFactory { + + private static final Map<Class<? extends UrlRewriteStepDescriptor>,Map<String,Class<? extends UrlRewriteStepProcessor>>> MAP + = loadStepProcessors(); + + private UrlRewriteStepProcessorFactory() { + } + + public static UrlRewriteStepProcessor create( UrlRewriteStepDescriptor descriptor ) throws IllegalAccessException, InstantiationException { + UrlRewriteStepProcessor processor; + Map<String,Class<? extends UrlRewriteStepProcessor>> typeMap; + typeMap = MAP.get( descriptor.getClass() ); + if( typeMap == null ) { + Class<? extends UrlRewriteStepDescriptor> descriptorInterface = getDescriptorInterface( descriptor ); + typeMap = MAP.get( descriptorInterface ); + } + if( typeMap == null ) { + throw new IllegalArgumentException( descriptor.getClass().getName() ); + } else { + String type = descriptor.type(); + Class<? extends UrlRewriteStepProcessor> processorClass = typeMap.get( type ); + if( processorClass == null ) { + throw new IllegalArgumentException( type ); + } else { + processor = processorClass.newInstance(); + } + } + return processor; + } + + private static Map<Class<? extends UrlRewriteStepDescriptor>,Map<String,Class<? extends UrlRewriteStepProcessor>>> loadStepProcessors() { + Map<Class<? extends UrlRewriteStepDescriptor>,Map<String,Class<? extends UrlRewriteStepProcessor>>> descriptorMap + = new HashMap<>(); + ServiceLoader<UrlRewriteStepProcessor> processors = ServiceLoader.load( UrlRewriteStepProcessor.class ); + for( UrlRewriteStepProcessor processor : processors ) { + Class<? extends UrlRewriteStepDescriptor> descriptorInterface = getDescriptorInterface( processor ); + Map<String,Class<? extends UrlRewriteStepProcessor>> typeMap = descriptorMap.get( descriptorInterface ); + if( typeMap == null ) { + typeMap = new HashMap<>(); + descriptorMap.put( descriptorInterface, typeMap ); + } + String processorType = processor.getType(); + typeMap.put( processorType, processor.getClass() ); + } + return descriptorMap; + } + + private static Class<? extends UrlRewriteStepDescriptor> getDescriptorInterface( UrlRewriteStepDescriptor descriptor ) { + Class<? extends UrlRewriteStepDescriptor> descriptorClass = null; + for( Type interfaceType : descriptor.getClass().getGenericInterfaces() ) { + Class genericClass = (Class)interfaceType; + if( UrlRewriteStepDescriptor.class.isAssignableFrom( genericClass ) ) { + descriptorClass = uncheckedStepDescriptorClassCast( genericClass ); + break; + } + } + return descriptorClass; + } + + private static Class<? extends UrlRewriteStepDescriptor> getDescriptorInterface( UrlRewriteStepProcessor processor ) { + Class<? extends UrlRewriteStepDescriptor> descriptorClass = null; + Class<? extends UrlRewriteStepProcessor> processorClass = processor.getClass(); + for( Type interfaceType : processorClass.getGenericInterfaces() ) { + if( UrlRewriteStepProcessor.class.isAssignableFrom( (Class)((ParameterizedType)interfaceType).getRawType() ) ) { + ParameterizedType interfaceClass = (ParameterizedType)interfaceType; + descriptorClass = uncheckedStepDescriptorClassCast( interfaceClass.getActualTypeArguments()[ 0 ] ); + break; + } + } + return descriptorClass; + } + + @SuppressWarnings("unchecked") + private static Class<? extends UrlRewriteStepDescriptor> uncheckedStepDescriptorClassCast( Type type ) { + return (Class<? extends UrlRewriteStepDescriptor>)type; + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorHolder.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorHolder.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorHolder.java new file mode 100644 index 0000000..1d4811c --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorHolder.java @@ -0,0 +1,233 @@ +/** + * 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; + +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteEnvironment; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFlowDescriptor; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteStepDescriptor; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteStepFlow; +import org.apache.knox.gateway.filter.rewrite.i18n.UrlRewriteMessages; +import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteContext; +import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteStepProcessor; +import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteStepStatus; +import org.apache.knox.gateway.i18n.messages.MessagesFactory; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class UrlRewriteStepProcessorHolder implements UrlRewriteStepProcessor { + + private static final UrlRewriteMessages LOG = MessagesFactory.get( UrlRewriteMessages.class ); + + private boolean isCondition; + private UrlRewriteStepDescriptor descriptor; + private UrlRewriteStepProcessor processor; + private List<UrlRewriteStepProcessorHolder> childProcessors; + + @Override + public String getType() { + return "system"; + } + + public boolean isCondition() { + return isCondition; + } + + public boolean isAction() { + return !isCondition; + } + + @Override + @SuppressWarnings( "unchecked" ) + public void initialize( UrlRewriteEnvironment environment, UrlRewriteStepDescriptor descriptor ) throws Exception { + UrlRewriteStepProcessor processor = UrlRewriteStepProcessorFactory.create( descriptor ); + processor.initialize( environment, descriptor ); + initialize( environment, descriptor, processor ); + } + + // For unit testing. + @SuppressWarnings("unchecked") + void initialize( UrlRewriteEnvironment environment, UrlRewriteStepDescriptor descriptor, UrlRewriteStepProcessor processor ) throws Exception { + this.descriptor = descriptor; + this.processor = processor; + this.isCondition = descriptor instanceof UrlRewriteFlowDescriptor; + this.childProcessors = new ArrayList<UrlRewriteStepProcessorHolder>(); + if( isCondition ) { + UrlRewriteFlowDescriptor flowDescriptor = (UrlRewriteFlowDescriptor)descriptor; + List<UrlRewriteStepDescriptor> stepList = flowDescriptor.steps(); + if( stepList != null && !stepList.isEmpty() ) { + Iterator<UrlRewriteStepDescriptor> stepIterator = stepList.iterator(); + while( stepIterator.hasNext() ) { + UrlRewriteStepDescriptor stepDescriptor = stepIterator.next(); + UrlRewriteStepProcessorHolder stepProcessor = new UrlRewriteStepProcessorHolder(); + stepProcessor.initialize( environment, stepDescriptor ); + childProcessors.add( stepProcessor ); + } + } + } + } + + // For unit testing. + UrlRewriteStepDescriptor getDescriptor() { + return descriptor; + } + + // For unit testing. + UrlRewriteStepProcessor getProcessor() { + return processor; + } + + @Override + public UrlRewriteStepStatus process( UrlRewriteContext context ) throws Exception { + UrlRewriteStepStatus status = UrlRewriteStepStatus.SUCCESS; + // If initialization failed then fail processing + if( processor != null ) { + status = processor.process( context ); + if( UrlRewriteStepStatus.SUCCESS == status && + descriptor instanceof UrlRewriteFlowDescriptor && + !childProcessors.isEmpty() ) { + UrlRewriteFlowDescriptor flowDescriptor = (UrlRewriteFlowDescriptor)descriptor; + UrlRewriteStepFlow flow = flowDescriptor.flow(); + if( flow == null ) { + flow = UrlRewriteStepFlow.AND; + } + switch( flow ) { + case ALL: + return processAllFlow( context ); + case AND: + return processAndFlow( context ); + case OR: + return processOrFlow( context ); + } + } + } + return status; + } + + private UrlRewriteStepStatus processAllFlow( UrlRewriteContext context ) throws Exception { + UrlRewriteStepProcessorState state = new UrlRewriteStepProcessorState( childProcessors.iterator() ); + UrlRewriteStepStatus stepStatus = UrlRewriteStepStatus.SUCCESS; + UrlRewriteStepProcessorHolder step; + while( state.hasNext() ) { + while( state.hasNextCondition() ) { + step = state.nextCondition( stepStatus ); + stepStatus = step.process( context ); + if( stepStatus == UrlRewriteStepStatus.FINISHED ) { + return stepStatus; + } + } + stepStatus = processActions( context, state ); + if( stepStatus == UrlRewriteStepStatus.FINISHED ) { + return stepStatus; + } + } + return UrlRewriteStepStatus.SUCCESS; + } + + // All conditions proceeding a set of one or more actions must succeed for the actions to be executed. + private UrlRewriteStepStatus processAndFlow( UrlRewriteContext context ) throws Exception { + UrlRewriteStepProcessorState state = new UrlRewriteStepProcessorState( childProcessors.iterator() ); + UrlRewriteStepStatus stepStatus = UrlRewriteStepStatus.SUCCESS; + UrlRewriteStepProcessorHolder step; + while( state.hasNext() ) { + while( state.hasNextCondition() ) { + step = state.nextCondition( stepStatus ); + stepStatus = step.process( context ); + if( !( stepStatus == UrlRewriteStepStatus.SUCCESS ) ) { + return stepStatus; + } + } + stepStatus = processActions( context, state ); + if( !( stepStatus == UrlRewriteStepStatus.SUCCESS ) ) { + return stepStatus; + } + } + return UrlRewriteStepStatus.SUCCESS; + } + + // At least one condition proceeding a set of one or more actions must succedd for the actions to be executed. + private UrlRewriteStepStatus processOrFlow( UrlRewriteContext context ) throws Exception { + UrlRewriteStepProcessorState state = new UrlRewriteStepProcessorState( childProcessors.iterator() ); + UrlRewriteStepStatus status = UrlRewriteStepStatus.SUCCESS; + UrlRewriteStepProcessorHolder step; + while( state.hasNext() ) { + UrlRewriteStepStatus flowStatus = UrlRewriteStepStatus.FAILURE; + while( state.hasNextCondition() ) { + step = state.nextCondition( status ); + if( flowStatus == UrlRewriteStepStatus.FAILURE ) { + status = step.process( context ); + switch( status ) { + case SUCCESS: + flowStatus = UrlRewriteStepStatus.SUCCESS; + continue; + case FINISHED: + return status; + } + } + } + status = processActions( context, state ); + if( status != UrlRewriteStepStatus.SUCCESS ) { + return status; + } + } + return UrlRewriteStepStatus.SUCCESS; + } + + private UrlRewriteStepStatus processActions( UrlRewriteContext context, UrlRewriteStepProcessorState state ) + throws Exception { + UrlRewriteStepStatus flowStatus = UrlRewriteStepStatus.SUCCESS; + while( state.hasNextAction() ) { + if( flowStatus == UrlRewriteStepStatus.SUCCESS ) { + UrlRewriteStepStatus stepStatus = UrlRewriteStepStatus.SUCCESS; + UrlRewriteStepProcessorHolder step = state.nextAction( stepStatus ); + stepStatus = step.process( context ); + switch( stepStatus ) { + case FAILURE: + flowStatus = UrlRewriteStepStatus.FAILURE; + continue; + case FINISHED: + return stepStatus; + } + } + } + return flowStatus; + } + + @Override + public void destroy() throws Exception { + destroy( processor ); + if( descriptor instanceof UrlRewriteFlowDescriptor ) { + for( UrlRewriteStepProcessorHolder childProcessor : childProcessors ) { + destroy( childProcessor ); + } + } + } + + public void destroy( UrlRewriteStepProcessor processor ) { + if( processor != null ) { + try { + processor.destroy(); + } catch( Exception e ) { + // Maybe it makes sense to throw exception + LOG.failedToDestroyRewriteStepProcessor( 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/UrlRewriteStepProcessorState.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorState.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorState.java new file mode 100644 index 0000000..dead19d --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteStepProcessorState.java @@ -0,0 +1,88 @@ +/** + * 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; + +import org.apache.knox.gateway.filter.rewrite.spi.UrlRewriteStepStatus; + +import java.util.Iterator; + +class UrlRewriteStepProcessorState { + + private UrlRewriteStepStatus status; + private UrlRewriteStepProcessorHolder next; + private Iterator<UrlRewriteStepProcessorHolder> steps; + + UrlRewriteStepProcessorState( Iterator<UrlRewriteStepProcessorHolder> steps ) { + this.status = UrlRewriteStepStatus.SUCCESS; + this.next = null; + this.steps = steps; + } + + private UrlRewriteStepProcessorHolder peek() { + if( next == null && steps.hasNext() ) { + next = steps.next(); + return next; + } else if ( next != null ) { + return next; + } else { + return null; + } + } + + public boolean hasNextCondition() { + UrlRewriteStepProcessorHolder curr = peek(); + return curr != null && curr.isCondition(); + } + + public boolean hasNextAction() { + UrlRewriteStepProcessorHolder curr = peek(); + return curr != null && curr.isAction(); + } + + private UrlRewriteStepProcessorHolder take( UrlRewriteStepStatus lastStatus ) { + UrlRewriteStepProcessorHolder step = peek(); + status = lastStatus; + next = null; + return step; + } + + public UrlRewriteStepProcessorHolder nextCondition( UrlRewriteStepStatus lastStatus ){ + if( hasNextCondition() ) { + return take( lastStatus ); + } else { + return null; + } + } + + public UrlRewriteStepProcessorHolder nextAction( UrlRewriteStepStatus lastStatus ){ + if( hasNextAction() ) { + return take( lastStatus ); + } else { + return null; + } + } + + public UrlRewriteStepStatus status(){ + return status; + } + + public boolean hasNext() { + return next != null || steps.hasNext(); + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/af9b0c3d/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteUtil.java ---------------------------------------------------------------------- diff --git a/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteUtil.java b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteUtil.java new file mode 100644 index 0000000..4e68d27 --- /dev/null +++ b/gateway-provider-rewrite/src/main/java/org/apache/knox/gateway/filter/rewrite/impl/UrlRewriteUtil.java @@ -0,0 +1,92 @@ +/** + * 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; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +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.UrlRewriteFilterDescriptor; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteFilterPathDescriptor; +import org.apache.knox.gateway.filter.rewrite.api.UrlRewriteRulesDescriptor; + +import javax.activation.MimeType; + +public class UrlRewriteUtil { + + public static String pickFirstRuleWithEqualsIgnoreCasePathMatch( UrlRewriteFilterContentDescriptor config, String name ) { + String rule = "*"; + if( config != null && !config.getSelectors().isEmpty() && name != null ) { + rule = ""; + for( UrlRewriteFilterPathDescriptor selector : config.getSelectors() ) { + if( name.equalsIgnoreCase( selector.path() ) ) { + if( selector instanceof UrlRewriteFilterApplyDescriptor) { + rule = ((UrlRewriteFilterApplyDescriptor)selector).rule(); + } + break; + } + } + } + return rule; + } + + public static UrlRewriteFilterContentDescriptor getRewriteFilterConfig( + UrlRewriteRulesDescriptor config, String filterName, MimeType mimeType ) { + UrlRewriteFilterContentDescriptor filterContentConfig = null; + if( config != null ) { + UrlRewriteFilterDescriptor filterConfig = config.getFilter( filterName ); + if( filterConfig != null ) { + filterContentConfig = filterConfig.getContent( mimeType ); + } + } + return filterContentConfig; + } + + public static String filterJavaScript( String inputValue, UrlRewriteFilterContentDescriptor config, + UrlRewriteFilterReader filterReader, UrlRewriteFilterPathDescriptor.Compiler<Pattern> regexCompiler ) { + StringBuffer tbuff = new StringBuffer(); + StringBuffer sbuff = new StringBuffer(); + sbuff.append( inputValue ); + if( config != null && !config.getSelectors().isEmpty() ) { + for( UrlRewriteFilterPathDescriptor selector : config.getSelectors() ) { + if ( selector instanceof UrlRewriteFilterApplyDescriptor ) { + UrlRewriteFilterApplyDescriptor apply = (UrlRewriteFilterApplyDescriptor)selector; + Matcher matcher = apply.compiledPath( regexCompiler ).matcher( sbuff ); + int index = 0; + while ( matcher.find() ) { + int start = matcher.start(); + int end = matcher.end(); + if ( start != -1 && end != -1 ) { + tbuff.append( sbuff, index, start ); + String value = matcher.group(); + value = filterReader.filterValueString( null, value, apply.rule() ); + tbuff.append(value); + index = end; + } + } + tbuff.append( sbuff, index, sbuff.length() ); + sbuff.setLength( 0 ); + sbuff.append( tbuff ); + tbuff.setLength( 0 ); + } + } + } + return sbuff.toString(); + } +}