📄 simplemapper1.java
字号:
/*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.tomcat.request;
import org.apache.tomcat.core.*;
import org.apache.tomcat.core.Constants;
import org.apache.tomcat.util.*;
import java.util.*;
/**
* This class will set up the data structures used by a simple patern matching
* alghoritm and use it to extract the path components from the request URI.
*
* The interceptor will be called in standalone case, for "integrated" mode
* we should have all the data from the web server - that means the
* performance of this code is not relevant for production mode if a web
* server is used.
*
* This particular implementation does the following:
* - extract the information that is relevant to matching from the Request
* object. The current implementation deals with the Host header and the
* request URI.
* - Use an external mapper to find the best match.
* - Adjust the request paths
*
* The execution time is proportional with the number of hosts, number of
* context, number of mappings and with the length of the request.
*
* Security mappings are more complex ( method, transport are also part of the
* matching ). We can share the same mapping alghoritm or even the mapper -
* but until security code will be stable it's better to keep it separated.
*
*/
public class SimpleMapper1 extends BaseInterceptor {
ContextManager cm;
PrefixMapper map;
// We store the extension maps as per/context notes.
int ctExtMapNote=-1;
int defaultMapNOTE=-1;
// Property for the PrefixMapper - cache the mapping results
boolean mapCacheEnabled=false;
public SimpleMapper1() {
map=new PrefixMapper();
}
/* -------------------- Support functions -------------------- */
/** Allow the mapper to cache mapping results - resulting in a
* faster match for frequent requests. ( treat this as experimental)
*/
public void setMapCache( boolean v ) {
mapCacheEnabled = v;
map.setMapCache( v );
}
/* -------------------- Initialization -------------------- */
/** Set the context manager. To keep it simple we don't support
* dynamic add/remove for this interceptor.
*/
public void engineInit( ContextManager cm )
throws TomcatException
{
this.cm=cm;
// set-up a per/container note for maps
ctExtMapNote = cm.getNoteId( ContextManager.CONTAINER_NOTE,
"map.extension");
defaultMapNOTE=cm.getNoteId( ContextManager.CONTAINER_NOTE,
"tomcat.map.default");
}
/** Called when a context is added.
*/
public void addContext( ContextManager cm, Context ctx )
throws TomcatException
{
map.addMapping( ctx.getHost(), ctx.getPath(), ctx.getContainer());
}
/** Called when a context is removed from a CM - we must ask the mapper to
remove all the maps related with this context
*/
public void removeContext( ContextManager cm, Context ctx )
throws TomcatException
{
if(debug>0) log( "Removed from maps ");
map.removeAllMappings( ctx.getHost(), ctx.getPath());
// extension mappings are local to ctx, no need to do something
// about that
}
/**
* Associate URL pattern to a set of propreties.
*
* Note that the order of resolution to handle a request is:
*
* exact mapped servlet (eg /catalog)
* prefix mapped servlets (eg /foo/bar/*)
* extension mapped servlets (eg *jsp)
* default servlet
*
*/
public void addContainer( Container ct )
throws TomcatException
{
Context ctx=ct.getContext();
String vhost=ctx.getHost();
String path=ct.getPath();
String ctxP=ctx.getPath();
if(ct.getRoles() != null || ct.getTransport() != null ) {
// it was only a security map, no handler defined
return;
}
switch( ct.getMapType() ) {
case Container.PREFIX_MAP:
// cut /* ( no need to do a string concat for every match )
// workaround for frequent bug in web.xml ( backw. compat )
if( ! path.startsWith( "/" ) ) path="/" + path;
map.addMapping( vhost,
ctxP + path.substring( 0, path.length()-2 ), ct);
if( debug>0 )
log("SM: prefix map " + vhost + ":" + ctxP +
path + " -> " + ct + " " );
break;
case Container.DEFAULT_MAP:
// This will be used if no other map match.
// AVOID USING IT - STATIC FILES SHOULD BE HANDLED BY
// APACHE ( or tomcat )
Container defMapC=ct.getContext().getContainer();
defMapC.setNote( defaultMapNOTE, ct );
if( debug>0 )
log("SM: default map " + vhost + ":" + ctxP +
path + " -> " + ct + " " );
break;
case Container.EXTENSION_MAP:
// Add it per/defaultContainer - as spec require ( it may also be
// possible to support type maps per/Container, i.e. /foo/*.jsp -
// but that would require changes in the spec.
Context mapCtx=ct.getContext();
Container defC=mapCtx.getContainer();
SimpleHashtable eM=(SimpleHashtable) defC.getNote( ctExtMapNote );
if( eM==null ) {
eM=new SimpleHashtable();
defC.setNote( ctExtMapNote, eM );
}
// add it to the Container local maps
eM.put( path.substring( 1 ), ct );
if(debug>0)
log( "SM: extension map " + ctxP + "/" +
path + " " + ct + " " );
break;
case Container.PATH_MAP:
// workaround for frequent bug in web.xml
if( ! path.startsWith( "/" ) ) path="/" + path;
map.addExactMapping( vhost, ctxP + path, ct);
if( debug>0 )
log("SM: exact map " + vhost + ":" + ctxP +
path + " -> " + ct + " " );
break;
}
}
// XXX not implemented - will deal with that after everything else works.
// Remove context will still work
public void removeContainer( Container ct )
throws TomcatException
{
Context ctx=ct.getContext();
String mapping=ct.getPath();
String ctxP=ctx.getPath();
mapping = mapping.trim();
if(debug>0) log( "Remove mapping " + mapping );
}
/* -------------------- Request mapping -------------------- */
/** First step of request porcessing is finding the Context.
*/
public int contextMap( Request req ) {
String path = req.getRequestURI();
if( path==null)
throw new RuntimeException("ASSERT: null path in request URI");
if( path.indexOf("?") >=0 )
throw new RuntimeException("ASSERT: ? in requestURI");
try {
String host=req.getServerName();
if(debug>0) cm.log("Host = " + host);
Container container =(Container)map.getLongestPrefixMatch( host,
path );
if( container == null )
return 404;
if(debug>0)
cm.log("SM: Prefix match " + path + " -> " +
container.getPath() + " " + container.getHandler() +
" " + container.getRoles());
// Once - adjust for prefix and context path
// If cached - we don't need to do it again ( since it is the
// final Container,
// either prefix or extension )
fixRequestPaths( path, req, container );
// if it's default container - try extension match
// if ( container.getMapType() == Container.DEFAULT_MAP ) {
if ( container.getHandler() == null ) {
Container extC = matchExtension( req );
if( extC != null ) {
// change the handler
if( extC.getHandler() != null ) {
fixRequestPaths( path, req, extC );
container=extC;
}
if( debug > 0 )
log("SM: Found extension mapping " +
extC.getHandler());
// change security roles
}
}
// Default map - if present
if( container.getHandler() == null ) {
Container ctxDef=req.getContext().getContainer();
Container defC=(Container)ctxDef.getNote( defaultMapNOTE );
if( defC != null && defC.getHandler() !=null ) {
fixRequestPaths( path, req, defC );
if( debug > 0 )
log("SM: Found default mapping " +
defC.getHandler() + " " + defC.getPath() +
" " + defC.getMapType());
}
}
if(debug>0) log("SM: After mapping " + req + " " +
req.getWrapper());
} catch(Exception ex ) {
ex.printStackTrace();
return 500;
}
return OK;
}
/** No need to do that - we finished everything in the first step.
*
*/
public int requestMap(Request req) {
// No op. All mapping is done in the first step - it's better because
// the alghoritm is more efficient. The only case where those 2 are
// not called togheter is in getContext( "path" ).
//
// We can split it again later if that creates problems - but right
// now it's important to have a clear alghoritm. Note that requestMap
// is _allways_ called after contextMap ( it was asserted in all
// implementations).
// Security check -- disallow any access under WEB-INF or META-INF
String contextPath = null;
Context context = req.getContext();
if (context != null)
contextPath = context.getPath();
if (contextPath == null)
contextPath = "";
String requestURI = req.getRequestURI();
if (requestURI == null)
requestURI = "";
String relativePath =
requestURI.substring(contextPath.length()).toUpperCase();
if (relativePath.equals("/META-INF") ||
relativePath.equals("/WEB-INF") ||
(relativePath.indexOf("/META-INF/") >= 0) ||
(relativePath.indexOf("/WEB-INF/") >= 0))
return 404;
return OK;
}
// -------------------- Implementation methods --------------------
/** Will match an extension - note that Servlet API use special rules
* for mapping extension, different from what is used in existing web
* servers. That makes this code very easy ( only need to deal with
* the last component of the name ), but it's hard to integrate and you
* have no way to use pathInfo.
*/
Container matchExtension( Request req ) {
Context ctx=req.getContext();
String ctxP=ctx.getPath();
String path = req.getServletPath(); // we haven't matched any prefix,
if( path == null ) return null;
String extension=URLUtil.getExtension( path );
if( extension == null ) return null;
if(debug>0)
cm.log("SM: Extension match " + ctxP + " " +
path + " " + extension );
// Find extension maps for the context
SimpleHashtable extM=(SimpleHashtable)ctx.
getContainer().getNote( ctExtMapNote );
if( extM==null ) return null;
// Find the container associated with that extension
Container container= (Container)extM.get(extension);
if (container == null)
return null;
// This container doesn't change the mappings - it only
// has "other" properties ( in the current code security
// constraints
if( container.getHandler() == null) return container;
return container;
}
/** Adjust the paths in request after matching a container
*/
void fixRequestPaths( String path, Request req, Container container ) {
// Set servlet path and path info
// Found a match !
// Adjust paths based on the match
String s=container.getPath();
String ctxP=container.getContext().getPath();
int sLen=s.length();
int pathLen=path.length();
int ctxPLen=ctxP.length();
String pathI=null;
switch( container.getMapType()) {
case Container.PREFIX_MAP:
s=s.substring( 0, sLen -2 );
pathI= path.substring( ctxPLen + sLen - 2, pathLen);
if( debug>0 ) log( "Adjust for prefix map " + s + " " + pathI );
break;
case Container.DEFAULT_MAP:
s=path.substring( ctxPLen );
pathI= null ;
if( debug>0 ) log( "Default map " + s + " " + pathI );
break;
case Container.PATH_MAP:
pathI= null;
// For exact matching - can't have path info ( or it's
// a prefix map )
//path.substring( ctxPLen + sLen , pathLen);
if( debug>0 ) log( "Adjust for path map " +
s + " " + pathI + container.getPath());
break; // keep the path
case Container.EXTENSION_MAP:
/* adjust paths */
s= path.substring( ctxPLen );
pathI=null;
}
req.setServletPath( s );
if( ! "".equals(pathI))
req.setPathInfo(pathI);
Context ctx=container.getContext();
req.setContext(ctx);
req.setWrapper( container.getHandler() );
req.setContainer( container );
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -