Author: woonsan Date: Wed Sep 30 17:45:51 2009 New Revision: 820363 URL: http://svn.apache.org/viewvc?rev=820363&view=rev Log: APA-17: Adding default link rewriting feature for reverse proxying.
Added: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContext.java (with props) portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContextAware.java (with props) portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperImpl.java (contents, props changed) - copied, changed from r820182, portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpProxyPathMapperImpl.java portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultReverseProxyRewritingContext.java (with props) portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/PassMappingLinkRewritingParserAaptor.java (with props) portals/applications/webcontent/trunk/webcontent-jar/src/test/java/org/apache/portals/applications/webcontent/proxy/TestReverseProxyLinkRewritingParserAaptor.java (with props) Removed: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpProxyPathMapperImpl.java Modified: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapperProvider.java portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperProviderImpl.java portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyServlet.java portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/RewritableHttpReverseProxyServiceImpl.java portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/SimpleLinkRewritingParserAaptor.java portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/WEB-INF/conf/reverseproxy.properties Modified: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapperProvider.java URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapperProvider.java?rev=820363&r1=820362&r2=820363&view=diff ============================================================================== --- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapperProvider.java (original) +++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/HttpReverseProxyPathMapperProvider.java Wed Sep 30 17:45:51 2009 @@ -16,6 +16,8 @@ */ package org.apache.portals.applications.webcontent.proxy; +import java.util.List; + import javax.servlet.http.HttpServletRequest; import org.apache.portals.applications.webcontent.rewriter.RewriterController; @@ -53,4 +55,10 @@ */ public Ruleset findRewriterRuleset(HttpServletRequest request); + /** + * Returns unmodifiable proxy path mappers. + * @return + */ + public List<HttpReverseProxyPathMapper> getProxyPathMappers(); + } \ No newline at end of file Added: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContext.java URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContext.java?rev=820363&view=auto ============================================================================== --- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContext.java (added) +++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContext.java Wed Sep 30 17:45:51 2009 @@ -0,0 +1,33 @@ +/* + * 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.portals.applications.webcontent.proxy; + +/** + * ReverseProxyRewritingContext interface providing some reverse proxy mapping related information. + * + * @version $Id$ + */ +public interface ReverseProxyRewritingContext +{ + + public HttpReverseProxyPathMapper getHttpReverseProxyPathMapper(); + + public HttpReverseProxyPathMapperProvider getHttpReverseProxyPathMapperProvider(); + + public String getRewritingContextPath(); + +} Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContext.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContext.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContext.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContextAware.java URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContextAware.java?rev=820363&view=auto ============================================================================== --- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContextAware.java (added) +++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContextAware.java Wed Sep 30 17:45:51 2009 @@ -0,0 +1,33 @@ +/* + * 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.portals.applications.webcontent.proxy; + +/** + * ReverseProxyRewritingContextAware interface. + * <P> + * <CODE>ParserAdaptor</CODE> implementation class can implement this interface + * when it wants to be injected with <CODE>ReverseProxyRewritingContext</CODE> object. + * </P> + * + * @version $Id$ + */ +public interface ReverseProxyRewritingContextAware +{ + + public void setReverseProxyRewritingContext(ReverseProxyRewritingContext reverseProxyRewritingContext); + +} Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContextAware.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContextAware.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/ReverseProxyRewritingContextAware.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Copied: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperImpl.java (from r820182, portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpProxyPathMapperImpl.java) URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperImpl.java?p2=portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperImpl.java&p1=portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpProxyPathMapperImpl.java&r1=820182&r2=820363&rev=820363&view=diff ============================================================================== --- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpProxyPathMapperImpl.java (original) +++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperImpl.java Wed Sep 30 17:45:51 2009 @@ -24,12 +24,12 @@ * * @version $Id$ */ -public class DefaultHttpProxyPathMapperImpl implements HttpReverseProxyPathMapper +public class DefaultHttpReverseProxyPathMapperImpl implements HttpReverseProxyPathMapper { private String localBasePath; private String remoteBaseURL; - public DefaultHttpProxyPathMapperImpl(String localBasePath, String remoteBaseURL) + public DefaultHttpReverseProxyPathMapperImpl(String localBasePath, String remoteBaseURL) { this.localBasePath = localBasePath; this.remoteBaseURL = remoteBaseURL; Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperImpl.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperImpl.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperImpl.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperProviderImpl.java URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperProviderImpl.java?rev=820363&r1=820362&r2=820363&view=diff ============================================================================== --- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperProviderImpl.java (original) +++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyPathMapperProviderImpl.java Wed Sep 30 17:45:51 2009 @@ -16,6 +16,7 @@ */ package org.apache.portals.applications.webcontent.proxy.impl; +import java.util.Collections; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -120,4 +121,9 @@ return null; } + public List<HttpReverseProxyPathMapper> getProxyPathMappers() + { + return Collections.unmodifiableList(proxyPathMappers); + } + } Modified: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyServlet.java URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyServlet.java?rev=820363&r1=820362&r2=820363&view=diff ============================================================================== --- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyServlet.java (original) +++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultHttpReverseProxyServlet.java Wed Sep 30 17:45:51 2009 @@ -148,7 +148,7 @@ String localBasePath = pathPassConf.getString("local"); String remoteBaseURL = pathPassConf.getString("remote"); - proxyPathMappers.add(new DefaultHttpProxyPathMapperImpl(localBasePath, remoteBaseURL)); + proxyPathMappers.add(new DefaultHttpReverseProxyPathMapperImpl(localBasePath, remoteBaseURL)); Configuration rewritersConf = pathPassConf.subset("rewriter"); Added: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultReverseProxyRewritingContext.java URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultReverseProxyRewritingContext.java?rev=820363&view=auto ============================================================================== --- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultReverseProxyRewritingContext.java (added) +++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultReverseProxyRewritingContext.java Wed Sep 30 17:45:51 2009 @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.portals.applications.webcontent.proxy.impl; + +import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapper; +import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapperProvider; +import org.apache.portals.applications.webcontent.proxy.ReverseProxyRewritingContext; + +/** + * DefaultReverseProxyRewritingContext + * + * @version $Id$ + */ +public class DefaultReverseProxyRewritingContext implements ReverseProxyRewritingContext +{ + + private HttpReverseProxyPathMapper httpReverseProxyPathMapper; + private HttpReverseProxyPathMapperProvider httpReverseProxyPathMapperProvider; + private String rewritingContextPath; + + public DefaultReverseProxyRewritingContext(HttpReverseProxyPathMapper httpReverseProxyPathMapper, HttpReverseProxyPathMapperProvider httpReverseProxyPathMapperProvider, String rewritingContextPath) + { + this.httpReverseProxyPathMapper = httpReverseProxyPathMapper; + this.httpReverseProxyPathMapperProvider = httpReverseProxyPathMapperProvider; + this.rewritingContextPath = rewritingContextPath; + } + + public HttpReverseProxyPathMapper getHttpReverseProxyPathMapper() + { + return httpReverseProxyPathMapper; + } + + public HttpReverseProxyPathMapperProvider getHttpReverseProxyPathMapperProvider() + { + return httpReverseProxyPathMapperProvider; + } + + public String getRewritingContextPath() + { + return rewritingContextPath; + } +} Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultReverseProxyRewritingContext.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultReverseProxyRewritingContext.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/DefaultReverseProxyRewritingContext.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/PassMappingLinkRewritingParserAaptor.java URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/PassMappingLinkRewritingParserAaptor.java?rev=820363&view=auto ============================================================================== --- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/PassMappingLinkRewritingParserAaptor.java (added) +++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/PassMappingLinkRewritingParserAaptor.java Wed Sep 30 17:45:51 2009 @@ -0,0 +1,116 @@ +/* + * 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.portals.applications.webcontent.proxy.impl; + +import java.io.BufferedWriter; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.LineIterator; +import org.apache.commons.lang.StringUtils; +import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapper; +import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapperProvider; +import org.apache.portals.applications.webcontent.proxy.ReverseProxyRewritingContext; +import org.apache.portals.applications.webcontent.proxy.ReverseProxyRewritingContextAware; +import org.apache.portals.applications.webcontent.rewriter.ParserAdaptor; +import org.apache.portals.applications.webcontent.rewriter.Rewriter; +import org.apache.portals.applications.webcontent.rewriter.RewriterException; + +/** + * A simple reverse proxy link rewriting parser adaptor implementation. + * <P> + * You can refer to this example to implement more sophisticated rewriting parser adaptor. + * </P> + * + * @version $Id$ + */ +public class PassMappingLinkRewritingParserAaptor implements ParserAdaptor, ReverseProxyRewritingContextAware +{ + + private ReverseProxyRewritingContext reverseProxyRewritingContext; + + private Pattern linkBasePathPattern = Pattern.compile("\\s(href|src|action)\\s*=\\s*['\"]((\\/)[^'\"]+)['\"]", Pattern.CASE_INSENSITIVE); + + private String [] linkRemoteBaseURLSearches = { "/", "." }; + private String [] linkRemoteBaseURLReplaces = { "\\/", "\\." }; + + public PassMappingLinkRewritingParserAaptor() + { + } + + public void setReverseProxyRewritingContext(ReverseProxyRewritingContext reverseProxyRewritingContext) + { + this.reverseProxyRewritingContext = reverseProxyRewritingContext; + } + + public void rewrite(Rewriter rewriter, Reader reader, Writer writer) throws RewriterException + { + HttpReverseProxyPathMapperProvider mapperProvider = reverseProxyRewritingContext.getHttpReverseProxyPathMapperProvider(); + HttpReverseProxyPathMapper myProxyMapper = reverseProxyRewritingContext.getHttpReverseProxyPathMapper(); + String rewritingContextPath = reverseProxyRewritingContext.getRewritingContextPath(); + + String localBasePath = myProxyMapper.getLocalBasePath(); + String normalizedLocalBasePath = StringUtils.removeEnd(localBasePath, "/"); + + List<HttpReverseProxyPathMapper> proxyMappers = mapperProvider.getProxyPathMappers(); + int proxyMappersCount = proxyMappers.size(); + Pattern [] proxyMapperRemoteURLPatterns = new Pattern[proxyMappersCount]; + String [] proxyMapperMatcherReplaces = new String[proxyMappersCount]; + + int index = 0; + for (HttpReverseProxyPathMapper proxyMapper : proxyMappers) + { + String remoteBaseURLPattern = StringUtils.replaceEach(StringUtils.removeEnd(proxyMapper.getRemoteBaseURL(), "/"), linkRemoteBaseURLSearches, linkRemoteBaseURLReplaces); + proxyMapperRemoteURLPatterns[index] = Pattern.compile("\\s(href|src|action)\\s*=\\s*['\"](" + remoteBaseURLPattern + "([^'\"]+))['\"]", Pattern.CASE_INSENSITIVE); + proxyMapperMatcherReplaces[index] = rewritingContextPath + StringUtils.removeEnd(proxyMapper.getLocalBasePath(), "/"); + ++index; + } + + PrintWriter out = new PrintWriter(new BufferedWriter(writer)); + + for (LineIterator lineIt = IOUtils.lineIterator(reader); lineIt.hasNext(); ) + { + String line = lineIt.nextLine(); + + // first, replace slash leading relative paths by slash leading reverse proxying relative paths. + Matcher linkBasePathMatcher = linkBasePathPattern.matcher(line); + line = linkBasePathMatcher.replaceAll(" $1=\"" + rewritingContextPath + normalizedLocalBasePath + "$2\""); + + // second, replace absolute urls by slash leading relative paths. + for (int i = 0; i < proxyMappersCount; i++) + { + Matcher otherLinkBasePathMatcher = proxyMapperRemoteURLPatterns[i].matcher(line); + line = otherLinkBasePathMatcher.replaceAll(" $1=\"" + proxyMapperMatcherReplaces[i] + "$3\""); + } + + out.println(line); + } + + out.flush(); + } + + public void parse(Rewriter rewriter, Reader reader) throws RewriterException + { + throw new UnsupportedOperationException(); + } + +} Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/PassMappingLinkRewritingParserAaptor.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/PassMappingLinkRewritingParserAaptor.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/PassMappingLinkRewritingParserAaptor.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/RewritableHttpReverseProxyServiceImpl.java URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/RewritableHttpReverseProxyServiceImpl.java?rev=820363&r1=820362&r2=820363&view=diff ============================================================================== --- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/RewritableHttpReverseProxyServiceImpl.java (original) +++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/RewritableHttpReverseProxyServiceImpl.java Wed Sep 30 17:45:51 2009 @@ -26,6 +26,8 @@ import java.util.ArrayList; import java.util.Enumeration; import java.util.List; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -63,6 +65,8 @@ import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapper; import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapperProvider; import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyService; +import org.apache.portals.applications.webcontent.proxy.ReverseProxyRewritingContext; +import org.apache.portals.applications.webcontent.proxy.ReverseProxyRewritingContextAware; import org.apache.portals.applications.webcontent.proxy.SSOSiteCredentials; import org.apache.portals.applications.webcontent.proxy.SSOSiteCredentialsProvider; import org.apache.portals.applications.webcontent.rewriter.ParserAdaptor; @@ -266,16 +270,18 @@ { String headerName = (String) enumHeaderNames.nextElement(); - if (headerName.equalsIgnoreCase(HTTP.CONTENT_LEN)) + if (StringUtils.equalsIgnoreCase(headerName, HTTP.CONTENT_LEN)) continue; - if (headerName.equalsIgnoreCase(HTTP.TARGET_HOST)) + if (StringUtils.equalsIgnoreCase(headerName, HTTP.TARGET_HOST)) continue; - if (headerName.equalsIgnoreCase(HttpReverseProxyConstants.HTTP_HEADER_RANGE)) + // TODO: Is there any better way to handle "Range" and "If-Range" headers without ignoring like these? + + if (StringUtils.equalsIgnoreCase(headerName, HttpReverseProxyConstants.HTTP_HEADER_RANGE)) continue; - if (headerName.equalsIgnoreCase(HttpReverseProxyConstants.HTTP_HEADER_IF_RANGE)) + if (StringUtils.equalsIgnoreCase(headerName, HttpReverseProxyConstants.HTTP_HEADER_IF_RANGE)) continue; for (Enumeration enumHeaderValues = request.getHeaders(headerName); enumHeaderValues.hasMoreElements(); ) @@ -379,7 +385,7 @@ String headerName = header.getName(); String headerValue = header.getValue(); - if (headerName.equalsIgnoreCase(HTTP.TARGET_HOST)) + if (StringUtils.equalsIgnoreCase(headerName, HTTP.TARGET_HOST)) { response.setHeader(headerName, hostHeaderValue); } @@ -389,8 +395,14 @@ } } + String rewriterContextPath = localBaseURL; + + if (rewriterContextPath == null) { + rewriterContextPath = request.getContextPath() + request.getServletPath(); + } + // Send the content to the client - writeHttpEntityToClient(request, response, httpEntity); + writeHttpEntityToClient(request, response, httpEntity, proxyPathMapper, rewriterContextPath); } } } @@ -419,7 +431,7 @@ } } - private void writeHttpEntityToClient(HttpServletRequest request, HttpServletResponse response, HttpEntity httpEntity) throws IOException + private void writeHttpEntityToClient(HttpServletRequest request, HttpServletResponse response, HttpEntity httpEntity, HttpReverseProxyPathMapper proxyPathMapper, String rewriterContextPath) throws IOException { InputStream in = null; Reader reader = null; @@ -508,31 +520,35 @@ } else { - String responseCharSet = EntityUtils.getContentCharSet(httpEntity); + boolean gzipEncoded = false; + Header contentEncodingHeader = httpEntity.getContentEncoding(); - if (responseCharSet != null) + if (contentEncodingHeader != null) { - reader = new InputStreamReader(in, responseCharSet); - writer = new OutputStreamWriter(out, responseCharSet); + gzipEncoded = StringUtils.equalsIgnoreCase("gzip", contentEncodingHeader.getValue()); } - else + + if (parserAdaptor instanceof ReverseProxyRewritingContextAware) { - reader = new InputStreamReader(in); - writer = new OutputStreamWriter(out); + ReverseProxyRewritingContext rewritingContext = + new DefaultReverseProxyRewritingContext(proxyPathMapper, proxyPathMapperProvider, rewriterContextPath); + ((ReverseProxyRewritingContextAware) parserAdaptor).setReverseProxyRewritingContext(rewritingContext); } - String contentBaseURL = null; + String responseCharSet = EntityUtils.getContentCharSet(httpEntity); - if (localBaseURL != null) + if (responseCharSet != null) { - contentBaseURL = localBaseURL + request.getPathInfo(); + reader = new InputStreamReader(gzipEncoded ? new GZIPInputStream(in) : in, responseCharSet); + writer = new OutputStreamWriter(gzipEncoded ? new GZIPOutputStream(out) : out, responseCharSet); } else { - contentBaseURL = request.getContextPath() + request.getServletPath() + request.getPathInfo(); + reader = new InputStreamReader(gzipEncoded ? new GZIPInputStream(in) : in); + writer = new OutputStreamWriter(gzipEncoded ? new GZIPOutputStream(out) : out); } - rewriter.setBaseUrl(contentBaseURL); + rewriter.setBaseUrl(rewriterContextPath + request.getPathInfo()); rewriter.rewrite(parserAdaptor, reader, writer); writer.flush(); } Modified: portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/SimpleLinkRewritingParserAaptor.java URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/SimpleLinkRewritingParserAaptor.java?rev=820363&r1=820362&r2=820363&view=diff ============================================================================== --- portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/SimpleLinkRewritingParserAaptor.java (original) +++ portals/applications/webcontent/trunk/webcontent-jar/src/main/java/org/apache/portals/applications/webcontent/proxy/impl/SimpleLinkRewritingParserAaptor.java Wed Sep 30 17:45:51 2009 @@ -17,7 +17,6 @@ package org.apache.portals.applications.webcontent.proxy.impl; import java.io.BufferedWriter; -import java.io.IOException; import java.io.PrintWriter; import java.io.Reader; import java.io.Writer; @@ -27,6 +26,9 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.io.LineIterator; import org.apache.commons.lang.StringUtils; +import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapper; +import org.apache.portals.applications.webcontent.proxy.ReverseProxyRewritingContext; +import org.apache.portals.applications.webcontent.proxy.ReverseProxyRewritingContextAware; import org.apache.portals.applications.webcontent.rewriter.ParserAdaptor; import org.apache.portals.applications.webcontent.rewriter.Rewriter; import org.apache.portals.applications.webcontent.rewriter.RewriterException; @@ -39,46 +41,57 @@ * * @version $Id$ */ -public class SimpleLinkRewritingParserAaptor implements ParserAdaptor +public class SimpleLinkRewritingParserAaptor implements ParserAdaptor, ReverseProxyRewritingContextAware { + private ReverseProxyRewritingContext reverseProxyRewritingContext; + private Pattern linkBasePathPattern = Pattern.compile("\\s(href|src|action)\\s*=\\s*['\"]((\\/)[^'\"]+)['\"]", Pattern.CASE_INSENSITIVE); - + + private String [] linkRemoteBaseURLSearches = { "/", "." }; + private String [] linkRemoteBaseURLReplaces = { "\\/", "\\." }; + public SimpleLinkRewritingParserAaptor() { } - + + public void setReverseProxyRewritingContext(ReverseProxyRewritingContext reverseProxyRewritingContext) + { + this.reverseProxyRewritingContext = reverseProxyRewritingContext; + } + public void rewrite(Rewriter rewriter, Reader reader, Writer writer) throws RewriterException { - String baseURL = rewriter.getBaseUrl(); - int offset = baseURL.lastIndexOf('/'); - - if (StringUtils.isBlank(baseURL) || offset != -1) - { - try - { - IOUtils.copy(reader, writer); - } - catch (IOException e) - { - throw new RewriterException(e); - } - } + HttpReverseProxyPathMapper myProxyMapper = reverseProxyRewritingContext.getHttpReverseProxyPathMapper(); + String rewritingContextPath = reverseProxyRewritingContext.getRewritingContextPath(); - baseURL = baseURL.substring(0, offset + 1); + String localBasePath = myProxyMapper.getLocalBasePath(); + String normalizedLocalBasePath = StringUtils.removeEnd(localBasePath, "/"); + + String remoteBaseURLPattern = StringUtils.replaceEach(StringUtils.removeEnd(myProxyMapper.getRemoteBaseURL(), "/"), linkRemoteBaseURLSearches, linkRemoteBaseURLReplaces); + Pattern proxyMapperRemoteURLPattern = Pattern.compile("\\s(href|src|action)\\s*=\\s*['\"](" + remoteBaseURLPattern + "([^'\"]+))['\"]", Pattern.CASE_INSENSITIVE); + String proxyMapperMatcherReplace = rewritingContextPath + StringUtils.removeEnd(myProxyMapper.getLocalBasePath(), "/"); PrintWriter out = new PrintWriter(new BufferedWriter(writer)); for (LineIterator lineIt = IOUtils.lineIterator(reader); lineIt.hasNext(); ) { String line = lineIt.nextLine(); + + // first, replace slash leading relative paths by slash leading reverse proxying relative paths. Matcher linkBasePathMatcher = linkBasePathPattern.matcher(line); - out.println(linkBasePathMatcher.replaceAll(" $1=\"" + baseURL + "$2\"")); + line = linkBasePathMatcher.replaceAll(" $1=\"" + rewritingContextPath + normalizedLocalBasePath + "$2\""); + + // second, replace absolute urls by slash leading relative paths. + Matcher otherLinkBasePathMatcher = proxyMapperRemoteURLPattern.matcher(line); + line = otherLinkBasePathMatcher.replaceAll(" $1=\"" + proxyMapperMatcherReplace + "$3\""); + + out.println(line); } out.flush(); } - + public void parse(Rewriter rewriter, Reader reader) throws RewriterException { throw new UnsupportedOperationException(); Added: portals/applications/webcontent/trunk/webcontent-jar/src/test/java/org/apache/portals/applications/webcontent/proxy/TestReverseProxyLinkRewritingParserAaptor.java URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-jar/src/test/java/org/apache/portals/applications/webcontent/proxy/TestReverseProxyLinkRewritingParserAaptor.java?rev=820363&view=auto ============================================================================== --- portals/applications/webcontent/trunk/webcontent-jar/src/test/java/org/apache/portals/applications/webcontent/proxy/TestReverseProxyLinkRewritingParserAaptor.java (added) +++ portals/applications/webcontent/trunk/webcontent-jar/src/test/java/org/apache/portals/applications/webcontent/proxy/TestReverseProxyLinkRewritingParserAaptor.java Wed Sep 30 17:45:51 2009 @@ -0,0 +1,102 @@ +/* + * 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.portals.applications.webcontent.proxy; + +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.List; + +import junit.framework.TestCase; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.portals.applications.webcontent.proxy.impl.DefaultHttpReverseProxyPathMapperImpl; +import org.apache.portals.applications.webcontent.proxy.impl.DefaultHttpReverseProxyPathMapperProviderImpl; +import org.apache.portals.applications.webcontent.proxy.impl.DefaultReverseProxyRewritingContext; +import org.apache.portals.applications.webcontent.proxy.impl.PassMappingLinkRewritingParserAaptor; +import org.apache.portals.applications.webcontent.proxy.impl.SimpleLinkRewritingParserAaptor; + +/** + * TestSimpleLinkRewritingParserAaptor + * + * @version $Id$ + */ +public class TestReverseProxyLinkRewritingParserAaptor extends TestCase +{ + + private String html = + "<html>\n" + + "<p><a href=\"mission.html\">Mission</a> and <a href='about.html'>About</a></p>\n" + + "<p><a href=\"/docs/\">Documents</a></p>\n" + + "<p><a href=\"http://portals.apache.org/projectinfo.html\">Project Info.</a></p>\n" + + "<p><a href=\"http://www.apache.org/events.html\">Events</a></p>\n" + + "</html>"; + + public void testSimpleRewriting() throws Exception + { + SimpleLinkRewritingParserAaptor parserAdaptor = new SimpleLinkRewritingParserAaptor(); + HttpReverseProxyPathMapper portalsProxyPathMapper = new DefaultHttpReverseProxyPathMapperImpl("/portals/", "http://portals.apache.org/"); + HttpReverseProxyPathMapper apacheProxyPathMapper = new DefaultHttpReverseProxyPathMapperImpl("/apache/", "http://www.apache.org/"); + HttpReverseProxyPathMapper localhostProxyPathMapper = new DefaultHttpReverseProxyPathMapperImpl("/localhost/", "http://www.localhost.com/"); + HttpReverseProxyPathMapperProvider proxyPathMapperProvider = + new DefaultHttpReverseProxyPathMapperProviderImpl(Arrays.asList(new HttpReverseProxyPathMapper [] { portalsProxyPathMapper, apacheProxyPathMapper, localhostProxyPathMapper }), null, null); + + ReverseProxyRewritingContext rewritingContext = + new DefaultReverseProxyRewritingContext(portalsProxyPathMapper, proxyPathMapperProvider, "/webcontent/rproxy"); + parserAdaptor.setReverseProxyRewritingContext(rewritingContext); + + StringReader reader = new StringReader(html); + StringWriter writer = new StringWriter(); + parserAdaptor.rewrite(null, reader, writer); + + List<String> lines = (List<String>) IOUtils.readLines(new StringReader(writer.toString())); + + assertTrue("Wrong rewriting: " + lines.get(1), StringUtils.contains(lines.get(1), "href=\"mission.html\"")); + assertTrue("Wrong rewriting: " + lines.get(1), StringUtils.contains(lines.get(1), "href='about.html'")); + assertTrue("Wrong rewriting: " + lines.get(2), StringUtils.contains(lines.get(2), "href=\"/webcontent/rproxy/portals/docs/\"")); + assertTrue("Wrong rewriting: " + lines.get(3), StringUtils.contains(lines.get(3), "href=\"/webcontent/rproxy/portals/projectinfo.html\"")); + assertTrue("Wrong rewriting: " + lines.get(4), StringUtils.contains(lines.get(4), "href=\"http://www.apache.org/events.html\"")); + } + + public void testPathMappingRewriting() throws Exception + { + PassMappingLinkRewritingParserAaptor parserAdaptor = new PassMappingLinkRewritingParserAaptor(); + HttpReverseProxyPathMapper portalsProxyPathMapper = new DefaultHttpReverseProxyPathMapperImpl("/portals/", "http://portals.apache.org/"); + HttpReverseProxyPathMapper apacheProxyPathMapper = new DefaultHttpReverseProxyPathMapperImpl("/apache/", "http://www.apache.org/"); + HttpReverseProxyPathMapper localhostProxyPathMapper = new DefaultHttpReverseProxyPathMapperImpl("/localhost/", "http://www.localhost.com/"); + HttpReverseProxyPathMapperProvider proxyPathMapperProvider = + new DefaultHttpReverseProxyPathMapperProviderImpl(Arrays.asList(new HttpReverseProxyPathMapper [] { portalsProxyPathMapper, apacheProxyPathMapper, localhostProxyPathMapper }), null, null); + + ReverseProxyRewritingContext rewritingContext = + new DefaultReverseProxyRewritingContext(portalsProxyPathMapper, proxyPathMapperProvider, "/webcontent/rproxy"); + parserAdaptor.setReverseProxyRewritingContext(rewritingContext); + + StringReader reader = new StringReader(html); + StringWriter writer = new StringWriter(); + parserAdaptor.rewrite(null, reader, writer); + + List<String> lines = (List<String>) IOUtils.readLines(new StringReader(writer.toString())); + + assertTrue("Wrong rewriting: " + lines.get(1), StringUtils.contains(lines.get(1), "href=\"mission.html\"")); + assertTrue("Wrong rewriting: " + lines.get(1), StringUtils.contains(lines.get(1), "href='about.html'")); + assertTrue("Wrong rewriting: " + lines.get(2), StringUtils.contains(lines.get(2), "href=\"/webcontent/rproxy/portals/docs/\"")); + assertTrue("Wrong rewriting: " + lines.get(3), StringUtils.contains(lines.get(3), "href=\"/webcontent/rproxy/portals/projectinfo.html\"")); + assertTrue("Wrong rewriting: " + lines.get(4), StringUtils.contains(lines.get(4), "href=\"/webcontent/rproxy/apache/events.html\"")); + } + +} Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/test/java/org/apache/portals/applications/webcontent/proxy/TestReverseProxyLinkRewritingParserAaptor.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/test/java/org/apache/portals/applications/webcontent/proxy/TestReverseProxyLinkRewritingParserAaptor.java ------------------------------------------------------------------------------ svn:keywords = Id Propchange: portals/applications/webcontent/trunk/webcontent-jar/src/test/java/org/apache/portals/applications/webcontent/proxy/TestReverseProxyLinkRewritingParserAaptor.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/WEB-INF/conf/reverseproxy.properties URL: http://svn.apache.org/viewvc/portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/WEB-INF/conf/reverseproxy.properties?rev=820363&r1=820362&r2=820363&view=diff ============================================================================== --- portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/WEB-INF/conf/reverseproxy.properties (original) +++ portals/applications/webcontent/trunk/webcontent-war/src/main/webapp/WEB-INF/conf/reverseproxy.properties Wed Sep 30 17:45:51 2009 @@ -80,6 +80,13 @@ proxy.reverse.pass.portals.local = /portals/ proxy.reverse.pass.portals.remote = http://portals.apache.org/ +proxy.reverse.pass.portals.rewriter.basic = org.apache.portals.applications.webcontent.rewriter.WebContentRewriter +proxy.reverse.pass.portals.rewriter.parserAdaptor = html +# ... SimpleLinkRewritingParserAaptor rewrites links only based on its remote base URLs, +# ... while PassMappingLinkRewritingParserAaptor rewrites links based on all reverse proxy pass mappings. +#proxy.reverse.pass.portals.rewriter.parserAdaptor.html = org.apache.portals.applications.webcontent.proxy.impl.SimpleLinkRewritingParserAaptor +proxy.reverse.pass.portals.rewriter.parserAdaptor.html = org.apache.portals.applications.webcontent.proxy.impl.PassMappingLinkRewritingParserAaptor +proxy.reverse.pass.portals.rewriter.parserAdaptor.html.mimeType = text/html proxy.reverse.pass.localhost.local = /localhost/ proxy.reverse.pass.localhost.remote = http://localhost:8080/