前言

说到 Java Web 就是 Tomcat,在后面学习 Java 无文件 WebShell 之前先来观摩一下 Tomcat 源码。

主要看看 filter、servlet 等。


catalina.sh

启动 Tomcat 的方法之一:./catalina.sh start,在脚本中执行的启动如下:

eval exec "\"$_RUNJAVA\"" "\"$CATALINA_LOGGING_CONFIG\"" $LOGGING_MANAGER "$JAVA_OPTS" "$CATALINA_OPTS" \
    -D$ENDORSED_PROP="\"$JAVA_ENDORSED_DIRS\"" \
    -classpath "\"$CLASSPATH\"" \
    -Dcatalina.base="\"$CATALINA_BASE\"" \
    -Dcatalina.home="\"$CATALINA_HOME\"" \
    -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \
    org.apache.catalina.startup.Bootstrap "$@" start

通过 ps 查看到的完整命令如下:

/usr/lib/jvm/jdk1.8.0_191/jre/bin/java 
-Djava.util.logging.config.file=/opt/apache-tomcat-9.0.12/conf/logging.properties 
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager 
-Djdk.tls.ephemeralDHKeySize=2048 
-Djava.protocol.handler.pkgs=org.apache.catalina.webresources 
-Dorg.apache.catalina.security.SecurityListener.UMASK=0027 
-Dignore.endorsed.dirs= 
-classpath /opt/apache-tomcat-9.0.12/bin/bootstrap.jar:/opt/apache-tomcat-9.0.12/bin/tomcat-juli.jar 
-Dcatalina.base=/opt/apache-tomcat-9.0.12 
-Dcatalina.home=/opt/apache-tomcat-9.0.12 
-Djava.io.tmpdir=/opt/apache-tomcat-9.0.12/temp 
org.apache.catalina.startup.Bootstrap start

这里的 classpath 主要是两个 jar:

-classpath /opt/apache-tomcat-9.0.12/bin/bootstrap.jar:/opt/apache-tomcat-9.0.12/bin/tomcat-juli.jar 

以 start 为参数调用了 org.apache.catalina.startup.Bootstrap 类。

load/init

这个类在 bootstrap.jar 中,静态代码块主要是设置各种环境变量,就不提了,看下 main 函数,前一半代码如下:

public static void main(String[] args) {
    synchronized(daemonLock) {
        if (daemon == null) {
            Bootstrap bootstrap = new Bootstrap();

            try {
                bootstrap.init();
            } catch (Throwable var5) {
                ...
            }

            daemon = bootstrap;
        } else {
            Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
        }
    }
    ...

}

首先设置线程的环境,初始化环境的代码在 Bootstrap 的 init 函数中,前一半如下:

public void init() throws Exception {
    this.initClassLoaders();
    Thread.currentThread().setContextClassLoader(this.catalinaLoader);
    SecurityClassLoad.securityClassLoad(this.catalinaLoader);
    if (log.isDebugEnabled()) {
        log.debug("Loading startup class");
    }

    ...
}

首先初始化了类加载器:

private void initClassLoaders() {
    try {
        this.commonLoader = this.createClassLoader("common", (ClassLoader)null);
        if (this.commonLoader == null) {
            this.commonLoader = this.getClass().getClassLoader();
        }

        this.catalinaLoader = this.createClassLoader("server", this.commonLoader);
        this.sharedLoader = this.createClassLoader("shared", this.commonLoader);
    } catch (Throwable var2) {
        handleThrowable(var2);
        log.error("Class loader creation threw exception", var2);
        System.exit(1);
    }

}

可以看到这里会创建三个类加载器,其中 catalinaLoader 和 sharedLoader 是 commonLoader 的子类加载器,createClassLoader 函数中的创建代码如下:

private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception {
    String value = CatalinaProperties.getProperty(name + ".loader");
    if (value != null && !value.equals("")) {
        value = this.replace(value);
        List<Repository> repositories = new ArrayList();
        String[] repositoryPaths = getPaths(value);
        String[] var6 = repositoryPaths;
        int var7 = repositoryPaths.length;

        for(int var8 = 0; var8 < var7; ++var8) {
            String repository = var6[var8];

            try {
                new URL(repository);
                repositories.add(new Repository(repository, RepositoryType.URL));
            } catch (MalformedURLException var11) {
                if (repository.endsWith("*.jar")) {
                    repository = repository.substring(0, repository.length() - "*.jar".length());
                    repositories.add(new Repository(repository, RepositoryType.GLOB));
                } else if (repository.endsWith(".jar")) {
                    repositories.add(new Repository(repository, RepositoryType.JAR));
                } else {
                    repositories.add(new Repository(repository, RepositoryType.DIR));
                }
            }
        }

        return ClassLoaderFactory.createClassLoader(repositories, parent);
    } else {
        return parent;
    }
}

简单来说,就是从配置中获取加载类的路径然后创建类加载器,观察 CatalinaProperties 类的 getProperty 和 loadProperties 函数,可以看到配置来自 /org/apache/catalina/startup/catalina.properties:

common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
server.loader=
shared.loader=

可以看到,commonLoader 类加载器的加载路径是 Tomcat 的 lib 目录,而 catalinaLoader 和 sharedLoader 的加载路径为空,也就是说他们的值都是 commonLoader。

创建完类加载器后, 会加载一个 org.apache.catalina.startup.Catalina 类,实例化并将 sharedLoader,即 commonLoader 放入其中:

Class<?> startupClass = this.catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();
if (log.isDebugEnabled()) {
    log.debug("Setting startup class properties");
}

String methodName = "setParentClassLoader";
Class<?>[] paramTypes = new Class[]{Class.forName("java.lang.ClassLoader")};
Object[] paramValues = new Object[]{this.sharedLoader};
Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
this.catalinaDaemon = startupInstance;

回到 main 函数,start 命令会执行的代码如下:

else if (command.equals("start")) {
    daemon.setAwait(true);
    daemon.load(args);
    daemon.start();
    if (null == daemon.getServer()) {
        System.exit(1);
    }
}

setAwait 只是给一个布尔属性赋值,跳过它看 load 函数。

调用的 load 函数来自 org.apache.catalina.startup.Catalina 类:

    public void load() {
    if (!this.loaded) {
        this.loaded = true;
        long t1 = System.nanoTime();
        this.initDirs();
        this.initNaming();
        this.parseServerXml(true);
        ...
    }
}

