ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/catalina/mapper/Mapper.java

Path
ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/catalina/mapper/Mapper.java
Status
scanned
Type
file
Name
Mapper.java
Extension
.java
Programming language
Java
Mime type
text/plain
File type
ASCII text, with CRLF line terminators
Tag

      
    
Rootfs path

      
    
Size
63195 (61.7 KB)
MD5
d2a633d1b0311e33f05e4248418c89e4
SHA1
fb30ec4a71be320dd54a8e84238138eb34e2439f
SHA256
12de52d73e2b086b6209185584abf01d81ea77d7523ff60d88ac12cdf02e0120
SHA512

      
    
SHA1_git
8253077ead363f13cac07ce54975f5858595e28b
Is binary

      
    
Is text
True
Is archive

      
    
Is media

      
    
Is legal

      
    
Is manifest

      
    
Is readme

      
    
Is top level

      
    
Is key file

      
    
Mapper.java | 61.7 KB |

/* * 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.catalina.mapper; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import jakarta.servlet.http.MappingMatch; import org.apache.catalina.Context; import org.apache.catalina.Host; import org.apache.catalina.WebResource; import org.apache.catalina.WebResourceRoot; import org.apache.catalina.Wrapper; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.buf.Ascii; import org.apache.tomcat.util.buf.CharChunk; import org.apache.tomcat.util.buf.MessageBytes; import org.apache.tomcat.util.res.StringManager; /** * Mapper, which implements the servlet API mapping rules (which are derived from the HTTP rules). */ public final class Mapper { private static final Log log = LogFactory.getLog(Mapper.class); private static final StringManager sm = StringManager.getManager(Mapper.class); private static final CharChunk CONTEXT_ROOT_MAPPED_PATH_CHAR_CHUNK; static { CONTEXT_ROOT_MAPPED_PATH_CHAR_CHUNK = new CharChunk(1); try { CONTEXT_ROOT_MAPPED_PATH_CHAR_CHUNK.append('/'); } catch (IOException ioe) { // Should never happen. Convert to a runtime exception if it does. throw new IllegalStateException(ioe); } } // ----------------------------------------------------- Instance Variables /** * Array containing the virtual hosts definitions. */ // Package private to facilitate testing volatile MappedHost[] hosts = new MappedHost[0]; /** * Default host name. */ private volatile String defaultHostName = null; private volatile MappedHost defaultHost = null; /** * Mapping from Context object to Context version to support RequestDispatcher mappings. */ private final Map<Context,ContextVersion> contextObjectToContextVersionMap = new ConcurrentHashMap<>(); // --------------------------------------------------------- Public Methods /** * Set default host. * * @param defaultHostName Default host name */ public synchronized void setDefaultHostName(String defaultHostName) { this.defaultHostName = renameWildcardHost(defaultHostName); if (this.defaultHostName == null) { defaultHost = null; } else { defaultHost = exactFind(hosts, this.defaultHostName); } } /** * Add a new host to the mapper. * * @param name Virtual host name * @param aliases Alias names for the virtual host * @param host Host object */ public synchronized void addHost(String name, String[] aliases, Host host) { name = renameWildcardHost(name); MappedHost[] newHosts = new MappedHost[hosts.length + 1]; MappedHost newHost = new MappedHost(name, host); if (insertMap(hosts, newHosts, newHost)) { hosts = newHosts; if (newHost.name.equals(defaultHostName)) { defaultHost = newHost; } if (log.isDebugEnabled()) { log.debug(sm.getString("mapper.addHost.success", name)); } } else { MappedHost duplicate = hosts[find(hosts, name)]; if (duplicate.object == host) { // The host is already registered in the mapper. // E.g. it might have been added by addContextVersion() if (log.isDebugEnabled()) { log.debug(sm.getString("mapper.addHost.sameHost", name)); } newHost = duplicate; } else { log.error(sm.getString("mapper.duplicateHost", name, duplicate.getRealHostName())); // Do not add aliases, as removeHost(hostName) won't be able to // remove them return; } } List<MappedHost> newAliases = new ArrayList<>(aliases.length); for (String alias : aliases) { alias = renameWildcardHost(alias); MappedHost newAlias = new MappedHost(alias, newHost); if (addHostAliasImpl(newAlias)) { newAliases.add(newAlias); } } newHost.addAliases(newAliases); } /** * Remove a host from the mapper. * * @param name Virtual host name */ public synchronized void removeHost(String name) { name = renameWildcardHost(name); // Find and remove the old host MappedHost host = exactFind(hosts, name); if (host == null || host.isAlias()) { return; } MappedHost[] newHosts = hosts.clone(); // Remove real host and all its aliases int j = 0; for (int i = 0; i < newHosts.length; i++) { if (newHosts[i].getRealHost() != host) { newHosts[j++] = newHosts[i]; } } hosts = Arrays.copyOf(newHosts, j); } /** * Add an alias to an existing host. * * @param name The name of the host * @param alias The alias to add */ public synchronized void addHostAlias(String name, String alias) { MappedHost realHost = exactFind(hosts, name); if (realHost == null) { // Should not be adding an alias for a host that doesn't exist but // just in case... return; } alias = renameWildcardHost(alias); MappedHost newAlias = new MappedHost(alias, realHost); if (addHostAliasImpl(newAlias)) { realHost.addAlias(newAlias); } } private synchronized boolean addHostAliasImpl(MappedHost newAlias) { MappedHost[] newHosts = new MappedHost[hosts.length + 1]; if (insertMap(hosts, newHosts, newAlias)) { hosts = newHosts; if (newAlias.name.equals(defaultHostName)) { defaultHost = newAlias; } if (log.isDebugEnabled()) { log.debug(sm.getString("mapper.addHostAlias.success", newAlias.name, newAlias.getRealHostName())); } return true; } else { MappedHost duplicate = hosts[find(hosts, newAlias.name)]; if (duplicate.getRealHost() == newAlias.getRealHost()) { // A duplicate Alias for the same Host. // A harmless redundancy. E.g. // <Host name="localhost"><Alias>localhost</Alias></Host> if (log.isDebugEnabled()) { log.debug(sm.getString("mapper.addHostAlias.sameHost", newAlias.name, newAlias.getRealHostName())); } return false; } log.error(sm.getString("mapper.duplicateHostAlias", newAlias.name, newAlias.getRealHostName(), duplicate.getRealHostName())); return false; } } /** * Remove a host alias * * @param alias The alias to remove */ public synchronized void removeHostAlias(String alias) { alias = renameWildcardHost(alias); // Find and remove the alias MappedHost hostMapping = exactFind(hosts, alias); if (hostMapping == null || !hostMapping.isAlias()) { return; } MappedHost[] newHosts = new MappedHost[hosts.length - 1]; if (removeMap(hosts, newHosts, alias)) { hosts = newHosts; hostMapping.getRealHost().removeAlias(hostMapping); } } /** * Replace {@link MappedHost#contextList} field in <code>realHost</code> and all its aliases with a new value. */ private void updateContextList(MappedHost realHost, ContextList newContextList) { realHost.contextList = newContextList; for (MappedHost alias : realHost.getAliases()) { alias.contextList = newContextList; } } /** * Add a new Context to an existing Host. * * @param hostName Virtual host name this context belongs to * @param host Host object * @param path Context path * @param version Context version * @param context Context object * @param welcomeResources Welcome files defined for this context * @param resources Static resources of the context * @param wrappers Information on wrapper mappings */ public void addContextVersion(String hostName, Host host, String path, String version, Context context, String[] welcomeResources, WebResourceRoot resources, Collection<WrapperMappingInfo> wrappers) { hostName = renameWildcardHost(hostName); MappedHost mappedHost = exactFind(hosts, hostName); if (mappedHost == null) { addHost(hostName, new String[0], host); mappedHost = exactFind(hosts, hostName); if (mappedHost == null) { log.error(sm.getString("mapper.addContext.noHost", hostName)); return; } } if (mappedHost.isAlias()) { log.error(sm.getString("mapper.addContext.hostIsAlias", hostName)); return; } int slashCount = slashCount(path); synchronized (mappedHost) { ContextVersion newContextVersion = new ContextVersion(version, path, slashCount, context, resources, welcomeResources); if (wrappers != null) { addWrappers(newContextVersion, wrappers); } ContextList contextList = mappedHost.contextList; MappedContext mappedContext = exactFind(contextList.contexts, path); if (mappedContext == null) { mappedContext = new MappedContext(path, newContextVersion); ContextList newContextList = contextList.addContext(mappedContext, slashCount); if (newContextList != null) { updateContextList(mappedHost, newContextList); contextObjectToContextVersionMap.put(context, newContextVersion); } } else { ContextVersion[] contextVersions = mappedContext.versions; ContextVersion[] newContextVersions = new ContextVersion[contextVersions.length + 1]; if (insertMap(contextVersions, newContextVersions, newContextVersion)) { mappedContext.versions = newContextVersions; contextObjectToContextVersionMap.put(context, newContextVersion); } else { // Re-registration after Context.reload() // Replace ContextVersion with the new one int pos = find(contextVersions, version); if (pos >= 0 && contextVersions[pos].name.equals(version)) { contextVersions[pos] = newContextVersion; contextObjectToContextVersionMap.put(context, newContextVersion); } } } } } /** * Remove a context from an existing host. * * @param ctxt The actual context * @param hostName Virtual host name this context belongs to * @param path Context path * @param version Context version */ public void removeContextVersion(Context ctxt, String hostName, String path, String version) { hostName = renameWildcardHost(hostName); contextObjectToContextVersionMap.remove(ctxt); MappedHost host = exactFind(hosts, hostName); if (host == null || host.isAlias()) { return; } synchronized (host) { ContextList contextList = host.contextList; MappedContext context = exactFind(contextList.contexts, path); if (context == null) { return; } ContextVersion[] contextVersions = context.versions; ContextVersion[] newContextVersions = new ContextVersion[contextVersions.length - 1]; if (removeMap(contextVersions, newContextVersions, version)) { if (newContextVersions.length == 0) { // Remove the context ContextList newContextList = contextList.removeContext(path); if (newContextList != null) { updateContextList(host, newContextList); } } else { context.versions = newContextVersions; } } } } /** * Mark a context as being reloaded. Reversion of this state is performed by calling * <code>addContextVersion(...)</code> when context starts up. * * @param ctxt The actual context * @param hostName Virtual host name this context belongs to * @param contextPath Context path * @param version Context version */ public void pauseContextVersion(Context ctxt, String hostName, String contextPath, String version) { hostName = renameWildcardHost(hostName); ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, true); if (contextVersion == null || !ctxt.equals(contextVersion.object)) { return; } contextVersion.markPaused(); } private ContextVersion findContextVersion(String hostName, String contextPath, String version, boolean silent) { MappedHost host = exactFind(hosts, hostName); if (host == null || host.isAlias()) { if (!silent) { log.error(sm.getString("mapper.findContext.noHostOrAlias", hostName)); } return null; } MappedContext context = exactFind(host.contextList.contexts, contextPath); if (context == null) { if (!silent) { log.error(sm.getString("mapper.findContext.noContext", contextPath)); } return null; } ContextVersion contextVersion = exactFind(context.versions, version); if (contextVersion == null) { if (!silent) { log.error(sm.getString("mapper.findContext.noContextVersion", contextPath, version)); } return null; } return contextVersion; } public void addWrapper(String hostName, String contextPath, String version, String path, Wrapper wrapper, boolean jspWildCard, boolean resourceOnly) { hostName = renameWildcardHost(hostName); ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false); if (contextVersion == null) { return; } addWrapper(contextVersion, path, wrapper, jspWildCard, resourceOnly); } public void addWrappers(String hostName, String contextPath, String version, Collection<WrapperMappingInfo> wrappers) { hostName = renameWildcardHost(hostName); ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false); if (contextVersion == null) { return; } addWrappers(contextVersion, wrappers); } /** * Adds wrappers to the given context. * * @param contextVersion The context to which to add the wrappers * @param wrappers Information on wrapper mappings */ private void addWrappers(ContextVersion contextVersion, Collection<WrapperMappingInfo> wrappers) { for (WrapperMappingInfo wrapper : wrappers) { addWrapper(contextVersion, wrapper.mapping(), wrapper.wrapper(), wrapper.jspWildCard(), wrapper.resourceOnly()); } } /** * Adds a wrapper to the given context. * * @param context The context to which to add the wrapper * @param path Wrapper mapping * @param wrapper The Wrapper object * @param jspWildCard true if the wrapper corresponds to the JspServlet and the mapping path contains a wildcard; * false otherwise * @param resourceOnly true if this wrapper always expects a physical resource to be present (such as a JSP) */ private void addWrapper(ContextVersion context, String path, Wrapper wrapper, boolean jspWildCard, boolean resourceOnly) { synchronized (context) { if (path.endsWith("/*")) { // Wildcard wrapper String name = path.substring(0, path.length() - 2); MappedWrapper newWrapper = new MappedWrapper(name, wrapper, jspWildCard, resourceOnly); MappedWrapper[] oldWrappers = context.wildcardWrappers; MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1]; if (insertMap(oldWrappers, newWrappers, newWrapper)) { context.wildcardWrappers = newWrappers; int slashCount = slashCount(newWrapper.name); if (slashCount > context.nesting) { context.nesting = slashCount; } } } else if (path.startsWith("*.")) { // Extension wrapper String name = path.substring(2); MappedWrapper newWrapper = new MappedWrapper(name, wrapper, jspWildCard, resourceOnly); MappedWrapper[] oldWrappers = context.extensionWrappers; MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1]; if (insertMap(oldWrappers, newWrappers, newWrapper)) { context.extensionWrappers = newWrappers; } } else if (path.equals("/")) { // Default wrapper context.defaultWrapper = new MappedWrapper("", wrapper, jspWildCard, resourceOnly); } else { // Exact wrapper final String name; if (path.isEmpty()) { // Special case for the Context Root mapping which is // treated as an exact match name = "/"; } else { name = path; } MappedWrapper newWrapper = new MappedWrapper(name, wrapper, jspWildCard, resourceOnly); MappedWrapper[] oldWrappers = context.exactWrappers; MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1]; if (insertMap(oldWrappers, newWrappers, newWrapper)) { context.exactWrappers = newWrappers; } } } } /** * Remove a wrapper from an existing context. * * @param hostName Virtual host name this wrapper belongs to * @param contextPath Context path this wrapper belongs to * @param version Context version this wrapper belongs to * @param path Wrapper mapping */ public void removeWrapper(String hostName, String contextPath, String version, String path) { hostName = renameWildcardHost(hostName); ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, true); if (contextVersion == null || contextVersion.isPaused()) { return; } removeWrapper(contextVersion, path); } private void removeWrapper(ContextVersion context, String path) { if (log.isTraceEnabled()) { log.trace(sm.getString("mapper.removeWrapper", context.name, path)); } synchronized (context) { if (path.endsWith("/*")) { // Wildcard wrapper String name = path.substring(0, path.length() - 2); MappedWrapper[] oldWrappers = context.wildcardWrappers; if (oldWrappers.length == 0) { return; } MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length - 1]; if (removeMap(oldWrappers, newWrappers, name)) { // Recalculate nesting context.nesting = 0; for (MappedWrapper newWrapper : newWrappers) { int slashCount = slashCount(newWrapper.name); if (slashCount > context.nesting) { context.nesting = slashCount; } } context.wildcardWrappers = newWrappers; } } else if (path.startsWith("*.")) { // Extension wrapper String name = path.substring(2); MappedWrapper[] oldWrappers = context.extensionWrappers; if (oldWrappers.length == 0) { return; } MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length - 1]; if (removeMap(oldWrappers, newWrappers, name)) { context.extensionWrappers = newWrappers; } } else if (path.equals("/")) { // Default wrapper context.defaultWrapper = null; } else { // Exact wrapper String name; if (path.isEmpty()) { // Special case for the Context Root mapping which is // treated as an exact match name = "/"; } else { name = path; } MappedWrapper[] oldWrappers = context.exactWrappers; if (oldWrappers.length == 0) { return; } MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length - 1]; if (removeMap(oldWrappers, newWrappers, name)) { context.exactWrappers = newWrappers; } } } } /** * Add a welcome file to the given context. * * @param hostName The host where the given context can be found * @param contextPath The path of the given context * @param version The version of the given context * @param welcomeFile The welcome file to add */ public void addWelcomeFile(String hostName, String contextPath, String version, String welcomeFile) { hostName = renameWildcardHost(hostName); ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false); if (contextVersion == null) { return; } int len = contextVersion.welcomeResources.length + 1; String[] newWelcomeResources = new String[len]; System.arraycopy(contextVersion.welcomeResources, 0, newWelcomeResources, 0, len - 1); newWelcomeResources[len - 1] = welcomeFile; contextVersion.welcomeResources = newWelcomeResources; } /** * Remove a welcome file from the given context. * * @param hostName The host where the given context can be found * @param contextPath The path of the given context * @param version The version of the given context * @param welcomeFile The welcome file to remove */ public void removeWelcomeFile(String hostName, String contextPath, String version, String welcomeFile) { hostName = renameWildcardHost(hostName); ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false); if (contextVersion == null || contextVersion.isPaused()) { return; } int match = -1; for (int i = 0; i < contextVersion.welcomeResources.length; i++) { if (welcomeFile.equals(contextVersion.welcomeResources[i])) { match = i; break; } } if (match > -1) { int len = contextVersion.welcomeResources.length - 1; String[] newWelcomeResources = new String[len]; System.arraycopy(contextVersion.welcomeResources, 0, newWelcomeResources, 0, match); if (match < len) { System.arraycopy(contextVersion.welcomeResources, match + 1, newWelcomeResources, match, len - match); } contextVersion.welcomeResources = newWelcomeResources; } } /** * Clear the welcome files for the given context. * * @param hostName The host where the context to be cleared can be found * @param contextPath The path of the context to be cleared * @param version The version of the context to be cleared */ public void clearWelcomeFiles(String hostName, String contextPath, String version) { hostName = renameWildcardHost(hostName); ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false); if (contextVersion == null) { return; } contextVersion.welcomeResources = new String[0]; } /** * Map the specified host name and URI, mutating the given mapping data. * * @param host Virtual host name * @param uri URI * @param version The version, if any, included in the request to be mapped * @param mappingData This structure will contain the result of the mapping operation * * @throws IOException if the buffers are too small to hold the results of the mapping. */ public void map(MessageBytes host, MessageBytes uri, String version, MappingData mappingData) throws IOException { if (host.isNull()) { String defaultHostName = this.defaultHostName; if (defaultHostName == null) { return; } host.setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0); host.getCharChunk().append(defaultHostName); } host.toChars(); uri.toChars(); internalMap(host.getCharChunk(), uri.getCharChunk(), version, mappingData); } /** * Map the specified URI relative to the context, mutating the given mapping data. * * @param context The actual context * @param uri URI * @param mappingData This structure will contain the result of the mapping operation * * @throws IOException if the buffers are too small to hold the results of the mapping. */ public void map(Context context, MessageBytes uri, MappingData mappingData) throws IOException { ContextVersion contextVersion = contextObjectToContextVersionMap.get(context); uri.toChars(); CharChunk uricc = uri.getCharChunk(); uricc.setLimit(-1); internalMapWrapper(contextVersion, uricc, mappingData); } // -------------------------------------------------------- Private Methods /** * Map the specified URI. * * @throws IOException If an error occurs while manipulating the URI during the mapping */ private void internalMap(CharChunk host, CharChunk uri, String version, MappingData mappingData) throws IOException { if (mappingData.host != null) { // The legacy code (dating down at least to Tomcat 4.1) just // skipped all mapping work in this case. That behaviour has a risk // of returning an inconsistent result. // I do not see a valid use case for it. throw new AssertionError(); } // Virtual host mapping MappedHost[] hosts = this.hosts; MappedHost mappedHost = exactFindIgnoreCase(hosts, host); if (mappedHost == null) { // Note: Internally, the Mapper does not use the leading * on a // wildcard host. This is to allow this shortcut. int firstDot = host.indexOf('.'); if (firstDot > -1) { int start = host.getStart(); try { host.setStart(firstDot + start); mappedHost = exactFindIgnoreCase(hosts, host); } finally { // Make absolutely sure this gets reset host.setStart(start); } } if (mappedHost == null) { mappedHost = defaultHost; if (mappedHost == null) { return; } } } mappingData.host = mappedHost.object; if (uri.isNull()) { // Can't map context or wrapper without a uri return; } uri.setLimit(-1); // Context mapping ContextList contextList = mappedHost.contextList; MappedContext[] contexts = contextList.contexts; int pos = find(contexts, uri); if (pos == -1) { return; } int lastSlash = -1; int uriEnd = uri.getEnd(); boolean found = false; MappedContext context = null; while (pos >= 0) { context = contexts[pos]; if (uri.startsWith(context.name)) { int length = context.name.length(); if (uri.getLength() == length) { found = true; break; } else if (uri.startsWithIgnoreCase("/", length)) { found = true; break; } } if (lastSlash == -1) { lastSlash = nthSlash(uri, contextList.nesting + 1); } else { lastSlash = lastSlash(uri); } uri.setEnd(lastSlash); pos = find(contexts, uri); } uri.setEnd(uriEnd); if (!found) { if (contexts[0].name.isEmpty()) { context = contexts[0]; } else { context = null; } } if (context == null) { return; } ContextVersion contextVersion = null; ContextVersion[] contextVersions = context.versions; final int versionCount = contextVersions.length; if (versionCount > 1) { Context[] contextObjects = new Context[contextVersions.length]; for (int i = 0; i < contextObjects.length; i++) { contextObjects[i] = contextVersions[i].object; } mappingData.contexts = contextObjects; if (version != null) { contextVersion = exactFind(contextVersions, version); } } if (contextVersion == null) { // Return the latest version // The versions array is known to contain at least one element contextVersion = contextVersions[versionCount - 1]; } mappingData.context = contextVersion.object; mappingData.contextSlashCount = contextVersion.slashCount; // Wrapper mapping if (!contextVersion.isPaused()) { internalMapWrapper(contextVersion, uri, mappingData); } } /** * Wrapper mapping. * * @throws IOException if the buffers are too small to hold the results of the mapping. */ private void internalMapWrapper(ContextVersion contextVersion, CharChunk path, MappingData mappingData) throws IOException { int pathStart = path.getStart(); int pathEnd = path.getEnd(); boolean noServletPath = false; int length = contextVersion.path.length(); if (length == (pathEnd - pathStart)) { noServletPath = true; } int servletPath = pathStart + length; path.setStart(servletPath); // Rule 1 -- Exact Match MappedWrapper[] exactWrappers = contextVersion.exactWrappers; internalMapExactWrapper(exactWrappers, path, mappingData); // Rule 2 -- Prefix Match boolean checkJspWelcomeFiles = false; MappedWrapper[] wildcardWrappers = contextVersion.wildcardWrappers; if (mappingData.wrapper == null) { internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting, path, mappingData); if (mappingData.wrapper != null && mappingData.jspWildCard) { char[] buf = path.getBuffer(); if (buf[pathEnd - 1] == '/') { /* * Path ending in '/' was mapped to JSP servlet based on wildcard match (e.g., as specified in * url-pattern of a jsp-property-group). Force the context's welcome files, which are interpreted as * JSP files (since they match the url-pattern), to be considered. See Bugzilla 27664. */ mappingData.wrapper = null; checkJspWelcomeFiles = true; } else { // See Bugzilla 27704 mappingData.wrapperPath.setChars(buf, path.getStart(), path.getLength()); mappingData.pathInfo.recycle(); } } } if (mappingData.wrapper == null && noServletPath && contextVersion.object.getMapperContextRootRedirectEnabled()) { // The path is empty, redirect to "/" path.append('/'); pathEnd = path.getEnd(); mappingData.redirectPath.setChars(path.getBuffer(), pathStart, pathEnd - pathStart); path.setEnd(pathEnd - 1); return; } // Rule 3 -- Extension Match MappedWrapper[] extensionWrappers = contextVersion.extensionWrappers; if (mappingData.wrapper == null && !checkJspWelcomeFiles) { internalMapExtensionWrapper(extensionWrappers, path, mappingData, true); } // Rule 4 -- Welcome resources processing for servlets if (mappingData.wrapper == null) { boolean checkWelcomeFiles = checkJspWelcomeFiles; if (!checkWelcomeFiles) { char[] buf = path.getBuffer(); checkWelcomeFiles = (buf[pathEnd - 1] == '/'); } if (checkWelcomeFiles) { for (int i = 0; (i < contextVersion.welcomeResources.length) && (mappingData.wrapper == null); i++) { path.setStart(pathStart); path.setEnd(pathEnd); path.append(contextVersion.welcomeResources[i], 0, contextVersion.welcomeResources[i].length()); path.setStart(servletPath); // Rule 4a -- Welcome resources processing for exact macth internalMapExactWrapper(exactWrappers, path, mappingData); // Rule 4b -- Welcome resources processing for prefix match if (mappingData.wrapper == null) { internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting, path, mappingData); } // Rule 4c -- Welcome resources processing // for physical folder if (mappingData.wrapper == null && contextVersion.resources != null) { String pathStr = path.toString(); WebResource file = contextVersion.resources.getResource(pathStr); if (file != null && file.isFile()) { internalMapExtensionWrapper(extensionWrappers, path, mappingData, true); if (mappingData.wrapper == null && contextVersion.defaultWrapper != null) { mappingData.wrapper = contextVersion.defaultWrapper.object; mappingData.requestPath.setChars(path.getBuffer(), path.getStart(), path.getLength()); mappingData.wrapperPath.setChars(path.getBuffer(), path.getStart(), path.getLength()); mappingData.requestPath.setString(pathStr); mappingData.wrapperPath.setString(pathStr); } } } } path.setStart(servletPath); path.setEnd(pathEnd); } } /* * Welcome file processing - take 2. Now that we have looked for welcome files with a physical backing, now look * for an extension mapping listed but may not have a physical backing to it. This is for the case of index.jsf, * index.do, etc. A watered down version of rule 4 */ if (mappingData.wrapper == null) { boolean checkWelcomeFiles = checkJspWelcomeFiles; if (!checkWelcomeFiles) { char[] buf = path.getBuffer(); checkWelcomeFiles = (buf[pathEnd - 1] == '/'); } if (checkWelcomeFiles) { for (int i = 0; (i < contextVersion.welcomeResources.length) && (mappingData.wrapper == null); i++) { path.setStart(pathStart); path.setEnd(pathEnd); path.append(contextVersion.welcomeResources[i], 0, contextVersion.welcomeResources[i].length()); path.setStart(servletPath); internalMapExtensionWrapper(extensionWrappers, path, mappingData, false); } path.setStart(servletPath); path.setEnd(pathEnd); } } // Rule 7 -- Default servlet if (mappingData.wrapper == null && !checkJspWelcomeFiles) { if (contextVersion.defaultWrapper != null) { mappingData.wrapper = contextVersion.defaultWrapper.object; mappingData.requestPath.setChars(path.getBuffer(), path.getStart(), path.getLength()); mappingData.wrapperPath.setChars(path.getBuffer(), path.getStart(), path.getLength()); mappingData.matchType = MappingMatch.DEFAULT; } // Redirection to a folder char[] buf = path.getBuffer(); if (contextVersion.resources != null && buf[pathEnd - 1] != '/') { String pathStr = path.toString(); // Note: Check redirect first to save unnecessary getResource() // call. See BZ 62968. if (contextVersion.object.getMapperDirectoryRedirectEnabled()) { WebResource file; // Handle context root if (pathStr.isEmpty()) { file = contextVersion.resources.getResource("/"); } else { file = contextVersion.resources.getResource(pathStr); } if (file != null && file.isDirectory()) { // Note: this mutates the path: do not do any processing // after this (since we set the redirectPath, there // shouldn't be any) path.setStart(pathStart); path.append('/'); mappingData.redirectPath.setChars(path.getBuffer(), path.getStart(), path.getLength()); } else { mappingData.requestPath.setString(pathStr); mappingData.wrapperPath.setString(pathStr); } } else { mappingData.requestPath.setString(pathStr); mappingData.wrapperPath.setString(pathStr); } } } path.setStart(pathStart); path.setEnd(pathEnd); } /** * Exact mapping. */ private void internalMapExactWrapper(MappedWrapper[] wrappers, CharChunk path, MappingData mappingData) { if (path.isEmpty()) { /* * Looking for a context root mapped servlet but that will be stored under the name "/" */ path = CONTEXT_ROOT_MAPPED_PATH_CHAR_CHUNK; } MappedWrapper wrapper = exactFind(wrappers, path); if (wrapper != null) { mappingData.requestPath.setString(wrapper.name); mappingData.wrapper = wrapper.object; if (path.equals("/")) { // Special handling for Context Root mapped servlet mappingData.pathInfo.setString("/"); mappingData.wrapperPath.setString(""); mappingData.matchType = MappingMatch.CONTEXT_ROOT; } else { mappingData.wrapperPath.setString(wrapper.name); mappingData.matchType = MappingMatch.EXACT; } } } /** * Wildcard mapping. */ private void internalMapWildcardWrapper(MappedWrapper[] wrappers, int nesting, CharChunk path, MappingData mappingData) { int pathEnd = path.getEnd(); int lastSlash = -1; int length = -1; int pos = find(wrappers, path); if (pos != -1) { boolean found = false; while (pos >= 0) { if (path.startsWith(wrappers[pos].name)) { length = wrappers[pos].name.length(); if (path.getLength() == length) { found = true; break; } else if (path.startsWithIgnoreCase("/", length)) { found = true; break; } } if (lastSlash == -1) { lastSlash = nthSlash(path, nesting + 1); } else { lastSlash = lastSlash(path); } path.setEnd(lastSlash); pos = find(wrappers, path); } path.setEnd(pathEnd); if (found) { mappingData.wrapperPath.setString(wrappers[pos].name); if (path.getLength() > length) { mappingData.pathInfo.setChars(path.getBuffer(), path.getStart() + length, path.getLength() - length); } mappingData.requestPath.setChars(path.getBuffer(), path.getStart(), path.getLength()); mappingData.wrapper = wrappers[pos].object; mappingData.jspWildCard = wrappers[pos].jspWildCard; mappingData.matchType = MappingMatch.PATH; } } } /** * Extension mappings. * * @param wrappers Set of wrappers to check for matches * @param path Path to map * @param mappingData Mapping data for result * @param resourceExpected Is this mapping expecting to find a resource */ private void internalMapExtensionWrapper(MappedWrapper[] wrappers, CharChunk path, MappingData mappingData, boolean resourceExpected) { char[] buf = path.getBuffer(); int pathEnd = path.getEnd(); int servletPath = path.getStart(); int slash = -1; for (int i = pathEnd - 1; i >= servletPath; i--) { if (buf[i] == '/') { slash = i; break; } } if (slash >= 0) { int period = -1; for (int i = pathEnd - 1; i > slash; i--) { if (buf[i] == '.') { period = i; break; } } if (period >= 0) { path.setStart(period + 1); path.setEnd(pathEnd); MappedWrapper wrapper = exactFind(wrappers, path); if (wrapper != null && (resourceExpected || !wrapper.resourceOnly)) { mappingData.wrapperPath.setChars(buf, servletPath, pathEnd - servletPath); mappingData.requestPath.setChars(buf, servletPath, pathEnd - servletPath); mappingData.wrapper = wrapper.object; mappingData.matchType = MappingMatch.EXTENSION; } path.setStart(servletPath); path.setEnd(pathEnd); } } } /** * Find a map element given its name in a sorted array of map elements. This will return the index for the closest * inferior or equal item in the given array. */ private static <T> int find(MapElement<T>[] map, CharChunk name) { return find(map, name, name.getStart(), name.getEnd()); } /** * Find a map element given its name in a sorted array of map elements. This will return the index for the closest * inferior or equal item in the given array. */ private static <T> int find(MapElement<T>[] map, CharChunk name, int start, int end) { int a = 0; int b = map.length - 1; // Special cases: -1 and 0 if (b == -1) { return -1; } if (compare(name, start, end, map[0].name) < 0) { return -1; } if (b == 0) { return 0; } int i; while (true) { i = (b + a) >>> 1; int result = compare(name, start, end, map[i].name); if (result == 1) { a = i; } else if (result == 0) { return i; } else { b = i; } if ((b - a) == 1) { int result2 = compare(name, start, end, map[b].name); if (result2 < 0) { return a; } else { return b; } } } } /** * Find a map element given its name in a sorted array of map elements. This will return the index for the closest * inferior or equal item in the given array. */ private static <T> int findIgnoreCase(MapElement<T>[] map, CharChunk name) { return findIgnoreCase(map, name, name.getStart(), name.getEnd()); } /** * Find a map element given its name in a sorted array of map elements. This will return the index for the closest * inferior or equal item in the given array. */ private static <T> int findIgnoreCase(MapElement<T>[] map, CharChunk name, int start, int end) { int a = 0; int b = map.length - 1; // Special cases: -1 and 0 if (b == -1) { return -1; } if (compareIgnoreCase(name, start, end, map[0].name) < 0) { return -1; } if (b == 0) { return 0; } int i; while (true) { i = (b + a) >>> 1; int result = compareIgnoreCase(name, start, end, map[i].name); if (result == 1) { a = i; } else if (result == 0) { return i; } else { b = i; } if ((b - a) == 1) { int result2 = compareIgnoreCase(name, start, end, map[b].name); if (result2 < 0) { return a; } else { return b; } } } } /** * Find a map element given its name in a sorted array of map elements. This will return the index for the closest * inferior or equal item in the given array. * * @see #exactFind(MapElement[], String) */ private static <T> int find(MapElement<T>[] map, String name) { int a = 0; int b = map.length - 1; // Special cases: -1 and 0 if (b == -1) { return -1; } if (name.compareTo(map[0].name) < 0) { return -1; } if (b == 0) { return 0; } int i; while (true) { i = (b + a) >>> 1; int result = name.compareTo(map[i].name); if (result > 0) { a = i; } else if (result == 0) { return i; } else { b = i; } if ((b - a) == 1) { int result2 = name.compareTo(map[b].name); if (result2 < 0) { return a; } else { return b; } } } } /** * Find a map element given its name in a sorted array of map elements. This will return the element that you were * searching for. Otherwise it will return <code>null</code>. * * @see #find(MapElement[], String) */ private static <T, E extends MapElement<T>> E exactFind(E[] map, String name) { int pos = find(map, name); if (pos >= 0) { E result = map[pos]; if (name.equals(result.name)) { return result; } } return null; } /** * Find a map element given its name in a sorted array of map elements. This will return the element that you were * searching for. Otherwise it will return <code>null</code>. */ private static <T, E extends MapElement<T>> E exactFind(E[] map, CharChunk name) { int pos = find(map, name); if (pos >= 0) { E result = map[pos]; if (name.equals(result.name)) { return result; } } return null; } /** * Find a map element given its name in a sorted array of map elements. This will return the element that you were * searching for. Otherwise it will return <code>null</code>. * * @see #findIgnoreCase(MapElement[], CharChunk) */ private static <T, E extends MapElement<T>> E exactFindIgnoreCase(E[] map, CharChunk name) { int pos = findIgnoreCase(map, name); if (pos >= 0) { E result = map[pos]; if (name.equalsIgnoreCase(result.name)) { return result; } } return null; } /** * Compare given char chunk with String. Return -1, 0 or +1 if inferior, equal, or superior to the String. */ private static int compare(CharChunk name, int start, int end, String compareTo) { int result = 0; char[] c = name.getBuffer(); int compareLen = compareTo.length(); int len = compareLen; if ((end - start) < len) { len = end - start; } for (int i = 0; (i < len) && (result == 0); i++) { char nameChar = c[i + start]; char compareToChar = compareTo.charAt(i); if (nameChar > compareToChar) { result = 1; } else if (nameChar < compareToChar) { result = -1; } } if (result == 0) { if (compareLen > (end - start)) { result = -1; } else if (compareLen < (end - start)) { result = 1; } } return result; } /** * Compare given char chunk with String ignoring case. Return -1, 0 or +1 if inferior, equal, or superior to the * String. */ private static int compareIgnoreCase(CharChunk name, int start, int end, String compareTo) { int result = 0; char[] c = name.getBuffer(); int compareLen = compareTo.length(); int len = compareLen; if ((end - start) < len) { len = end - start; } for (int i = 0; (i < len) && (result == 0); i++) { int nameLower = Ascii.toLower(c[i + start]); int compareLower = Ascii.toLower(compareTo.charAt(i)); if (nameLower > compareLower) { result = 1; } else if (nameLower < compareLower) { result = -1; } } if (result == 0) { if (compareLen > (end - start)) { result = -1; } else if (compareLen < (end - start)) { result = 1; } } return result; } /** * Find the position of the last slash in the given char chunk. */ private static int lastSlash(CharChunk name) { char[] c = name.getBuffer(); int end = name.getEnd(); int start = name.getStart(); int pos = end; while (pos > start) { if (c[--pos] == '/') { break; } } return pos; } /** * Find the position of the nth slash, in the given char chunk. */ private static int nthSlash(CharChunk name, int n) { char[] c = name.getBuffer(); int end = name.getEnd(); int pos = name.getStart(); int count = 0; while (pos < end) { if ((c[pos++] == '/') && ((++count) == n)) { pos--; break; } } return pos; } /** * Return the slash count in a given string. */ private static int slashCount(String name) { int pos = -1; int count = 0; while ((pos = name.indexOf('/', pos + 1)) != -1) { count++; } return count; } /** * Insert into the right place in a sorted MapElement array, and prevent duplicates. */ private static <T> boolean insertMap(MapElement<T>[] oldMap, MapElement<T>[] newMap, MapElement<T> newElement) { int pos = find(oldMap, newElement.name); if ((pos != -1) && (newElement.name.equals(oldMap[pos].name))) { return false; } System.arraycopy(oldMap, 0, newMap, 0, pos + 1); newMap[pos + 1] = newElement; System.arraycopy(oldMap, pos + 1, newMap, pos + 2, oldMap.length - pos - 1); return true; } /** * Insert into the right place in a sorted MapElement array. */ private static <T> boolean removeMap(MapElement<T>[] oldMap, MapElement<T>[] newMap, String name) { int pos = find(oldMap, name); if ((pos != -1) && (name.equals(oldMap[pos].name))) { System.arraycopy(oldMap, 0, newMap, 0, pos); System.arraycopy(oldMap, pos + 1, newMap, pos, oldMap.length - pos - 1); return true; } return false; } /* * To simplify the mapping process, wild card hosts take the form ".apache.org" rather than "*.apache.org" * internally. However, for ease of use the external form remains "*.apache.org". Any host name passed into this * class needs to be passed through this method to rename and wild card host names from the external to internal * form. */ private static String renameWildcardHost(String hostName) { if (hostName != null && hostName.startsWith("*.")) { return hostName.substring(1); } else { return hostName; } } // ------------------------------------------------- MapElement Inner Class protected abstract static class MapElement<T> { public final String name; public final T object; public MapElement(String name, T object) { this.name = name; this.object = object; } } // ------------------------------------------------------- Host Inner Class protected static final class MappedHost extends MapElement<Host> { public volatile ContextList contextList; /** * Link to the "real" MappedHost, shared by all aliases. */ private final MappedHost realHost; /** * Links to all registered aliases, for easy enumeration. This field is available only in the "real" MappedHost. * In an alias this field is <code>null</code>. */ private final List<MappedHost> aliases; /** * Constructor used for the primary Host * * @param name The name of the virtual host * @param host The host */ public MappedHost(String name, Host host) { super(name, host); realHost = this; contextList = new ContextList(); aliases = new CopyOnWriteArrayList<>(); } /** * Constructor used for an Alias * * @param alias The alias of the virtual host * @param realHost The host the alias points to */ public MappedHost(String alias, MappedHost realHost) { super(alias, realHost.object); this.realHost = realHost; this.contextList = realHost.contextList; this.aliases = null; } public boolean isAlias() { return realHost != this; } public MappedHost getRealHost() { return realHost; } public String getRealHostName() { return realHost.name; } public Collection<MappedHost> getAliases() { return aliases; } public void addAlias(MappedHost alias) { aliases.add(alias); } public void addAliases(Collection<? extends MappedHost> c) { aliases.addAll(c); } public void removeAlias(MappedHost alias) { aliases.remove(alias); } } // ------------------------------------------------ ContextList Inner Class protected static final class ContextList { public final MappedContext[] contexts; public final int nesting; public ContextList() { this(new MappedContext[0], 0); } private ContextList(MappedContext[] contexts, int nesting) { this.contexts = contexts; this.nesting = nesting; } public ContextList addContext(MappedContext mappedContext, int slashCount) { MappedContext[] newContexts = new MappedContext[contexts.length + 1]; if (insertMap(contexts, newContexts, mappedContext)) { return new ContextList(newContexts, Math.max(nesting, slashCount)); } return null; } public ContextList removeContext(String path) { MappedContext[] newContexts = new MappedContext[contexts.length - 1]; if (removeMap(contexts, newContexts, path)) { int newNesting = 0; for (MappedContext context : newContexts) { newNesting = Math.max(newNesting, slashCount(context.name)); } return new ContextList(newContexts, newNesting); } return null; } } // ---------------------------------------------------- Context Inner Class protected static final class MappedContext extends MapElement<Void> { public volatile ContextVersion[] versions; public MappedContext(String name, ContextVersion firstVersion) { super(name, null); this.versions = new ContextVersion[] { firstVersion }; } } protected static final class ContextVersion extends MapElement<Context> { public final String path; public final int slashCount; public final WebResourceRoot resources; public String[] welcomeResources; public MappedWrapper defaultWrapper = null; public MappedWrapper[] exactWrappers = new MappedWrapper[0]; public MappedWrapper[] wildcardWrappers = new MappedWrapper[0]; public MappedWrapper[] extensionWrappers = new MappedWrapper[0]; public int nesting = 0; private volatile boolean paused; public ContextVersion(String version, String path, int slashCount, Context context, WebResourceRoot resources, String[] welcomeResources) { super(version, context); this.path = path; this.slashCount = slashCount; this.resources = resources; this.welcomeResources = welcomeResources; } public boolean isPaused() { return paused; } public void markPaused() { paused = true; } } // ---------------------------------------------------- Wrapper Inner Class protected static class MappedWrapper extends MapElement<Wrapper> { public final boolean jspWildCard; public final boolean resourceOnly; public MappedWrapper(String name, Wrapper wrapper, boolean jspWildCard, boolean resourceOnly) { super(name, wrapper); this.jspWildCard = jspWildCard; this.resourceOnly = resourceOnly; } } }
Detected license expression
apache-2.0
Detected license expression (SPDX)
Apache-2.0
Percentage of license text
2.19
Copyrights

      
    
Holders

      
    
Authors

      
    
License detections License expression License expression SPDX
apache_2_0-4bde3f57-78aa-4201-96bf-531cba09e7de apache-2.0 Apache-2.0
URL Start line End line
http://www.apache.org/licenses/LICENSE-2.0 9 9