Fork me on GitHub
11 November 2019

环境:

  • Tomcat 9.0.17

org.apache.catalina.connector.Connector 是 “Tomcat” 的核心组件,负责协议的解析(由它持有的ProtocolHandler处理)以及相关处理等,并定义了运行中需要的相关配置项,包括支持协议,端口,以及解析相关参数等,实例由org.apache.catalina.Service持有和管理,下面我们来逐一看看它的这些属性。

protected boolean allowTrace = false;
protected long asyncTimeout = 30000;
protected boolean enableLookups = false;
protected boolean xpoweredBy = false;
protected String proxyName = null;
protected int proxyPort = 0;
protected int redirectPort = 443;
protected String scheme = "http";
protected boolean secure = false;
private int maxCookieCount = 200;
protected int maxParameterCount = 10000;
protected int maxPostSize = 2 * 1024 * 1024;
protected int maxSavePostSize = 4 * 1024;
protected String parseBodyMethods = "POST";
protected boolean useIPVHosts = false;
protected final String protocolHandlerClassName;
protected final ProtocolHandler protocolHandler;
private Charset uriCharset = StandardCharsets.UTF_8;
protected boolean useBodyEncodingForURI = false;

它的属性有以上这些(无关本文的属性已去除),其中的部分属性可参考该包下面的mbeans-descriptors.xml文件,该文件包括 Connector 的可配置属性和 ProtocolHandler 的属性,可通过 server.xml 中的 connector 标签进行配置,并由ConnectorMBean把配置项注入到Connector中,具体过程可参考org.apache.tomcat.util.modeler.Registryorg.apache.catalina.mbeans.MBeanUtils

allowTrace

是否支持 TRACE 请求

asyncTimeout

异步请求超时时间,默认 30s

enableLookups

是否寻找远程主机名

xpoweredBy

响应头是否添加 X-Powered-By 字段

proxyName

代理服务器名称,影响 request.serverName

proxyPort

代理服务器端口,影响 request.serverPort

redirectPort

从非 SSL 定向到 SSL 的端口

scheme

默认请求为 http

secure

是否使用 https

maxCookieCount

每个请求创建 cookie 的最大数量,默认为200,-1为不限制

maxParameterCount

GETPOST 请求中参数的最大数量,默认为10000。小于0为不限制

maxPostSize

POST 请求数据的最大长度,默认为2MB,会影响文件上传,参数解析,具体可参考org.apache.catalina.connector.Request,以parseParameters方法为例:

int maxPostSize = connector.getMaxPostSize();
// 比较长度
if ((maxPostSize >= 0) && (len > maxPostSize)) {
    Context context = getContext();
    if (context != null && context.getLogger().isDebugEnabled()) {
        context.getLogger().debug(
                sm.getString("coyoteRequest.postTooLarge"));
    }
    checkSwallowInput();
    // 解析失败,设置失败原因
    parameters.setParseFailedReason(FailReason.POST_TOO_LARGE);
    return;
}

maxSavePostSize

授权时允许存储到容器中数据的最大长度,该属性只会影响到FormAuthenticator

parseBodyMethods

当请求的Content-Typeapplication/x-www-form-urlencoded,并且请求方式在parseBodyMethods中定义的请求会进行参数解析,默认为POST,参考org.apache.catalina.connector.Request.parseParameters方法

useIPVHosts

是否开启虚拟主机

useBodyEncodingForURI

请求”URI”的编码是否和请求体一致,如果是,则使用请求体的编码来解析该QueryString,如果否,则使用”utf-8”

// org.apache.catalina.connector.Request
// 解析编码
private Charset getCharset() {
    Charset charset = null;
    try {
        // 从请求中读取
        charset = coyoteRequest.getCharset();
    } catch (UnsupportedEncodingException e) {
        // Ignore
    }
    if (charset != null) {
        return charset;
    }
    Context context = getContext();
    if (context != null) {
        // 从配置中读取
        String encoding = context.getRequestCharacterEncoding();
        if (encoding != null) {
            try {
                return B2CConverter.getCharset(encoding);
            } catch (UnsupportedEncodingException e) {
                // Ignore
            }
        }
    }

    // ISO_8859_1
    return org.apache.coyote.Constants.DEFAULT_BODY_CHARSET;
}

uriCharset

“URI”使用的编码,默认为”utf-8”,可调用setURIEncoding方法可进行配置

protocol

支持协议:

1. HTTP/1.1
  • org.apache.coyote.http11.Http11AprProtocol
  • org.apache.coyote.http11.Http11NioProtocol
  • org.apache.coyote.http11.Http11Nio2Protocol

如果需要”HTTP2”,配置protocol下的UpgradeProtocol即可,”SSL”也是同理,添加SSLHostConfig即可

3. AJP/1.3
  • org.apache.coyote.ajp.AjpAprProtocol
  • org.apache.coyote.ajp.AjpNioProtocol
  • org.apache.coyote.ajp.AjpNio2Protocol

解析过程:

// 解析 protocol
public Connector(String protocol) {
    boolean aprConnector = AprLifecycleListener.isAprAvailable() &&
            AprLifecycleListener.getUseAprConnector();
    if ("HTTP/1.1".equals(protocol) || protocol == null) {
        if (aprConnector) {
            protocolHandlerClassName = "org.apache.coyote.http11.Http11AprProtocol";
        } else {
            protocolHandlerClassName = "org.apache.coyote.http11.Http11NioProtocol";
        }
    } else if ("AJP/1.3".equals(protocol)) {
        if (aprConnector) {
            protocolHandlerClassName = "org.apache.coyote.ajp.AjpAprProtocol";
        } else {
            protocolHandlerClassName = "org.apache.coyote.ajp.AjpNioProtocol";
        }
    } else {
        protocolHandlerClassName = protocol;
    }
    // Instantiate protocol handler
    ProtocolHandler p = null;
    try {
        Class<?> clazz = Class.forName(protocolHandlerClassName);
        p = (ProtocolHandler) clazz.getConstructor().newInstance();
    } catch (Exception e) {
        log.error(sm.getString(
                "coyoteConnector.protocolHandlerInstantiationFailed"), e);
    } finally {
        this.protocolHandler = p;
    }
    // Default for Connector depends on this system property
    setThrowOnFailure(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"));
}

ProtocolHandler相当的重要,”HTTP”等协议的解析全靠它来完成,可以说是”Tomcat”的核心组件,所以它的代码设计也会很复杂,因此会在以后专门写一篇文章来解析。