diff --git a/applications/product/src/main/java/org/apache/ofbiz/product/category/ftl/UrlRegexpTransform.java b/applications/product/src/main/java/org/apache/ofbiz/product/category/ftl/UrlRegexpTransform.java index ec008c7c4ed..2d57b98b726 100644 --- a/applications/product/src/main/java/org/apache/ofbiz/product/category/ftl/UrlRegexpTransform.java +++ b/applications/product/src/main/java/org/apache/ofbiz/product/category/ftl/UrlRegexpTransform.java @@ -103,6 +103,7 @@ public Writer getWriter(final Writer out, @SuppressWarnings("rawtypes") Map args final StringBuffer buf = new StringBuffer(); final boolean fullPath = checkArg(args, "fullPath", false); final boolean secure = checkArg(args, "secure", false); + final boolean shortener = checkArg(args, "pathShortener", false); final boolean encode = checkArg(args, "encode", true); final String controlPath = convertToString(args.get("controlPath")); final String webSiteId = convertToString(args.get("webSiteId")); @@ -142,7 +143,7 @@ public void close() throws IOException { RequestHandler rh = RequestHandler.from(request); String seoUrl = seoUrl(rh.makeLink(request, response, buf.toString(), fullPath, - secure || request.isSecure(), encode, controlPath), userLogin == null); + secure || request.isSecure(), encode, controlPath, shortener), userLogin == null); String requestURI = buf.toString(); // add / update csrf token to link when required @@ -159,7 +160,7 @@ public void close() throws IOException { ComponentConfig.WebappInfo webAppInfo = WebAppUtil.getWebappInfoFromWebsiteId(webSiteId); StringBuilder newUrlBuff = new StringBuilder(250); OfbizUrlBuilder builder = OfbizUrlBuilder.from(webAppInfo, delegator); - builder.buildFullUrl(newUrlBuff, buf.toString(), secure); + builder.buildFullUrl(newUrlBuff, buf.toString(), secure, shortener); String newUrl = newUrlBuff.toString(); if (encode) { newUrl = URLEncoder.encode(newUrl, "UTF-8"); diff --git a/framework/common/webcommon/WEB-INF/common-controller.xml b/framework/common/webcommon/WEB-INF/common-controller.xml index fa7f592437a..b6ef970a153 100644 --- a/framework/common/webcommon/WEB-INF/common-controller.xml +++ b/framework/common/webcommon/WEB-INF/common-controller.xml @@ -109,6 +109,10 @@ under the License. + + + + diff --git a/framework/security/config/security.properties b/framework/security/config/security.properties index c7effe8d3f6..be3bdf052ab 100644 --- a/framework/security/config/security.properties +++ b/framework/security/config/security.properties @@ -323,3 +323,6 @@ Content-Security-Policy=Content-Security-Policy-Report-Only #-- Define policy directives, see https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP PolicyDirectives=default-src 'self' + +#-- Give the size of shortener path when the functionality to shorter the url is used +path.shortener.size=10 \ No newline at end of file diff --git a/framework/webapp/dtd/site-conf.xsd b/framework/webapp/dtd/site-conf.xsd index 8f2927a3371..cbf2cfde81f 100644 --- a/framework/webapp/dtd/site-conf.xsd +++ b/framework/webapp/dtd/site-conf.xsd @@ -530,7 +530,8 @@ under the License. request-redirect-noparam, url, url-redirect, - cross-redirect + cross-redirect, + shortener @@ -625,6 +626,13 @@ under the License. + + + + Call ofbiz shortener to resolve the next uri to call + + + diff --git a/framework/webapp/entitydef/entitymodel.xml b/framework/webapp/entitydef/entitymodel.xml index 010969815b5..f8403d7a62c 100644 --- a/framework/webapp/entitydef/entitymodel.xml +++ b/framework/webapp/entitydef/entitymodel.xml @@ -30,10 +30,24 @@ under the License. + + + + + + + + + + + + diff --git a/framework/webapp/ofbiz-component.xml b/framework/webapp/ofbiz-component.xml index 54e4b39280b..152f9310e7e 100644 --- a/framework/webapp/ofbiz-component.xml +++ b/framework/webapp/ofbiz-component.xml @@ -31,5 +31,5 @@ under the License. --> - + diff --git a/framework/webapp/src/main/groovy/org/apache/ofbiz/webapp/test/OfbizPathShortenerTests.groovy b/framework/webapp/src/main/groovy/org/apache/ofbiz/webapp/test/OfbizPathShortenerTests.groovy new file mode 100644 index 00000000000..824e3b30cba --- /dev/null +++ b/framework/webapp/src/main/groovy/org/apache/ofbiz/webapp/test/OfbizPathShortenerTests.groovy @@ -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.ofbiz.webapp.test + +import org.apache.ofbiz.service.testtools.OFBizTestCase +import org.apache.ofbiz.webapp.OfbizPathShortener + +class OfbizPathShortenerTests extends OFBizTestCase { + + OfbizPathShortenerTests(String name) { + super(name) + } + void testComputeLongUrlToShortUrl() { + String longUri = "passwordChange?USERNAME=admin&TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxML" + + "LL.eyJ1c2VyTG9naW5JZCI6Imx1Y2lsZS5wZWxsZXRpZXJAZWRsbi5vcmciLCJpc3MiOiJBcGFjaGVPRkJpeiIsImV4cCI6MTcyNTU" + + "0MjM0OSwiaWF0IjoxNzI1NTQwNTQLLL.Rycl_L-u4ZeWkx82pWWGu7gycfsHQxIxE8zu1nQ5oueGDBeOXALL-SJzMuvSARbpxCwF9A" + + "jl4rTxgoEYuRMoHg&JavaScriptEnabled=Y&Albert=Yoda" + assert OfbizPathShortener.resolveShortenedPath(this.getDelegator(), longUri).length() < 11 + } + void testResolveLongUrlComputedFromShort() { + String longUri = "passwordChange?USERNAME=admin&TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxML" + + "LL.eyJ1c2VyTG9naW5JZCI6Imx1Y2lsZS5wZWxsZXRpZXJAZWRsbi5vcmciLCJpc3MiOiJBcGFjaGVPRkJpeiIsImV4cCI6MTcyNTU" + + "0MjM0OSwiaWF0IjoxNzI1NTQwNTQLLL.Rycl_L-u4ZeWkx82pWWGu7gycfsHQxIxE8zu1nQ5oueGDBeOXALL-SJzMuvSARbpxCwF9A" + + "jl4rTxgoEYuRMoHg&JavaScriptEnabled=Y" + String shortUri = OfbizPathShortener.resolveShortenedPath(this.getDelegator(), longUri) + assert longUri == OfbizPathShortener.resolveOriginalPathFromShortened(this.getDelegator(), shortUri) + } + void testResolveLongUrlComputedFromShortAlreadyStored() { + String longUri = "passwordChange?USERNAME=admin&TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxML" + + "LL.eyJ1c2VyTG9naW5JZCI6Imx1Y2lsZS5wZWxsZXRpZXJAZWRsbi5vcmciLCJpc3MiOiJBcGFjaGVPRkJpeiIsImV4cCI6MTcyNTU" + + "0MjM0OSwiaWF0IjoxNzI1NTQwNTQLLL.Rycl_L-u4ZeWkx82pWWGu7gycfsHQxIxE8zu1nQ5oueGDBeOXALL-SJzMuvSARbpxCwF9A" + + "jl4rTxgoEYuRMoHg&JavaScriptEnabled=Y&And=Again" + String shortUriFirst = OfbizPathShortener.resolveShortenedPath(this.getDelegator(),longUri) + String shortUriSecond = OfbizPathShortener.resolveShortenedPath(this.getDelegator(),longUri) + assert shortUriSecond == shortUriFirst + } + +} diff --git a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/OfbizPathShortener.java b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/OfbizPathShortener.java new file mode 100644 index 00000000000..81f485d5594 --- /dev/null +++ b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/OfbizPathShortener.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * 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.ofbiz.webapp; + +import java.util.Map; +import javax.transaction.Transaction; +import org.apache.commons.lang.RandomStringUtils; +import org.apache.ofbiz.base.crypto.HashCrypt; +import org.apache.ofbiz.base.util.UtilDateTime; +import org.apache.ofbiz.base.util.UtilProperties; +import org.apache.ofbiz.entity.Delegator; +import org.apache.ofbiz.entity.GenericEntityException; +import org.apache.ofbiz.entity.GenericValue; +import org.apache.ofbiz.entity.transaction.GenericTransactionException; +import org.apache.ofbiz.entity.transaction.TransactionUtil; +import org.apache.ofbiz.entity.util.EntityQuery; + +public class OfbizPathShortener { + public static final String SHORTENED_PATH = "s/"; + public static final String RESTORE_PATH = "../"; + + /** + * For an ofbiz path, return a shortened url that will be linked to the given path + * example : orderview?orderId=HA1023 -> s/izapnreiis + * @param delegator + * @param path to shorten + * @return a shortened key corresponding to the path + * @throws GenericEntityException + */ + public static String shortenPath(Delegator delegator, String path) throws GenericEntityException { + return SHORTENED_PATH + resolveShortenedPath(delegator, path); + } + + /** + * For the given path, check if a shortened path already exists otherwise generate a new one + * @param delegator + * @param path + * @return a shortened path corresponding to the given path + * @throws GenericEntityException + */ + public static String resolveShortenedPath(Delegator delegator, String path) throws GenericEntityException { + String shortenedPath = resolveExistingShortenedPath(delegator, path); + int nbLoop = 0; + if (shortenedPath == null) { + do { + shortenedPath = generate(); + nbLoop++; + } while (!recordPathMapping(delegator, path, shortenedPath) || nbLoop > 10); + } + return shortenedPath; + } + + /** + * Try to resolve the original path, if failed, return to the webapp root. Use views request for that define on common-controller + * @param delegator + * @param shortenedPath + * @return the origin path corresponding to the given shortened path, webapp root otherwise + * @throws GenericEntityException + */ + public static String restoreOriginalPath(Delegator delegator, String shortenedPath) throws GenericEntityException { + String originalPath = resolveOriginalPathFromShortened(delegator, shortenedPath); + return RESTORE_PATH + (originalPath != null ? originalPath : "views"); + } + + /** + * From a shortened path, resolve the origin path + * @param delegator + * @param shortenedPath path + * @return the original path corresponding to the shortened path + * @throws GenericEntityException + */ + public static String resolveOriginalPathFromShortened(Delegator delegator, String shortenedPath) throws GenericEntityException { + return readPathMapping(delegator, shortenedPath); + } + + /** + * For a path, function tried to resolve if it already presents in database + * For performance issue the function use the hash to resolve it + * @param delegator + * @param path + * @return the shortened path if found, null otherwise + * @throws GenericEntityException + */ + private static String resolveExistingShortenedPath(Delegator delegator, String path) throws GenericEntityException { + GenericValue existingPath = EntityQuery.use(delegator) + .from("ShortenedPath") + .where("originalPathHash", generateHash(path)) + .cache() + .queryFirst(); + return existingPath != null ? existingPath.getString("shortenedPath") : null; + } + + /** + * generate a random shortened path, the size can be set on property : security. + * @return shortened path + */ + private static String generate() { + int shortenerSize = UtilProperties.getPropertyAsInteger("security", "path.shortener.size", 10); + return RandomStringUtils.randomAlphabetic(shortenerSize); + } + + /** + * Create the mapping between an origin map and the shortened path + * This will be executed on dedicate transaction to be sure to not rollback it after. + * @param delegator + * @param path + * @param shortenedPath + * @return true if it's create with success + */ + private static boolean recordPathMapping(Delegator delegator, String path, String shortenedPath) { + Transaction trans = null; + try { + try { + trans = TransactionUtil.suspend(); + TransactionUtil.begin(); + delegator.create("ShortenedPath", Map.of("shortenedPath", shortenedPath, + "originalPath", path, + "originalPathHash", generateHash(path), + "createdDate", UtilDateTime.nowTimestamp(), + "createdByUserLogin", "system")); + TransactionUtil.commit(); + } catch (GenericEntityException e) { + TransactionUtil.rollback(); + return false; + } finally { + TransactionUtil.resume(trans); + } + } catch (GenericTransactionException e) { + return false; + } + return true; + } + + /** + * @param path + * @return a hash of the given path + */ + private static String generateHash(String path) { + return HashCrypt.digestHash("SHA", path.getBytes()); + } + + /** + * Find the origin path corresponding to the shorter in database + * @param delegator + * @param shortenedPath + * @return the original path corresponding to shortened path given, null if not found + * @throws GenericEntityException + */ + private static String readPathMapping(Delegator delegator, String shortenedPath) throws GenericEntityException { + GenericValue existingPath = EntityQuery.use(delegator) + .from("ShortenedPath") + .where("shortenedPath", shortenedPath) + .cache() + .queryOne(); + return existingPath != null + ? existingPath.getString("originalPath") + : null; + } + +} diff --git a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/OfbizUrlBuilder.java b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/OfbizUrlBuilder.java index cfb80dfccf1..05c05cda404 100644 --- a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/OfbizUrlBuilder.java +++ b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/OfbizUrlBuilder.java @@ -57,7 +57,8 @@ public static OfbizUrlBuilder from(HttpServletRequest request) throws GenericEnt URL url = ConfigXMLReader.getControllerConfigURL(request.getServletContext()); ControllerConfig config = ConfigXMLReader.getControllerConfig(url); String servletPath = (String) request.getAttribute("_CONTROL_PATH_"); - builder = new OfbizUrlBuilder(config, webSiteProps, servletPath); + Delegator delegator = (Delegator) request.getAttribute("delegator"); + builder = new OfbizUrlBuilder(delegator, config, webSiteProps, servletPath); request.setAttribute("_OFBIZ_URL_BUILDER_", builder); } return builder; @@ -95,14 +96,16 @@ public static OfbizUrlBuilder from(WebappInfo webAppInfo, Delegator delegator) t if (webSiteProps == null) { webSiteProps = WebSiteProperties.defaults(delegator); } - return new OfbizUrlBuilder(config, webSiteProps, servletPath); + return new OfbizUrlBuilder(delegator, config, webSiteProps, servletPath); } + private final Delegator delegator; private final ControllerConfig config; private final WebSiteProperties webSiteProps; private final String servletPath; - private OfbizUrlBuilder(ControllerConfig config, WebSiteProperties webSiteProps, String servletPath) { + private OfbizUrlBuilder(Delegator delegator, ControllerConfig config, WebSiteProperties webSiteProps, String servletPath) { + this.delegator = delegator; this.config = config; this.webSiteProps = webSiteProps; this.servletPath = servletPath; @@ -118,9 +121,26 @@ private OfbizUrlBuilder(ControllerConfig config, WebSiteProperties webSiteProps, * @throws WebAppConfigurationException * @throws IOException */ - public boolean buildFullUrl(Appendable buffer, String url, boolean useSSL) throws WebAppConfigurationException, IOException { + public boolean buildFullUrl(Appendable buffer, String url, boolean useSSL) + throws WebAppConfigurationException, IOException, GenericEntityException { + return buildFullUrl(buffer, url, useSSL, false); + } + + /** + * Builds a full URL - including scheme, host, servlet path and resource. + * @param buffer + * @param url + * @param useSSL Default value to use - will be replaced by request-map setting + * if one is found. + * @param pathShortener + * @return true if the URL uses https + * @throws WebAppConfigurationException + * @throws IOException + */ + public boolean buildFullUrl(Appendable buffer, String url, boolean useSSL, boolean pathShortener) + throws WebAppConfigurationException, IOException, GenericEntityException { boolean makeSecure = buildHostPart(buffer, url, useSSL); - buildPathPart(buffer, url); + buildPathPart(buffer, url, pathShortener); return makeSecure; } @@ -183,7 +203,20 @@ public boolean buildHostPart(Appendable buffer, String url, boolean useSSL) thro * @throws WebAppConfigurationException * @throws IOException */ - public void buildPathPart(Appendable buffer, String url) throws WebAppConfigurationException, IOException { + public void buildPathPart(Appendable buffer, String url) + throws WebAppConfigurationException, IOException, GenericEntityException { + buildPathPart(buffer, url, false); + } + /** + * Builds a partial URL - including the servlet path and resource, but not the scheme or host. + * @param buffer + * @param url + * @param pathShortener + * @throws WebAppConfigurationException + * @throws IOException + */ + public void buildPathPart(Appendable buffer, String url, boolean pathShortener) + throws WebAppConfigurationException, IOException, GenericEntityException { if (servletPath == null) { throw new IllegalStateException("Servlet path is unknown"); } @@ -191,6 +224,8 @@ public void buildPathPart(Appendable buffer, String url) throws WebAppConfigurat if (!url.startsWith("/")) { buffer.append("/"); } - buffer.append(url); + buffer.append(pathShortener + ? OfbizPathShortener.shortenPath(delegator, url) + : url); } } diff --git a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java index 020bc83f910..f02efae40ff 100644 --- a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java +++ b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/control/RequestHandler.java @@ -64,6 +64,7 @@ import org.apache.ofbiz.entity.util.EntityUtilProperties; import org.apache.ofbiz.security.CsrfUtil; import org.apache.ofbiz.webapp.OfbizUrlBuilder; +import org.apache.ofbiz.webapp.OfbizPathShortener; import org.apache.ofbiz.webapp.control.ConfigXMLReader.ControllerConfig; import org.apache.ofbiz.webapp.control.ConfigXMLReader.RequestMap; import org.apache.ofbiz.webapp.event.EventFactory; @@ -901,6 +902,18 @@ public void doRequest(HttpServletRequest request, HttpServletResponse response, } String url = nextRequestResponse.getValue().startsWith("/") ? nextRequestResponse.getValue() : "/" + nextRequestResponse.getValue(); callRedirect(url + this.makeQueryString(request, nextRequestResponse), response, request, redirectSC); + } else if ("shortener".equals(nextRequestResponse.getType())) { + // check for a shortener + if (Debug.verboseOn()) { + Debug.logVerbose("[RequestHandler.doRequest]: Response is a shortener redirect." + showSessionId(request), MODULE); + } + String url = null; + try { + url = OfbizPathShortener.restoreOriginalPath(delegator, (String) request.getAttribute("shortener")); + } catch (GenericEntityException e) { + throw new RuntimeException(e); + } + callRedirect(url, response, request, redirectSC); } else if ("request-redirect".equals(nextRequestResponse.getType())) { if (Debug.verboseOn()) { Debug.logVerbose("[RequestHandler.doRequest]: Response is a Request redirect." + showSessionId(request), MODULE); @@ -1359,6 +1372,10 @@ public String makeLink(HttpServletRequest request, HttpServletResponse response, public String makeLink(HttpServletRequest request, HttpServletResponse response, String url, boolean fullPath, boolean secure, boolean encode, String targetControlPath) { + return makeLink(request, response, url, fullPath, secure, encode, "", false); + } + public String makeLink(HttpServletRequest request, HttpServletResponse response, String url, boolean fullPath, boolean secure, boolean encode, + String targetControlPath, boolean pathShortener) { WebSiteProperties webSiteProps = null; try { webSiteProps = WebSiteProperties.from(request); @@ -1458,8 +1475,19 @@ public String makeLink(HttpServletRequest request, HttpServletResponse response, } // now add the actual passed url, but if it doesn't start with a / add one first - if (url != null && !url.startsWith("/")) { - newURL.append("/"); + if (url != null) { + if (!url.startsWith("/")) { + newURL.append("/"); + } + if (pathShortener) { + try { + url = OfbizPathShortener.shortenPath(delegator, url); + } catch (GenericEntityException e) { + // If the entity engine is throwing exceptions, then there is no point in continuing. + Debug.logError(e, "Exception thrown while getting the path shortener: ", MODULE); + return null; + } + } } newURL.append(url == null ? "" : url); diff --git a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizUrlTransform.java b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizUrlTransform.java index 763b4f087fc..829a7392efc 100644 --- a/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizUrlTransform.java +++ b/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizUrlTransform.java @@ -47,6 +47,7 @@ *
  • fullPath (true/false) - generate a full URL including scheme and host, defaults to false.
  • *
  • secure (true/false) - generate a secure (https) URL, defaults to false. Server settings will * override this argument.
  • + *
  • pathShortener (true/false) - generate a shortened URL. Defaults to false.
  • *
  • encode (true/false) - encode the URL, defaults to true. Encoding is UTF-8.
  • *
  • webSiteId - generate a full URL using the web site settings found in the WebSite entity.
  • *
  • controlPath - override the default control path.
  • @@ -96,6 +97,7 @@ public Writer getWriter(final Writer out, Map args) { final StringBuilder buf = new StringBuilder(); final boolean fullPath = checkBooleanArg(args, "fullPath", false); final boolean secure = checkBooleanArg(args, "secure", false); + final boolean pathShortener = checkBooleanArg(args, "pathShortener", false); final boolean encode = checkBooleanArg(args, "encode", true); final String webSiteId = convertToString(args.get("webSiteId")); final String controlPath = convertToString(args.get("controlPath")); @@ -133,7 +135,7 @@ public void close() throws IOException { WebappInfo webAppInfo = WebAppUtil.getWebappInfoFromWebsiteId(webSiteId); StringBuilder newUrlBuff = new StringBuilder(250); OfbizUrlBuilder builder = OfbizUrlBuilder.from(webAppInfo, delegator); - builder.buildFullUrl(newUrlBuff, buf.toString(), secure); + builder.buildFullUrl(newUrlBuff, buf.toString(), secure, pathShortener); String newUrl = newUrlBuff.toString(); if (encode) { newUrl = URLEncoder.encode(newUrl, "UTF-8"); @@ -145,7 +147,7 @@ public void close() throws IOException { HttpServletResponse response = FreeMarkerWorker.unwrap(env.getVariable("response")); String requestUrl = buf.toString(); RequestHandler rh = RequestHandler.from(request); - String link = (rh.makeLink(request, response, requestUrl, fullPath, secure, encode, controlPath)); + String link = (rh.makeLink(request, response, requestUrl, fullPath, secure, encode, controlPath, pathShortener)); out.write(link); } else { out.write(buf.toString()); diff --git a/framework/webapp/testdef/webapptests.xml b/framework/webapp/testdef/webapptests.xml index f33f4989947..692e5640fa8 100644 --- a/framework/webapp/testdef/webapptests.xml +++ b/framework/webapp/testdef/webapptests.xml @@ -20,5 +20,5 @@ - +