diff --git a/source/java/org/alfresco/web/bean/wcm/AVMConstants.java b/source/java/org/alfresco/web/bean/wcm/AVMConstants.java index a75ee125c5..4b9914fc76 100644 --- a/source/java/org/alfresco/web/bean/wcm/AVMConstants.java +++ b/source/java/org/alfresco/web/bean/wcm/AVMConstants.java @@ -99,7 +99,7 @@ public final class AVMConstants { throw new IllegalArgumentException("Username is mandatory."); } - return store + '-' + username + AVMConstants.STORE_MAIN; + return store + STORE_SEPARATOR + username + AVMConstants.STORE_MAIN; } public static String buildAVMUserPreviewStoreName(String store, String username) @@ -112,7 +112,7 @@ public final class AVMConstants { throw new IllegalArgumentException("Username is mandatory."); } - return store + '-' + username + AVMConstants.STORE_PREVIEW; + return store + STORE_SEPARATOR + username + AVMConstants.STORE_PREVIEW; } public static String buildAVMStoreRootPath(String store) @@ -304,7 +304,7 @@ public final class AVMConstants } /** - * Returns the path portion up the webap + * Returns the path portion up the webapp * * @param absoluteAVMPath the path from which to extract the webapp path * @@ -400,17 +400,27 @@ public final class AVMConstants } } + // Component Separator. + private static final String STORE_SEPARATOR = "--"; // names of the stores representing the layers for an AVM website - public final static String STORE_STAGING = "-staging"; - public final static String STORE_MAIN = "-main"; - public final static String STORE_PREVIEW = "-preview"; + public final static String STORE_STAGING = ""; + public final static String STORE_MAIN = ""; + public final static String STORE_PREVIEW = STORE_SEPARATOR + "preview"; // system directories at the top level of an AVM website + // + // TODO: The virtualization server should get these two parameters + // from the Alfresco webapp at registration time. + // public final static String DIR_APPBASE = "appBase"; public final static String DIR_WEBAPPS = "avm_webapps"; + + - // servlet implicit root directory + // servlet default webapp + // Note: this webapp is mapped to the URL path "" + // public final static String DIR_ROOT = "ROOT"; // system property keys for sandbox identification and DNS virtualisation mapping @@ -428,8 +438,8 @@ public final class AVMConstants private static final String BEAN_VIRT_SERVER_REGISTRY = "VirtServerRegistry"; // URLs for preview of sandboxes and assets - private final static String PREVIEW_SANDBOX_URL = "http://www-{0}.{1}:{2}"; - private final static String PREVIEW_ASSET_URL = "http://www-{0}.{1}:{2}{3}"; + private final static String PREVIEW_SANDBOX_URL = "http://{0}.www--sandbox.{1}:{2}"; + private final static String PREVIEW_ASSET_URL = "http://{0}.www--sandbox.{1}:{2}{3}"; // pattern for absolute AVM Path private final static Pattern WEBAPP_RELATIVE_PATH_PATTERN = diff --git a/source/java/org/alfresco/web/bean/wcm/DNSNameMangler.java b/source/java/org/alfresco/web/bean/wcm/DNSNameMangler.java index 37a930b80d..0bd97b13bd 100644 --- a/source/java/org/alfresco/web/bean/wcm/DNSNameMangler.java +++ b/source/java/org/alfresco/web/bean/wcm/DNSNameMangler.java @@ -23,16 +23,60 @@ import org.alfresco.util.GUID; /** * Utility to convert sandbox store names into DNS save names. - * @author britt + * + * Host labels appear in the same order as transparent overlays + * are viewed (highest first). For example: + * + * The "preview" layer on the "alice" layer on the "mysite" layer + * within the domain www--sandbox.127-0-0-1.ip.alfrescodemo.net + * is encoded as: + * + * http://preview.alice.mysite.www--sandbox.127-0-0-1.ip.alfrescodemo.net + * + * Note that the "virtualization" domain config just refers to + * where the wildcard DNS entry begins. Here, both domains + * "127-0-0-1.ip.alfrescodemo.net" and "*.127-0-0-1.ip.alfrescodemo.net" + * resolve to 127.0.0.1, so effectively "127-0-0-1.ip.alfrescodemo.net" + * is the "virtualization domain". The "www--sandbox" part just + * delmits the end of the dns-name-mangled store. + * + * This manging scheme was also designed to be fully compatible with + * the future use of I18N-encoded DNS names; the relevant standard + * is IDNA ("Internationalizing Domain Names In Applications"). + * See RFC 3490 and 3492. + * + * @author Jon Cox + * @author Britt Park + * */ class DNSNameMangler { // Component Separator. - private static final String SEPARATOR = "--"; + private static final String SEPARATOR = "."; + + // DNS rules allow up to 255 chars, but limiting + // MAX_INTERNAL_DNS_NAME_LENGTH to less in order + // to provide plenty of extra room for: + // + // + // o The AVMUrlValve's end-of-info-bearing-part-of-DNS-name delimiter: + // (i.e.: ".www--sandbox"). For example: + // + // http://.www--sandbox:/ + // + // o Other AVMUrlValve args after "www--sandbox". For example: + // http://alice.mysite.www--sandbox.version--v44.:/ + // http://alice.mysite.www--sandbox.gmt--2006-12-31-23-59. + // ... + // + + private static final int MAX_INTERNAL_DNS_NAME_LENGTH = 150; // Regular expressions. private static final Pattern RX_DNS_LEGAL = - Pattern.compile("^[a-zA-Z0-9][a-zA-Z0-9-]{0,57}[a-zA-Z0-9]$"); + Pattern.compile("[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]" + + "(?:\\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])*"); + private static final Pattern RX_ILLEGAL_CHARS = Pattern.compile("[^a-zA-Z0-9]"); private static final Pattern RX_HYPHENS = @@ -50,26 +94,38 @@ class DNSNameMangler static String MakeDNSName(String... components) { StringBuilder builder = new StringBuilder(); - for (int i = 0; i < components.length - 1; i++) + + // Make domain name order the reverse + // of the file system ordering. + + for (int i = components.length - 1; i > 0; i--) { builder.append(MangleOne(components[i])); builder.append(SEPARATOR); } - builder.append(MangleOne(components[components.length - 1])); + builder.append(MangleOne(components[0])); String result = builder.toString(); - if (RX_DNS_LEGAL.matcher(result).matches()) + + if ( (result.length() <= MAX_INTERNAL_DNS_NAME_LENGTH) && + RX_DNS_LEGAL.matcher(result).matches() + ) { return result; } + // Otherwise more drastic measures are needed. - result = components[0] + "--" + GUID.generate(); + result = components[0] + SEPARATOR + "guid-" + GUID.generate(); result = MangleOne(result); - if (RX_DNS_LEGAL.matcher(result).matches()) + + if ( (result.length() <= MAX_INTERNAL_DNS_NAME_LENGTH) && + RX_DNS_LEGAL.matcher(result).matches() + ) { return result; } + // Finally this cannot fail. - return MangleOne(GUID.generate()); + return MangleOne("guid-" + GUID.generate()); } /** @@ -79,13 +135,28 @@ class DNSNameMangler */ static String MangleOne(String name) { - // Preserve case for prettier auto-generated URLs - // name = name.toLowerCase(); + // Even if the name is IDNA-encoded, the result is never + // a string that contains chars outside of [a-zA-Z0-9-] + // Replace bad chars with "-", rather than throwing + // an error. name = RX_ILLEGAL_CHARS.matcher(name).replaceAll("-"); - name = RX_HYPHENS.matcher(name).replaceAll("-"); + + // While it's tempting to reserve "--" as our own + // mangling delimiter, IDNA has already clamed it. + // Therefore, doing something like this would be bad: + // + // name = RX_HYPHENS.matcher(name).replaceAll("-"); + // + // Any IDNA I18N-encoded host label ("xn--...") would + // be corrupted. + + // However, leading/trailing hyphens are always illegal, + // so we can still check for that: + name = RX_LEADING_HYPHEN.matcher(name).replaceAll("x"); name = RX_TRAILING_HYPHEN.matcher(name).replaceAll("x"); + return name; } } diff --git a/source/java/org/alfresco/web/bean/wcm/SandboxFactory.java b/source/java/org/alfresco/web/bean/wcm/SandboxFactory.java index 90ce110c22..c11742f46e 100644 --- a/source/java/org/alfresco/web/bean/wcm/SandboxFactory.java +++ b/source/java/org/alfresco/web/bean/wcm/SandboxFactory.java @@ -152,9 +152,9 @@ public final class SandboxFactory /** * Create a user sandbox for the named store. * - * A user sandbox is comprised of two stores, the first named 'storename-username-main' layered - * over the staging store with a preview store named 'storename-username-preview' layered over - * the main store. + * A user sandbox is comprised of two stores, the first + * named 'storename---username' layered over the staging store with a preview store + * named 'storename--username--preview' layered over the main store. * * Various store meta-data properties are set including: * Identifier for store-types: .sandbox.author.main and .sandbox.author.preview diff --git a/source/java/org/alfresco/web/config/ClientConfigElement.java b/source/java/org/alfresco/web/config/ClientConfigElement.java index 97f86a45cf..71e1b7e841 100644 --- a/source/java/org/alfresco/web/config/ClientConfigElement.java +++ b/source/java/org/alfresco/web/config/ClientConfigElement.java @@ -38,7 +38,7 @@ public class ClientConfigElement extends ConfigElementAdapter public static final String CONFIG_ELEMENT_ID = "client"; private static final String BEAN_VIRT_SERVER_REGISTRY = "VirtServerRegistry"; - private static final String DEFAULT_VSERVER_IP = "avm.127-0-0-1.ip.alfrescodemo.net"; + private static final String DEFAULT_VSERVER_IP = "127-0-0-1.ip.alfrescodemo.net"; private static final int DEFAULT_VSERVER_PORT = 8180; private static final String DEFAULT_FROM_ADDRESS = "alfresco@alfresco.org";