此时还没有加载,所以 loaded 为 false,前面的环境变量设置(时间、目录等)跳过,后面的 parseServerXml 函数一看就是跟加载 web 配置文件相关的函数:

protected void parseServerXml(boolean start) {
    ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), this.getConfigFile()));
    File file = this.configFile();
    File serverXmlLocation = null;
    if (this.generateCode) {
        ...
    }

    Catalina.ServerXml serverXml = null;
    if (this.useGeneratedCode) {
        ...
    }

    if (serverXml != null) {
        serverXml.load(this);
    } else {
        try {
            Resource resource = ConfigFileLoader.getSource().getServerXml();
            Throwable var6 = null;

            try {
                Digester digester = start ? this.createStartDigester() : this.createStopDigester();
                InputStream inputStream = resource.getInputStream();
                InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
                inputSource.setByteStream(inputStream);
                digester.push(this);
                if (this.generateCode) {
                    ...
                }

                digester.parse(inputSource);
                if (this.generateCode) {
                    ...
                }
            } 
            ...
        } 
        ...
    }

}

实例化了一个 Digester XML 解析器,并将自身 Catalina 对象放入栈中,用于后面保存解析过程中生成的对象。

配置文件来自 CatalinaBaseConfigurationSource 类,使用 ConfigFileLoader.getSource().getServerXml() 获取配置文件资源:

public Resource getServerXml() throws IOException {
    IOException ioe = null;
    Resource result = null;

    try {
        if (this.serverXmlPath != null && !this.serverXmlPath.equals("conf/server.xml")) {
            result = this.getResource(this.serverXmlPath);
        } else {
            result = super.getServerXml();
        }
    } catch (IOException var6) {
        ioe = var6;
    }

    if (result == null) {
        ...
    }

    if (result == null && ioe != null) {
        throw ioe;
    } else {
        return result;
    }
}

this.serverXmlPath 来自实例化时传入的 this.getConfigFile(),即 Catalina 类的 configFile 属性:

protected String configFile = "conf/server.xml";

获取配置文件资源后,然后开始使用 Digester 解析 xml(参考文章),这是个匹配 -> 回调感觉的解析器,详细的规则可以在 createStartDigester 函数中看到,比如这三句:

digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
digester.addSetProperties("Server");
digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");

这里的规则匹配 Server 标签,addObjectCreate 规则在没有 className 属性的情况下默认实例化一个 org.apache.catalina.core.StandardServer 类并放入栈中:

public void begin(String namespace, String name, Attributes attributes) throws Exception {
    String realClassName = this.getRealClassName(attributes);
    if (realClassName == null) {
        throw new NullPointerException(sm.getString("rule.noClassName", new Object[]{namespace, name}));
    } else {
        Class<?> clazz = this.digester.getClassLoader().loadClass(realClassName);
        Object instance = clazz.getConstructor().newInstance();
        this.digester.push(instance);
        ...

    }
}

protected String getRealClassName(Attributes attributes) {
    String realClassName = this.className;
    if (this.attributeName != null) {
        String value = attributes.getValue(this.attributeName);
        if (value != null) {
            realClassName = value;
        }
    }

    return realClassName;
}

addSetProperties 规则用其他属性给 StandardServer 类赋值,跳过。

addSetNext 规则将 StandardServer 对象作为参数,调用 Catalina 对象的 setServer 函数将 StandardServer 保存起来。

解析完 server.xml 之后,Tomcat 服务端对象已经创建好并按层次保存在 Catalina 对象的 server 属性中了,回到 load 函数,接下来的代码如下:

Server s = this.getServer();
if (s != null) {
    this.getServer().setCatalina(this);
    this.getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    this.getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
    this.initStreams();

    try {
        this.getServer().init();
    } catch (LifecycleException var5) {
        ...
    }

    if (log.isInfoEnabled()) {
        log.info(...);
    }

}

调用 this.getServer().init() 开始初始化服务端,看了一下各个模块类的 initInternal 函数,好像没什么特别的,那就直接回到 main 函数里面 daemon.start() 这一句吧。

start

观察 server.xml 中的配置,可以发现一个 Service 就是一个 Web 服务,所以我们关注 Service 的启动,Service 的配置如下:

<Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <Engine name="Catalina" defaultHost="localhost">
        <Realm className="org.apache.catalina.realm.LockOutRealm">
            <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                   resourceName="UserDatabase"/>
        </Realm>
        <Host name="localhost"  appBase="webapps"
              unpackWARs="true" autoDeploy="true">
            <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                   prefix="localhost_access_log" suffix=".txt"
                   pattern="%h %l %u %t &quot;%r&quot; %s %b" />
        </Host>
    </Engine>
</Service>

观察里面的配置项,可以看出 Connector 应该用于处理 HTTP 协议,而 Host 部分应该用于处理 HTTP 请求。

跟 init 类似,start 主要调用的是 startInternal 函数,Service 的 startInternal 函数如下:

protected void startInternal() throws LifecycleException {
    if (log.isInfoEnabled()) {
        log.info(sm.getString("standardService.start.name", new Object[]{this.name}));
    }

    this.setState(LifecycleState.STARTING);
    if (this.engine != null) {
        synchronized(this.engine) {
            this.engine.start();
        }
    }

    synchronized(this.executors) {
        ...
    }

    this.mapperListener.start();
    synchronized(this.connectorsLock) {
        Connector[] var10 = this.connectors;
        int var11 = var10.length;

        for(int var4 = 0; var4 < var11; ++var4) {
            Connector connector = var10[var4];
            if (connector.getState() != LifecycleState.FAILED) {
                connector.start();
            }
        }

    }
}

因为配置文件中没有 executors,所以就跳过了,简单来说就是启动 engine、mapperListener 和 connector,我们先看看 engine:

protected synchronized void startInternal() throws LifecycleException {
    if (log.isInfoEnabled()) {
        log.info(sm.getString("standardEngine.start", new Object[]{ServerInfo.getServerInfo()}));
    }

    super.startInternal();
}

engine 的启动没什么特别的,再看看 mapperListener:

public void startInternal() throws LifecycleException {
    this.setState(LifecycleState.STARTING);
    Engine engine = this.service.getContainer();
    if (engine != null) {
        this.findDefaultHost();
        this.addListeners(engine);
        Container[] conHosts = engine.findChildren();
        Container[] var3 = conHosts;
        int var4 = conHosts.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            Container conHost = var3[var5];
            Host host = (Host)conHost;
            if (!LifecycleState.NEW.equals(host.getState())) {
                this.registerHost(host);
            }
        }

    }
}

看代码中的命名,engine 应该就是我们在 Tomcat 中常常看到的 container。

mapperListener 启动时首先调用 findDefaultHost:

private void findDefaultHost() {
    Engine engine = this.service.getContainer();
    String defaultHost = engine.getDefaultHost();
    boolean found = false;
    if (defaultHost != null && defaultHost.length() > 0) {
        Container[] containers = engine.findChildren();
        Container[] var5 = containers;
        int var6 = containers.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Container container = var5[var7];
            Host host = (Host)container;
            if (defaultHost.equalsIgnoreCase(host.getName())) {
                found = true;
                break;
            }

            String[] aliases = host.findAliases();
            String[] var11 = aliases;
            int var12 = aliases.length;

            for(int var13 = 0; var13 < var12; ++var13) {
                String alias = var11[var13];
                if (defaultHost.equalsIgnoreCase(alias)) {
                    found = true;
                    break;
                }
            }
        }
    }

    if (found) {
        this.mapper.setDefaultHostName(defaultHost);
    } else {
        log.error(sm.getString("mapperListener.unknownDefaultHost", new Object[]{defaultHost, this.service}));
    }

}

可以看到,只有在 engine 的 children 中找到一个其 name 和 defaultHost 相对应的,才会给 this.mapper 赋值。

engine 的 children 是通过 addChild 添加的,具体的解析规则可以到 HostRuleSet 类中找到:

digester.addObjectCreate(this.prefix + "Host", "org.apache.catalina.core.StandardHost", "className");
digester.addSetProperties(this.prefix + "Host");
...
digester.addSetNext(this.prefix + "Host", "addChild", "org.apache.catalina.Container");

简单来说它的 children 就是 Host,因为 Host 中配置的 name 和 engine 中的 defaultHost 相同,所以会将 defaultHost 赋给 mapper。

findDefaultHost 结束后,调用 addListeners 将自身这个 MapperListener 对象放入 Host 及 Host 的 children 中(默认配置时应该为空)。

最后调用 registerHost,将 Host 注册进 mapper 中(感觉 MapperListener 跟 Host 互相套娃的)。

回到 Service,下一步执行的是 connector 的 start:

protected void startInternal() throws LifecycleException {
    if (this.getPortWithOffset() < 0) {
        throw new LifecycleException(sm.getString("coyoteConnector.invalidPort", new Object[]{this.getPortWithOffset()}));
    } else {
        this.setState(LifecycleState.STARTING);

        try {
            this.protocolHandler.start();
        } catch (Exception var2) {
            throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerStartFailed"), var2);
        }
    }
}

这里启动了 protocolHandler,看名字就是用于处理协议的类,是在 connector 的构造函数中初始化的:

ProtocolHandler p = null;

try {
    p = ProtocolHandler.create(protocol, apr);
} catch (Exception var5) {
    log.error(sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"), var5);
}

if (p != null) {
    this.protocolHandler = p;
    this.protocolHandlerClassName = this.protocolHandler.getClass().getName();
} else {
    this.protocolHandler = null;
    this.protocolHandlerClassName = protocol;
}

而配置的协议为 HTTP/1.1:

static ProtocolHandler create(String protocol, boolean apr) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
    if (protocol == null || "HTTP/1.1".equals(protocol) || !apr && Http11NioProtocol.class.getName().equals(protocol) || apr && Http11AprProtocol.class.getName().equals(protocol)) {
        return (ProtocolHandler)(apr ? new Http11AprProtocol() : new Http11NioProtocol());
    } else if ("AJP/1.3".equals(protocol) || !apr && AjpNioProtocol.class.getName().equals(protocol) || apr && AjpAprProtocol.class.getName().equals(protocol)) {
        return (ProtocolHandler)(apr ? new AjpAprProtocol() : new AjpNioProtocol());
    } else {
        Class<?> clazz = Class.forName(protocol);
        return (ProtocolHandler)clazz.getConstructor().newInstance();
    }
}

默认没有开启 apr 的情况下,处理 HTTP 协议的类应该就是 Http11NioProtocol。

shutdown

回到 Catalina 类的 start 函数,最后会执行 await 开始监听请求:

if (this.await) {
    this.await();
    this.stop();
}

this.await 在 Bootstrap 对象执行 daemon.setAwait(true) 时赋值为 true,await 函数会执行到 Server 的 await 函数,首先在 8005 端口创建了一个监听本地请求的 ServerSocket:

this.awaitSocket = new ServerSocket(this.getPortWithOffset(), 1, InetAddress.getByName(this.address));

然后从 client 读取字符,如果收到的命令为 shutdown 就会中断监听并关闭 socket:

boolean match = command.toString().equals(this.shutdown);
if (match) {
    log.info(sm.getString("standardServer.shutdownViaPort"));
    var32 = false;
    break;
}
...
serverSocket = this.awaitSocket;
this.awaitThread = null;
this.awaitSocket = null;
if (serverSocket != null) {
    try {
        serverSocket.close();
    } catch (IOException var65) {
    }
}

这里的死循环结束之后,就会结束 await 函数并调用 this.stop:

public void stop() {
    try {
        if (this.useShutdownHook) {
            Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
            LogManager logManager = LogManager.getLogManager();
            if (logManager instanceof ClassLoaderLogManager) {
                ((ClassLoaderLogManager)logManager).setUseShutdownHook(true);
            }
        }
    } catch (Throwable var3) {
        ExceptionUtils.handleThrowable(var3);
    }

    try {
        Server s = this.getServer();
        LifecycleState state = s.getState();
        if (LifecycleState.STOPPING_PREP.compareTo(state) > 0 || LifecycleState.DESTROYED.compareTo(state) < 0) {
            s.stop();
            s.destroy();
        }
    } catch (LifecycleException var4) {
        log.error(sm.getString("catalina.stopError"), var4);
    }

}

调用 Server 的 stop 和 destroy,大体来说就是一级级下去分别调用了 Service、Engine 等模块类的停止函数,就把整个 Tomcat 服务关闭了。

当我们使用 stop 命令关闭 Tomcat 时,Catalina 类会解析配置文件,获取 IP 端口等信息并传输 shutdown 命令:

try {
    Socket socket = new Socket(s.getAddress(), s.getPortWithOffset());
    Throwable var4 = null;

    try {
        OutputStream stream = socket.getOutputStream();
        Throwable var6 = null;

        try {
            String shutdown = s.getShutdown();

            for(int i = 0; i < shutdown.length(); ++i) {
                stream.write(shutdown.charAt(i));
            }

            stream.flush();
        } 
        ...

        }
    } 
    ...
}

这样一来就实现了关闭 Tomcat。

await

回到 connector 中执行的这一句代码:

this.protocolHandler.start();

这里就开始启动 web 部分的监听了,来到 Http11NioProtocol 父类 AbstractProtocol 的 start 函数:

public void start() throws Exception {
    if (this.getLog().isInfoEnabled()) {
        this.getLog().info(sm.getString("abstractProtocolHandler.start", new Object[]{this.getName()}));
        this.logPortOffset();
    }

    this.endpoint.start();
    this.monitorFuture = this.getUtilityExecutor().scheduleWithFixedDelay(new Runnable() {
        public void run() {
            if (!AbstractProtocol.this.isPaused()) {
                AbstractProtocol.this.startAsyncTimeout();
            }

        }
    }, 0L, 60L, TimeUnit.SECONDS);
}

首先调用了 endpoint.start,看下 endpoint 是个什么对象:

public Http11NioProtocol() {
    super(new NioEndpoint());
}

然后来到 Http11NioProtocol 类的 startInternal 函数:

public void startInternal() throws Exception {
    if (!this.running) {
        this.running = true;
        this.paused = false;
        if (this.socketProperties.getProcessorCache() != 0) {
            this.processorCache = new SynchronizedStack(128, this.socketProperties.getProcessorCache());
        }

        if (this.socketProperties.getEventCache() != 0) {
            this.eventCache = new SynchronizedStack(128, this.socketProperties.getEventCache());
        }

        if (this.socketProperties.getBufferPool() != 0) {
            this.nioChannels = new SynchronizedStack(128, this.socketProperties.getBufferPool());
        }

        if (this.getExecutor() == null) {
            this.createExecutor();
        }

        this.initializeConnectionLatch();
        this.poller = new NioEndpoint.Poller();
        Thread pollerThread = new Thread(this.poller, this.getName() + "-ClientPoller");
        pollerThread.setPriority(this.threadPriority);
        pollerThread.setDaemon(true);
        pollerThread.start();
        this.startAcceptorThread();
    }

}

Tomcat 中的客户端请求处理是通过 Acceptor 和 Poller 两部分实现的,我们先看看 Acceptor:

public void run() {
    byte errorDelay = 0;

    while(this.endpoint.isRunning()) {
        ...

        this.state = Acceptor.AcceptorState.RUNNING;

        try {
            this.endpoint.countUpOrAwaitConnection();
            if (!this.endpoint.isPaused()) {
                Object socket = null;

                try {
                    socket = this.endpoint.serverSocketAccept();
                } catch (Exception var6) {
                    this.endpoint.countDownConnection();
                    if (!this.endpoint.isRunning()) {
                        break;
                    }

                    this.handleExceptionWithDelay(errorDelay);
                    throw var6;
                }

                errorDelay = 0;
                if (this.endpoint.isRunning() && !this.endpoint.isPaused()) {
                    if (!this.endpoint.setSocketOptions(socket)) {
                        this.endpoint.closeSocket(socket);
                    }
                } else {
                    this.endpoint.destroySocket(socket);
                }
            }
        } 
        ...
    }

    this.state = Acceptor.AcceptorState.ENDED;
}

调用 this.endpoint.serverSocketAccept 获取一个 SocketChannel:

protected SocketChannel serverSocketAccept() throws Exception {
    return this.serverSock.accept();
}

accept 函数会一直阻塞直到新的请求到来。serverSock 的初始化在 initServerSocket 函数中(往上可以追溯到 Connector 的 this.protocolHandler.init() 这一句):

this.serverSock = ServerSocketChannel.open();
this.socketProperties.setProperties(this.serverSock.socket());
InetSocketAddress addr = new InetSocketAddress(this.getAddress(), this.getPortWithOffset());
this.serverSock.socket().bind(addr, this.getAcceptCount());

可以看到监听的地址和端口来自自身的两个属性,他们的赋值方式要追溯到 connector 的创建去了,解析 XML 时:

Connector con = new Connector(protocolName);
...

this.digester.push(con);

创建一个 Connector 并放入栈中(如上文所说,protocolHandler 就是在 connector 构造函数中创建的),然后下一条规则:

digester.addSetProperties("Server/Service/Connector", new String[]{"executor", "sslImplementationName", "protocol"});

除了这三个属性,其他都会进行赋值,SetPropertiesRule 的赋值处理如下:

if (!IntrospectionUtils.setProperty(top, name, value, true, actualMethod)) {
    if (this.digester.getRulesValidation() && !"optional".equals(name)) {
        this.digester.log.warn(sm.getString("rule.noProperty", new Object[]{this.digester.match, name, value}));
    }
}

IntrospectionUtils 的处理比较别致,找不到特定属性的 setter 时会调用 setProperty:

if ("setProperty".equals(method.getName())) {
    if (method.getReturnType() == Boolean.TYPE) {
        setPropertyMethodBool = method;
    } else {
        setPropertyMethodVoid = method;
    }
}

对于 connector 来说就是:

public boolean setProperty(String name, String value) {
    return this.protocolHandler == null ? false : IntrospectionUtils.setProperty(this.protocolHandler, name, value);
}

对于 protocolHandler(Http11NioProtocol)来说就是:

public void setAddress(InetAddress ia) {
    this.endpoint.setAddress(ia);
}

所以一层层反射下去就会给 endpoint 赋值。

不过又有个问题,connector 反射的参数类型为 String,而 protocolHandler 却是 InetAddress,所以中间还存在一个类型转换的操作。再回到了 IntrospectionUtils,这里又有个对 InetAddress 类型参数的别致操作:

if ("java.net.InetAddress".equals(paramType.getName())) {
    try {
        params[0] = InetAddress.getByName(value);
    } catch (UnknownHostException var20) {
        if (log.isDebugEnabled()) {
            log.debug("IntrospectionUtils: Unable to resolve host name:" + value);
        }

        ok = false;
    }

    if (actualMethod != null) {
        actualMethod.append(method.getName()).append("(InetAddress.getByName(\"").append(value).append("\"))");
    }
}

所以在 connector 中配置 address 可以设置 Tomcat 的监听地址。

不过看 server.xml,里面是没有配置 address 的,所以就是默认的 0.0.0.0:

public InetSocketAddress(InetAddress addr, int port) {
    holder = new InetSocketAddressHolder(
        null,
        addr == null ? InetAddress.anyLocalAddress() : addr,
        checkPort(port));
}

回到 Acceptor,在接收到请求并获取到一个 SocketChannel 之后,会调用 setSocketOptions:

protected boolean setSocketOptions(SocketChannel socket) {
    Object socketWrapper = null;

    try {
        NioChannel channel = null;
        if (this.nioChannels != null) {
            channel = (NioChannel)this.nioChannels.pop();
        }

        if (channel == null) {
            SocketBufferHandler bufhandler = new SocketBufferHandler(this.socketProperties.getAppReadBufSize(), this.socketProperties.getAppWriteBufSize(), this.socketProperties.getDirectBuffer());
            if (this.isSSLEnabled()) {
                channel = new SecureNioChannel(bufhandler, this.selectorPool, this);
            } else {
                channel = new NioChannel(bufhandler);
            }
        }

        NioEndpoint.NioSocketWrapper newWrapper = new NioEndpoint.NioSocketWrapper((NioChannel)channel, this);
        ((NioChannel)channel).reset(socket, newWrapper);
        this.connections.put(socket, newWrapper);
        socket.configureBlocking(false);
        this.socketProperties.setProperties(socket.socket());
        newWrapper.setReadTimeout((long)this.getConnectionTimeout());
        newWrapper.setWriteTimeout((long)this.getConnectionTimeout());
        newWrapper.setKeepAliveLeft(this.getMaxKeepAliveRequests());
        this.poller.register((NioChannel)channel, newWrapper);
        return true;
    } 
    ...
}

新建一个 NioChannel,将 SocketChannel 和 NioSocketWrapper 放进去,再将新生成的 channel 和 wrapper 一起放入 connections Map 并注册进 Poller 中,注册进 Poller 中的代码如下:

public void register(NioChannel socket, NioEndpoint.NioSocketWrapper socketWrapper) {
    socketWrapper.interestOps(1);
    NioEndpoint.PollerEvent event = null;
    if (NioEndpoint.this.eventCache != null) {
        event = (NioEndpoint.PollerEvent)NioEndpoint.this.eventCache.pop();
    }

    if (event == null) {
        event = new NioEndpoint.PollerEvent(socket, 256);
    } else {
        event.reset(socket, 256);
    }

    this.addEvent(event);
}

简单来说就是会添加进 events 这个队列中,Acceptor 的 run 函数大致结束了,接下来看看 Poller 的:

public void run() {
    while(true) {
        boolean hasEvents = false;

        label59: {
            try {
                if (!this.close) {
                    hasEvents = this.events();
                    ...
                }

                if (!this.close) {
                    break label59;
                }

                ...
            } 
            ...
        }

        ...
    }
}

看看 events 函数:

public boolean events() {
    boolean result = false;
    NioEndpoint.PollerEvent pe = null;
    int i = 0;

    for(int size = this.events.size(); i < size && (pe = (NioEndpoint.PollerEvent)this.events.poll()) != null; ++i) {
        result = true;
        NioChannel channel = pe.getSocket();
        NioEndpoint.NioSocketWrapper socketWrapper = channel.getSocketWrapper();
        int interestOps = pe.getInterestOps();
        if (interestOps == 256) {
            try {
                channel.getIOChannel().register(this.getSelector(), 1, socketWrapper);
            } catch (Exception var12) {
                NioEndpoint.log.error(AbstractEndpoint.sm.getString("endpoint.nio.registerFail"), var12);
            }
        } else {
            ...
        }

        if (NioEndpoint.this.running && !NioEndpoint.this.paused && NioEndpoint.this.eventCache != null) {
            pe.reset();
            NioEndpoint.this.eventCache.push(pe);
        }
    }

    return result;
}

调用 poll 从中队列中取出一个 event(并从队列中删除),关注这一句:

channel.getIOChannel().register(this.getSelector(), 1, socketWrapper);

这里将 channel 注册到了 selector 上,并监听 read 事件:

public static final int OP_READ = 1 << 0;

继续看 run 函数:

if (this.wakeupCounter.getAndSet(-1L) > 0L) {
    this.keyCount = this.selector.selectNow();
} else {
    this.keyCount = this.selector.select(NioEndpoint.this.selectorTimeout);
}

this.wakeupCounter.set(0L);

wakeupCounter 是一个初始为 0 的计数器,每次 addEvent 就会 +1,没有 event 时就会为 0,从而进入 this.selector.select 的阻塞,超时时间为 1 秒。

如果存在有效事件,keyCount 就是有效事件的数量,后面就开始处理这些事件:

Iterator iterator = this.keyCount > 0 ? this.selector.selectedKeys().iterator() : null;

while(iterator != null && iterator.hasNext()) {
    SelectionKey sk = (SelectionKey)iterator.next();
    NioEndpoint.NioSocketWrapper socketWrapper = (NioEndpoint.NioSocketWrapper)sk.attachment();
    if (socketWrapper == null) {
        iterator.remove();
    } else {
        iterator.remove();
        this.processKey(sk, socketWrapper);
    }
}

看看 processKey 函数,关注 read 的部分:

if (sk.isReadable()) {
    if (socketWrapper.readOperation != null) {
        if (!socketWrapper.readOperation.process()) {
            closeSocket = true;
        }
    } else if (!NioEndpoint.this.processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) {
        closeSocket = true;
    }
}

下一步 processSocket:

if (handshake == 0) {
    AbstractEndpoint.Handler.SocketState state = AbstractEndpoint.Handler.SocketState.OPEN;
    if (this.event == null) {
        state = NioEndpoint.this.getHandler().process(this.socketWrapper, SocketEvent.OPEN_READ);
    } else {
        state = NioEndpoint.this.getHandler().process(this.socketWrapper, this.event);
    }

    if (state == AbstractEndpoint.Handler.SocketState.CLOSED) {
        poller.cancelledKey(socket.getIOChannel().keyFor(poller.getSelector()), this.socketWrapper);
    }
}

handshake 为 0 代表 TCP 握手完成(大概),然后调用 handler 的 process 函数,这里的 handler 是个 ConnectionHandler 类,在 protocolHandler 实例化时赋值(AbstractHttp11Protocol 类构造函数):

AbstractProtocol.ConnectionHandler<S> cHandler = new AbstractProtocol.ConnectionHandler(this);
this.setHandler(cHandler);
this.getEndpoint().setHandler(cHandler);

process:

if (processor == null) {
    processor = this.getProtocol().createProcessor();
    this.register(processor);
    if (this.getLog().isDebugEnabled()) {
        this.getLog().debug(AbstractProtocol.sm.getString("abstractConnectionHandler.processorCreate", new Object[]{processor}));
    }
}

第一次访问时没有 processor,所以会调用 createProcessor 生成一个,AbstractHttp11Protocol 类的 createProcessor 函数如下:

protected Processor createProcessor() {
    Http11Processor processor = new Http11Processor(this, this.adapter);
    return processor;
}

生成的是 Http11Processor,然后调用其 process 函数:

state = processor.process(wrapper, status);

然后来到 AbstractProcessorLight 类的 process:

else if (status == SocketEvent.OPEN_READ) {
    state = this.service(socketWrapper);
}

回到 Http11Processor 类的 service 函数:

this.getAdapter().service(this.request, this.response);

可以看到,这里有 request 和 response 两个属性,都来自 org.apache.coyote 包,找一下他们的生成方式,在父类 AbstractProcessor 的构造函数中:

public AbstractProcessor(Adapter adapter) {
    this(adapter, new Request(), new Response());
}

protected AbstractProcessor(Adapter adapter, Request coyoteRequest, Response coyoteResponse) {
    this.hostNameC = new char[0];
    this.asyncTimeout = -1L;
    this.asyncTimeoutGeneration = 0L;
    this.socketWrapper = null;
    this.errorState = ErrorState.NONE;
    this.adapter = adapter;
    this.asyncStateMachine = new AsyncStateMachine(this);
    this.request = coyoteRequest;
    this.response = coyoteResponse;
    this.response.setHook(this);
    this.request.setResponse(this.response);
    this.request.setHook(this);
    this.userDataHelper = new UserDataHelper(this.getLog());
}

可以看到 response 就存放在 request 中,Http11Processor 放在 request 和 response 中。不过仔细观察这个 request 中的属性,会发现他们似乎并不是我们习惯的那个 request。

而 Adapter 的赋值要追溯到 Connector 的 initInternal 函数:

this.adapter = new CoyoteAdapter(this);

跟进 CoyoteAdapter 类的 service 函数:

Request request = (Request)req.getNote(1);
Response response = (Response)res.getNote(1);
if (request == null) {
    request = this.connector.createRequest();
    request.setCoyoteRequest(req);
    response = this.connector.createResponse();
    response.setCoyoteResponse(res);
    request.setResponse(response);
    response.setRequest(request);
    req.setNote(1, request);
    res.setNote(1, response);
    req.getParameters().setQueryStringCharset(this.connector.getURICharset());
}

可以看到我们习惯的 request(org.apache.catalina.connector),第一次访问时 request(org.apache.coyote)是刚刚生成的,所以 note 属性里面没有保存 request(org.apache.catalina.connector),这时会调用 createRequest/createResponse 从 connector 生成并保存起来:

public Request createRequest() {
    return new Request(this);
}

public Response createResponse() {
    int size = this.protocolHandler.getDesiredBufferSize();
    return size > 0 ? new Response(size) : new Response();
}

request 和 response 互相嵌套,connector 套进了 request 中。

然后开始处理 HTTP 请求:

postParseSuccess = this.postParseRequest(req, request, res, response);
if (postParseSuccess) {
    request.setAsyncSupported(this.connector.getService().getContainer().getPipeline().isAsyncSupported());
    this.connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
}

调用 postParseRequest 解析请求,如果解析成功就会开始反射,看这一长串链式执行,可以简化成 engine.getPipeline().getFirst().invoke(request, response),我们回到 engine 去看看:

protected final Pipeline pipeline = new StandardPipeline(this);

StandardPipeline 类的 getFirst 函数如下:

public Valve getFirst() {
    return this.first != null ? this.first : this.basic;
}

调试可以发现,这里的 first 为 null,basic 来自 engine 的构造函数:

this.pipeline.setBasic(new StandardEngineValve());

所以最后的反射函数如下:

public final void invoke(Request request, Response response) throws IOException, ServletException {
    Host host = request.getHost();
    if (host != null) {
        if (request.isAsyncSupported()) {
            request.setAsyncSupported(host.getPipeline().isAsyncSupported());
        }

        host.getPipeline().getFirst().invoke(request, response);
    }
}

看看这里的 host 是怎么生成的,回到 postParseRequest,里面有一句:

this.connector.getService().getMapper().map(serverName, decodedURI, version, request.getMappingData());

会来到 Mapper 类的 internalMap 函数:

Mapper.MappedHost[] hosts = this.hosts;
Mapper.MappedHost mappedHost = (Mapper.MappedHost)exactFindIgnoreCase(hosts, host);
...

mappingData.host = (Host)mappedHost.object;

hosts 是一个数组,通过 addHost 函数进行添加,再往上回到 MapperListener 类的 registerHost 函数中,在前面 start 的时候会注册进去:

public void startInternal() throws LifecycleException {
    this.setState(LifecycleState.STARTING);
    Engine engine = this.service.getContainer();
    if (engine != null) {
        this.findDefaultHost();
        this.addListeners(engine);
        Container[] conHosts = engine.findChildren();
        Container[] var3 = conHosts;
        int var4 = conHosts.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            Container conHost = var3[var5];
            Host host = (Host)conHost;
            if (!LifecycleState.NEW.equals(host.getState())) {
                this.registerHost(host);
            }
        }

    }
}


可以看到源数据来自 engine,即 server.xml 中配置并解析生成的 standardHost 对象。然后再调用 exactFindIgnoreCase 从这些 hosts 中获取该次请求对应的 host:

private static final <T, E extends Mapper.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;
}

简单来说就是将访问的 host 和 sever.xml 中配置的 host 匹配一下。

除了 host,mappingData 中还存放了其他重要属性,比如说 context,我们继续往下看 internalMap 函数:

Mapper.ContextList contextList = mappedHost.contextList;
Mapper.MappedContext[] contexts = contextList.contexts;
int pos = find(contexts, (CharChunk)uri);

这里出现了一个 host 中的属性 contextList,它是在 registerHost 的时候注册的:

private void registerHost(Host host) {
    String[] aliases = host.findAliases();
    this.mapper.addHost(host.getName(), aliases, host);
    Container[] var3 = host.findChildren();
    int var4 = var3.length;

    for(int var5 = 0; var5 < var4; ++var5) {
        Container container = var3[var5];
        if (container.getState().isAvailable()) {
            this.registerContext((Context)container);
        }
    }

    this.findDefaultHost();
    ...

}

最后在 addContextVersion 函数中:

Mapper.ContextVersion newContextVersion = new Mapper.ContextVersion(version, path, slashCount, context, resources, welcomeResources);
if (wrappers != null) {
    this.addWrappers(newContextVersion, wrappers);
}

Mapper.ContextList contextList = mappedHost.contextList;
Mapper.MappedContext mappedContext = (Mapper.MappedContext)exactFind(contextList.contexts, (String)path);
if (mappedContext == null) {
    mappedContext = new Mapper.MappedContext(path, newContextVersion);
    Mapper.ContextList newContextList = contextList.addContext(mappedContext, slashCount);
    if (newContextList != null) {
        this.updateContextList(mappedHost, newContextList);
        this.contextObjectToContextVersionMap.put(context, newContextVersion);
    }
}

context 和 contextVersion 放入 MappedContext,再放入 contextList 中。

context 配置在 server.xml 的 host 模块下面,代表一个 web 项目(war 包或其解压后的目录),而因为 Tomcat 开启了 autoDeploy 和 unpackWARs,所以文件中没有静态配置,而是由 Tomcat 进行动态加载。顺带一提的是 context 的 name 就是配置中的 path,在 setPath 函数中:

if (this.getName() == null) {
    this.setName(this.path);
}

继续 internalMap,通过 uri 匹配到对应的 context 后,会将其放入 mappingData 中。(standardContext 就保存在 contextVersion 中)

还有一个重要属性 wrapper,获取 context 之后,会调用 internalMapWrapper 获取相应的 wrapper,先来看看 wrapper 的生成方式,context 中的 wrapper 在 ContextConfig 类中生成,这个类会解析 web.xml 并做相应处理,其 configureContext 函数一部分如下:

var35 = webxml.getServlets().values().iterator();

Iterator var7;
while(var35.hasNext()) {
    ServletDef servlet = (ServletDef)var35.next();
    Wrapper wrapper = this.context.createWrapper();
    ...
    this.context.addChild(wrapper);
}

生成 wrapper 然后用 addChild 添加到 context 中,可以发现 wrapper 和 servlet 是对应的,中间有一大段将 servlet 相关数据存储在 wrapper 中的代码被我省略了,接下来回到 standardContext 类的 createWrapper 函数:

wrapper = new StandardWrapper();

wrapper 是一个 StandardWrapper 类,其他的没有什么特别操作了。在注册 context 的时候(registerContext),这些 wrapper 会经过 prepareWrapperMappingInfo 函数处理,合成一个 List:

private void prepareWrapperMappingInfo(Context context, Wrapper wrapper, List<WrapperMappingInfo> wrappers) {
    String wrapperName = wrapper.getName();
    boolean resourceOnly = context.isResourceOnlyServlet(wrapperName);
    String[] mappings = wrapper.findMappings();
    String[] var7 = mappings;
    int var8 = mappings.length;

    for(int var9 = 0; var9 < var8; ++var9) {
        String mapping = var7[var9];
        boolean jspWildCard = wrapperName.equals("jsp") && mapping.endsWith("/*");
        wrappers.add(new WrapperMappingInfo(mapping, wrapper, jspWildCard, resourceOnly));
    }

}

然后通过 addWrappers 分门别类组装成 MappedWrapper 放入 contextVersion:

private void addWrappers(Mapper.ContextVersion contextVersion, Collection<WrapperMappingInfo> wrappers) {
    Iterator var3 = wrappers.iterator();

    while(var3.hasNext()) {
        WrapperMappingInfo wrapper = (WrapperMappingInfo)var3.next();
        this.addWrapper(contextVersion, wrapper.getMapping(), wrapper.getWrapper(), wrapper.isJspWildCard(), wrapper.isResourceOnly());
    }

}

protected void addWrapper(Mapper.ContextVersion context, String path, Wrapper wrapper, boolean jspWildCard, boolean resourceOnly) {
    synchronized(context) {
        String name;
        Mapper.MappedWrapper newWrapper;
        Mapper.MappedWrapper[] oldWrappers;
        Mapper.MappedWrapper[] newWrappers;
        if (path.endsWith("/*")) {
            name = path.substring(0, path.length() - 2);
            newWrapper = new Mapper.MappedWrapper(name, wrapper, jspWildCard, resourceOnly);
            oldWrappers = context.wildcardWrappers;
            newWrappers = new Mapper.MappedWrapper[oldWrappers.length + 1];
            if (insertMap(oldWrappers, newWrappers, newWrapper)) {
                context.Wrappers = newWrappers;
                int slashCount = slashCount(newWrapper.name);
                if (slashCount > context.nesting) {
                    context.nesting = slashCount;
                }
            }
        } else if (path.startsWith("*.")) {
            name = path.substring(2);
            newWrapper = new Mapper.MappedWrapper(name, wrapper, jspWildCard, resourceOnly);
            oldWrappers = context.extensionWrappers;
            newWrappers = new Mapper.MappedWrapper[oldWrappers.length + 1];
            if (insertMap(oldWrappers, newWrappers, newWrapper)) {
                context.extensionWrappers = newWrappers;
            }
        } else if (path.equals("/")) {
            Mapper.MappedWrapper newWrapper = new Mapper.MappedWrapper("", wrapper, jspWildCard, resourceOnly);
            context.defaultWrapper = newWrapper;
        } else {
            if (path.length() == 0) {
                name = "/";
            } else {
                name = path;
            }

            newWrapper = new Mapper.MappedWrapper(name, wrapper, jspWildCard, resourceOnly);
            oldWrappers = context.exactWrappers;
            newWrappers = new Mapper.MappedWrapper[oldWrappers.length + 1];
            if (insertMap(oldWrappers, newWrappers, newWrapper)) {
                context.exactWrappers = newWrappers;
            }
        }

    }
}

主要有几个分类:

  • wildcardWrappers:/* 通配符结尾的路径
  • extensionWrappers:某一类后缀的路径
  • defaultWrapper:/ 结尾的默认(首页?)路径
  • exactWrappers:全匹配的详细路径

现在回到 internalMapWrapper,可以看到匹配成功后,会将 wrapper 放入 mappingData 中。

回到反射,下一步是 StandardHostValve 类,获取的 context 就是前面 internalMap 函数匹配到的那个:

Context context = request.getContext();
...
context.getPipeline().getFirst().invoke(request, response);

然后是 StandardContextValve 类:

public final void invoke(Request request, Response response) throws IOException, ServletException {
    MessageBytes requestPathMB = request.getRequestPathMB();
    if (!requestPathMB.startsWithIgnoreCase("/META-INF/", 0) && !requestPathMB.equalsIgnoreCase("/META-INF") && !requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0) && !requestPathMB.equalsIgnoreCase("/WEB-INF")) {
        Wrapper wrapper = request.getWrapper();
        if (wrapper != null && !wrapper.isUnavailable()) {
            try {
                response.sendAcknowledgement();
            } catch (IOException var6) {
                this.container.getLogger().error(sm.getString("standardContextValve.acknowledgeException"), var6);
                request.setAttribute("javax.servlet.error.exception", var6);
                response.sendError(500);
                return;
            }

            if (request.isAsyncSupported()) {
                request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
            }

            wrapper.getPipeline().getFirst().invoke(request, response);
        } else {
            response.sendError(404);
        }
    } else {
        response.sendError(404);
    }
}

然后是 StandardWrapperValve 类,首先会生成一个 Servlet 对象:

try {
    if (!unavailable) {
        servlet = wrapper.allocate();
    }
}

wrapper 的 allocate 函数中,会先检查自身的 instance 属性中有没有加载好的 servlet,没有则会调用 loadServlet 进行加载:

servlet = (Servlet)instanceManager.newInstance(this.servletClass);

这里的 servletClass 来自 web.xml 中配置的类。

获取到 servlet 后,会调用 createFilterChain 创建 filterChain:

ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

createFilterChain:

ApplicationFilterChain filterChain = null;
if (request instanceof Request) {
    Request req = (Request)request;
    if (Globals.IS_SECURITY_ENABLED) {
        filterChain = new ApplicationFilterChain();
    } else {
        filterChain = (ApplicationFilterChain)req.getFilterChain();
        if (filterChain == null) {
            filterChain = new ApplicationFilterChain();
            req.setFilterChain(filterChain);
        }
    }
} else {
    filterChain = new ApplicationFilterChain();
}

首先该 request 对象中有没有存放有 filterChain,如果没有(比如第一次访问)则生成一个 ApplicationFilterChain 对象。继续往下看:

filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
StandardContext context = (StandardContext)wrapper.getParent();
FilterMap[] filterMaps = context.findFilterMaps();

将 servlet 放入 filterChain,取出 context,再从 context 中取出 filterMaps,filterMaps 的赋值可以回到 configureContext:

var2 = webxml.getFilterMappings().iterator();

while(var2.hasNext()) {
    FilterMap filterMap = (FilterMap)var2.next();
    this.context.addFilterMap(filterMap);
}

简单来说也是从 web.xml 中来的。回到 createFilterChain:

String servletName = wrapper.getName();
FilterMap[] var10 = filterMaps;
int var11 = filterMaps.length;

int var12;
FilterMap filterMap;
ApplicationFilterConfig filterConfig;
for(var12 = 0; var12 < var11; ++var12) {
    filterMap = var10[var12];
    if (matchDispatcher(filterMap, dispatcher) && matchFiltersURL(filterMap, requestPath)) {
        filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMap.getFilterName());
        if (filterConfig != null) {
            filterChain.addFilter(filterConfig);
        }
    }
}

匹配上 DispatcherType 和 requestPath 就会加入 filterChain 中。

回到 StandardWrapperValve,创建完 filterChain 之后执行:

filterChain.doFilter(request.getRequest(), response.getResponse());

即:

if (this.pos < this.n) {
    ApplicationFilterConfig filterConfig = this.filters[this.pos++];

    try {
        Filter filter = filterConfig.getFilter();
        if (request.isAsyncSupported() && "false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {
            request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);
        }

        if (Globals.IS_SECURITY_ENABLED) {
            Principal principal = ((HttpServletRequest)request).getUserPrincipal();
            Object[] args = new Object[]{request, response, this};
            SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
        } else {
            filter.doFilter(request, response, this);
        }

    } catch (ServletException | RuntimeException | IOException var15) {
        throw var15;
    } catch (Throwable var16) {
        Throwable e = ExceptionUtils.unwrapInvocationTargetException(var16);
        ExceptionUtils.handleThrowable(e);
        throw new ServletException(sm.getString("filterChain.filter"), e);
    }
}

依次调用这些 filter 的 doFilter 函数,在处理完 filter 之后:

try {
    if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
        lastServicedRequest.set(request);
        lastServicedResponse.set(response);
    }

    if (request.isAsyncSupported() && !this.servletSupportsAsync) {
        request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);
    }

    if (request instanceof HttpServletRequest && response instanceof HttpServletResponse && Globals.IS_SECURITY_ENABLED) {
        Principal principal = ((HttpServletRequest)request).getUserPrincipal();
        Object[] args = new Object[]{request, response};
        SecurityUtil.doAsPrivilege("service", this.servlet, classTypeUsedInService, args, principal);
    } else {
        this.servlet.service(request, response);
    }
}

就开始调用 servlet 的 service 函数,往后就是项目的具体实现代码了,就不提了。


Orz


Web Java Tomcat

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!

Jenkins相关漏洞学习(一)
Java 7u21/8u20 反序列化漏洞