Tomcat Connector

Tomcat do not you have a conscience.

connector

  1. Create a Connector, construct a Connector object.
  2. Call Connector initIntenal method
  3. ProtocolHanlder init method contain thread pool and a thread deal request
  4. Call Connector startIntenal
  5. Call ProtocolHandler start
  6. Call MapperListener start, listen changes

Detail

Protocol

Tomcat have two protocol handlers HTTP/1.1 AJP/1.3
config in server.xml

1
2
3
<Connector port="8080" address="0.0.0.0" protocol="HTTP/1.1"
connectionTimeout="20000" redirectPort="8443"/>
<Connector port="8010" protocol="AJP/1.3" redirectPort="8444" />

setProtocol(String protocol) set Http11AprProtocol in default.

Init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
// Initialize adapter
adapter = new CoyoteAdapter(this);
protocolHandler.setAdapter(adapter);
......
try {
protocolHandler.init();
} catch (Exception e) {
......
}
mapperListener.init();
}

Call LifecycleMBeanBase‘s initInternal() regist self.
Create Adapter and set into handler
protocolHandler‘s concrete implements in abstract class AbstractProtocol
Implemented in AbstractAjpProtocol AbstractHttp11Protocol

Focus on AbstractProtocol:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
* NOTE: There is no maintenance of state or checking for valid transitions
* within this class. It is expected that the connector will maintain state
* and prevent invalid state transitions.
*/

@Override
public void init() throws Exception {
//set names
......
try {
endpoint.init();
} catch (Exception ex) {
......
}
}

endpoint.init();

  1. Setting recevie thread number and biggest connection number

    1
    2
    3
    4
    // Initialize thread count defaults for acceptor
    if (acceptorThreadCount == 0) {
    acceptorThreadCount = 1;
    }
  2. Create thread pool, start listener thread for client request

    1
    2
    3
    4
    5
    // Initialize maxConnections
    if (getMaxConnections() == 0) {
    // User hasn't set a value - use the default
    setMaxConnections(getMaxThreadsInternal());
    }
  3. Start a server socket

    1
    2
    3
    4
    if (getAddress() == null) {
    serverSocket = serverSocketFactory.createSocket(getPort(),
    getBacklog());
    }

Start Internal

  1. Connector -> startInternal()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /**
    * Begin processing requests via this Connector.
    */
    @Override
    protected void startInternal() throws LifecycleException {
    // Validate settings before starting
    ......
    setState(LifecycleState.STARTING);
    try {
    protocolHandler.start();
    } catch (Exception e) {
    ......
    }
    mapperListener.start();
    }

Call abstract protocol start

  1. AbstractProtocol -> start()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Override
    public void start() throws Exception {
    ......
    try {
    endpoint.start();
    } catch (Exception ex) {
    ......
    }
    }

Call endpoint start

  1. AbstractEndpoint -> start()
    1
    2
    3
    4
    5
    6
    7
    public final void start() throws Exception {
    if (bindState == BindState.UNBOUND) {
    bind();
    bindState = BindState.BOUND_ON_START;
    }
    startInternal();
    }

Call itself’s abstract method startInternal().

  1. JIoEndpoint -> startInternal()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    @Override
    public void startInternal() throws Exception {
    if (!running) {
    running = true;
    paused = false;

    // Create worker collection
    if (getExecutor() == null) {
    createExecutor();
    }

    initializeConnectionLatch();
    startAcceptorThreads();

    // Start async timeout thread
    Thread timeoutThread = new Thread(new AsyncTimeout(),
    getName() + "-AsyncTimeout");
    timeoutThread.setPriority(threadPriority);
    timeoutThread.setDaemon(true);
    timeoutThread.start();
    }
    }

Initialize connection
Start acceptor thread
Start an asynchronous thread deal request

  1. JIoEndpoint.AsyncTimeout -> run()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    /**
    * Async timeout thread
    */
    protected class AsyncTimeout implements Runnable {
    /**
    * The background thread that checks async requests and fires the
    * timeout if there has been no activity.
    */
    @Override
    public void run() {

    // Loop until we receive a shutdown command
    while (running) {
    // delay / get socket object/ check timeout
    ......
    while (sockets.hasNext()) {
    SocketWrapper<Socket> socket = sockets.next();
    ......
    processSocketAsync(socket,SocketStatus.TIMEOUT);
    }

    // Loop if endpoint is paused
    ......
    }
    }
    }

    /**
    * Process an existing async connection. If processing is required, passes
    * the wrapped socket to an executor for processing.
    *
    * @param socket The socket associated with the client.
    * @param status Only OPEN and TIMEOUT are used. The others are used for
    * Comet requests that are not supported by the BIO (JIO)
    * Connector.
    */
    @Override
    public void processSocketAsync(SocketWrapper<Socket> socket,
    SocketStatus status) {
    try {
    synchronized (socket) {
    if (waitingRequests.remove(socket)) {
    SocketProcessor proc = new SocketProcessor(socket,status);
    ClassLoader loader = Thread.currentThread().getContextClassLoader();
    try {
    //threads should not be created by the webapp classloader
    ......
    // During shutdown, executor may be null - avoid NPE
    if (!running) return;
    getExecutor().execute(proc);
    //TODO gotta catch RejectedExecutionException and properly handle it
    } finally {
    // doPrivileged
    ......
    }
    }
    }
    } catch (Throwable t) {
    ExceptionUtils.handleThrowable(t);
    // This means we got an OOM or similar creating a thread, or that
    // the pool and its queue are full
    log.error(sm.getString("endpoint.process.fail"), t);
    }
    }

org.apache.tomcat.util.net.JIoEndpoint.SocketProcessor it’s job is put
concrete request dispatch to org.apache.tomcat.util.net.JIoEndpoint.Handler.
Then based on handler‘s different return SocketState decide
whether close connection or do next loop.

  1. JIoEndpoint.SocketProcessor -> run()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    /**
    * This class is the equivalent of the Worker, but will simply use in an
    * external Executor thread pool.
    */
    ......
    @Override
    public void run() {
    boolean launch = false;
    synchronized (socket) {
    try {
    SocketState state = SocketState.OPEN;

    try {
    // SSL handshake
    serverSocketFactory.handshake(socket.getSocket());
    } catch (Throwable t) {
    ......
    }

    if ((state != SocketState.CLOSED)) {
    if (status == null) {
    state = handler.process(socket, SocketStatus.OPEN_READ);
    } else {
    state = handler.process(socket,status);
    }
    }
    if (state == SocketState.CLOSED) {
    // Close socket
    if (log.isTraceEnabled()) {
    log.trace("Closing socket:"+socket);
    }
    countDownConnection();
    try {
    socket.getSocket().close();
    } catch (IOException e) {
    // Ignore
    }
    } else if (state == SocketState.OPEN ||
    state == SocketState.UPGRADING ||
    state == SocketState.UPGRADING_TOMCAT ||
    state == SocketState.UPGRADED){
    socket.setKeptAlive(true);
    socket.access();
    launch = true;
    } else if (state == SocketState.LONG) {
    socket.access();
    waitingRequests.add(socket);
    }
    } finally {
    ......
    }
    socket = null;
    // Finish up this request
    }

state = handler.process(socket, SocketStatus.OPEN_READ);
call process

  1. AbstractProtocol.AbstractConnectionHandler -> process()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    public SocketState process(SocketWrapper<S> wrapper,
    SocketStatus status) {
    if (wrapper == null) {
    // Nothing to do. Socket has been closed.
    return SocketState.CLOSED;
    }

    S socket = wrapper.getSocket();
    if (socket == null) {
    // Nothing to do. Socket has been closed.
    return SocketState.CLOSED;
    }

    Processor<S> processor = connections.get(socket);
    if (status == SocketStatus.DISCONNECT && processor == null) {
    // Nothing to do. Endpoint requested a close and there is no
    // longer a processor associated with this socket.
    return SocketState.CLOSED;
    }

    wrapper.setAsync(false);
    ContainerThreadMarker.markAsContainerThread();

    try {
    if (processor == null) {
    processor = recycledProcessors.poll();
    }
    if (processor == null) {
    processor = createProcessor();
    }

    initSsl(wrapper, processor);

    SocketState state = SocketState.CLOSED;
    do {
    if (status == SocketStatus.DISCONNECT &&
    !processor.isComet()) {
    // Do nothing here, just wait for it to get recycled
    // Don't do this for Comet we need to generate an end
    // event (see BZ 54022)
    } else if (processor.isAsync() || state == SocketState.ASYNC_END) {
    state = processor.asyncDispatch(status);
    if (state == SocketState.OPEN) {
    // release() won't get called so in case this request
    // takes a long time to process, remove the socket from
    // the waiting requests now else the async timeout will
    // fire
    getProtocol().endpoint.removeWaitingRequest(wrapper);
    // There may be pipe-lined data to read. If the data
    // isn't processed now, execution will exit this
    // loop and call release() which will recycle the
    // processor (and input buffer) deleting any
    // pipe-lined data. To avoid this, process it now.
    state = processor.process(wrapper);
    }
    } else if (processor.isComet()) {
    state = processor.event(status);
    } else if (processor.getUpgradeInbound() != null) {
    state = processor.upgradeDispatch();
    } else if (processor.isUpgrade()) {
    state = processor.upgradeDispatch(status);
    } else {
    state = processor.process(wrapper);
    }

    if (state != SocketState.CLOSED && processor.isAsync()) {
    state = processor.asyncPostProcess();
    }

    if (state == SocketState.UPGRADING) {
    // Get the HTTP upgrade handler
    HttpUpgradeHandler httpUpgradeHandler =
    processor.getHttpUpgradeHandler();
    // Release the Http11 processor to be re-used
    release(wrapper, processor, false, false);
    // Create the upgrade processor
    processor = createUpgradeProcessor(
    wrapper, httpUpgradeHandler);
    // Mark the connection as upgraded
    wrapper.setUpgraded(true);
    // Associate with the processor with the connection
    connections.put(socket, processor);
    // Initialise the upgrade handler (which may trigger
    // some IO using the new protocol which is why the lines
    // above are necessary)
    // This cast should be safe. If it fails the error
    // handling for the surrounding try/catch will deal with
    // it.
    httpUpgradeHandler.init((WebConnection) processor);
    } else if (state == SocketState.UPGRADING_TOMCAT) {
    // Get the UpgradeInbound handler
    org.apache.coyote.http11.upgrade.UpgradeInbound inbound =
    processor.getUpgradeInbound();
    // Release the Http11 processor to be re-used
    release(wrapper, processor, false, false);
    // Create the light-weight upgrade processor
    processor = createUpgradeProcessor(wrapper, inbound);
    inbound.onUpgradeComplete();
    }
    if (getLog().isDebugEnabled()) {
    getLog().debug("Socket: [" + wrapper +
    "], Status in: [" + status +
    "], State out: [" + state + "]");
    }
    } while (state == SocketState.ASYNC_END ||
    state == SocketState.UPGRADING ||
    state == SocketState.UPGRADING_TOMCAT);

    if (state == SocketState.LONG) {
    // In the middle of processing a request/response. Keep the
    // socket associated with the processor. Exact requirements
    // depend on type of long poll
    connections.put(socket, processor);
    longPoll(wrapper, processor);
    } else if (state == SocketState.OPEN) {
    // In keep-alive but between requests. OK to recycle
    // processor. Continue to poll for the next request.
    connections.remove(socket);
    release(wrapper, processor, false, true);
    } else if (state == SocketState.SENDFILE) {
    // Sendfile in progress. If it fails, the socket will be
    // closed. If it works, the socket either be added to the
    // poller (or equivalent) to await more data or processed
    // if there are any pipe-lined requests remaining.
    connections.put(socket, processor);
    } else if (state == SocketState.UPGRADED) {
    // Need to keep the connection associated with the processor
    connections.put(socket, processor);
    // Don't add sockets back to the poller if this was a
    // non-blocking write otherwise the poller may trigger
    // multiple read events which may lead to thread starvation
    // in the connector. The write() method will add this socket
    // to the poller if necessary.
    if (status != SocketStatus.OPEN_WRITE) {
    longPoll(wrapper, processor);
    }
    } else {
    // Connection closed. OK to recycle the processor. Upgrade
    // processors are not recycled.
    connections.remove(socket);
    if (processor.isUpgrade()) {
    processor.getHttpUpgradeHandler().destroy();
    } else if (processor instanceof org.apache.coyote.http11.upgrade.UpgradeProcessor) {
    // NO-OP
    } else {
    release(wrapper, processor, true, false);
    }
    }
    return state;
    } catch(java.net.SocketException e) {
    // SocketExceptions are normal
    getLog().debug(sm.getString(
    "abstractConnectionHandler.socketexception.debug"), e);
    } catch (java.io.IOException e) {
    // IOExceptions are normal
    getLog().debug(sm.getString(
    "abstractConnectionHandler.ioexception.debug"), e);
    }
    // Future developers: if you discover any other
    // rare-but-nonfatal exceptions, catch them here, and log as
    // above.
    catch (Throwable e) {
    ExceptionUtils.handleThrowable(e);
    // any other exception or error is odd. Here we log it
    // with "ERROR" level, so it will show up even on
    // less-than-verbose logs.
    getLog().error(
    sm.getString("abstractConnectionHandler.error"), e);
    }
    // Make sure socket/processor is removed from the list of current
    // connections
    connections.remove(socket);
    // Don't try to add upgrade processors back into the pool
    if (!(processor instanceof org.apache.coyote.http11.upgrade.UpgradeProcessor)
    && !processor.isUpgrade()) {
    release(wrapper, processor, true, false);
    }
    return SocketState.CLOSED;
    }

Anaylsis request’s head / row/ body

  1. AbstractHttp11Processor -> process()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    /**
    * Process pipelined HTTP requests using the specified input and output
    * streams.
    *
    * @param socketWrapper Socket from which the HTTP requests will be read
    * and the HTTP responses will be written.
    *
    * @throws IOException error during an I/O operation
    */
    @Override
    public SocketState process(SocketWrapper<S> socketWrapper)
    throws IOException {
    RequestInfo rp = request.getRequestProcessor();
    rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);

    // Setting up the I/O
    setSocketWrapper(socketWrapper);
    getInputBuffer().init(socketWrapper, endpoint);
    getOutputBuffer().init(socketWrapper, endpoint);

    // Flags
    keepAlive = true;
    comet = false;
    openSocket = false;
    sendfileInProgress = false;
    readComplete = true;
    if (endpoint.getUsePolling()) {
    keptAlive = false;
    } else {
    keptAlive = socketWrapper.isKeptAlive();
    }

    if (disableKeepAlive()) {
    socketWrapper.setKeepAliveLeft(0);
    }

    while (!getErrorState().isError() && keepAlive && !comet && !isAsync() &&
    upgradeInbound == null &&
    httpUpgradeHandler == null && !endpoint.isPaused()) {

    // Parsing the request header
    try {
    setRequestLineReadTimeout();

    if (!getInputBuffer().parseRequestLine(keptAlive)) {
    if (handleIncompleteRequestLineRead()) {
    break;
    }
    }

    if (endpoint.isPaused()) {
    // 503 - Service unavailable
    response.setStatus(503);
    setErrorState(ErrorState.CLOSE_CLEAN, null);
    } else {
    keptAlive = true;
    // Set this every time in case limit has been changed via JMX
    request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
    request.getCookies().setLimit(getMaxCookieCount());
    // Currently only NIO will ever return false here
    if (!getInputBuffer().parseHeaders()) {
    // We've read part of the request, don't recycle it
    // instead associate it with the socket
    openSocket = true;
    readComplete = false;
    break;
    }
    if (!disableUploadTimeout) {
    setSocketTimeout(connectionUploadTimeout);
    }
    }
    } catch (IOException e) {
    if (getLog().isDebugEnabled()) {
    getLog().debug(
    sm.getString("http11processor.header.parse"), e);
    }
    setErrorState(ErrorState.CLOSE_NOW, e);
    break;
    } catch (Throwable t) {
    ExceptionUtils.handleThrowable(t);
    UserDataHelper.Mode logMode = userDataHelper.getNextMode();
    if (logMode != null) {
    String message = sm.getString(
    "http11processor.header.parse");
    switch (logMode) {
    case INFO_THEN_DEBUG:
    message += sm.getString(
    "http11processor.fallToDebug");
    //$FALL-THROUGH$
    case INFO:
    getLog().info(message, t);
    break;
    case DEBUG:
    getLog().debug(message, t);
    }
    }
    // 400 - Bad Request
    response.setStatus(400);
    setErrorState(ErrorState.CLOSE_CLEAN, t);
    getAdapter().log(request, response, 0);
    }

    if (!getErrorState().isError()) {
    // Setting up filters, and parse some request headers
    rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
    try {
    prepareRequest();
    } catch (Throwable t) {
    ExceptionUtils.handleThrowable(t);
    if (getLog().isDebugEnabled()) {
    getLog().debug(sm.getString(
    "http11processor.request.prepare"), t);
    }
    // 500 - Internal Server Error
    response.setStatus(500);
    setErrorState(ErrorState.CLOSE_CLEAN, t);
    getAdapter().log(request, response, 0);
    }
    }

    if (maxKeepAliveRequests == 1) {
    keepAlive = false;
    } else if (maxKeepAliveRequests > 0 &&
    socketWrapper.decrementKeepAlive() <= 0) {
    keepAlive = false;
    }

    // Process the request in the adapter
    if (!getErrorState().isError()) {
    try {
    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
    adapter.service(request, response);
    // Handle when the response was committed before a serious
    // error occurred. Throwing a ServletException should both
    // set the status to 500 and set the errorException.
    // If we fail here, then the response is likely already
    // committed, so we can't try and set headers.
    if(keepAlive && !getErrorState().isError() && (
    response.getErrorException() != null ||
    (!isAsync() &&
    statusDropsConnection(response.getStatus())))) {
    setErrorState(ErrorState.CLOSE_CLEAN, null);
    }
    setCometTimeouts(socketWrapper);
    } catch (InterruptedIOException e) {
    setErrorState(ErrorState.CLOSE_NOW, e);
    } catch (HeadersTooLargeException e) {
    getLog().error(sm.getString("http11processor.request.process"), e);
    // The response should not have been committed but check it
    // anyway to be safe
    if (response.isCommitted()) {
    setErrorState(ErrorState.CLOSE_NOW, e);
    } else {
    response.reset();
    response.setStatus(500);
    setErrorState(ErrorState.CLOSE_CLEAN, e);
    response.setHeader("Connection", "close"); // TODO: Remove
    }
    } catch (Throwable t) {
    ExceptionUtils.handleThrowable(t);
    getLog().error(sm.getString("http11processor.request.process"), t);
    // 500 - Internal Server Error
    response.setStatus(500);
    setErrorState(ErrorState.CLOSE_CLEAN, t);
    getAdapter().log(request, response, 0);
    }
    }

    // Finish the handling of the request
    rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);

    if (!isAsync() && !comet) {
    if (getErrorState().isError()) {
    // If we know we are closing the connection, don't drain
    // input. This way uploading a 100GB file doesn't tie up the
    // thread if the servlet has rejected it.
    getInputBuffer().setSwallowInput(false);
    } else {
    // Need to check this again here in case the response was
    // committed before the error that requires the connection
    // to be closed occurred.
    checkExpectationAndResponseStatus();
    }
    endRequest();
    }

    rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);

    // If there was an error, make sure the request is counted as
    // and error, and update the statistics counter
    if (getErrorState().isError()) {
    response.setStatus(500);
    }
    request.updateCounters();

    if (!isAsync() && !comet || getErrorState().isError()) {
    if (getErrorState().isIoAllowed()) {
    getInputBuffer().nextRequest();
    getOutputBuffer().nextRequest();
    }
    }

    if (!disableUploadTimeout) {
    if(endpoint.getSoTimeout() > 0) {
    setSocketTimeout(endpoint.getSoTimeout());
    } else {
    setSocketTimeout(0);
    }
    }

    rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);

    if (breakKeepAliveLoop(socketWrapper)) {
    break;
    }
    }

    rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);

    if (getErrorState().isError() || endpoint.isPaused()) {
    return SocketState.CLOSED;
    } else if (isAsync() || comet) {
    return SocketState.LONG;
    } else if (isUpgrade()) {
    return SocketState.UPGRADING;
    } else if (getUpgradeInbound() != null) {
    return SocketState.UPGRADING_TOMCAT;
    } else {
    if (sendfileInProgress) {
    return SocketState.SENDFILE;
    } else {
    if (openSocket) {
    if (readComplete) {
    return SocketState.OPEN;
    } else {
    return SocketState.LONG;
    }
    } else {
    return SocketState.CLOSED;
    }
    }
    }
    }

Read http request from socket, anaylsis request head,
construct Request Response Object, then call Adapter.service().
[raw 1115 adapter.service(request, response);]

adapter.service() will finish request row and body anaylsis, put
info package into Request Response Object.

Adapter org.apache.catalina.connector.CoyoteAdapter is the bridge between
Connector and Container.

When this step finished request have passed to Container from Connector

PS. First deal request with org.apache.coyote.Request is because it’s a
light-weight object in Tomcat.
It’s memory space can be easy recycle use by JVM.
Not put the job to Connector.Request is design for reduce burden[performence].

Connector.Request function for servlet is far more conplex than org.apache.coyote.Request

  1. CoyoteAdapter -> service()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    /**
    * Service method.
    */
    @Override
    public void service(org.apache.coyote.Request req,
    org.apache.coyote.Response res)
    throws Exception {

    Request request = (Request) req.getNote(ADAPTER_NOTES);
    Response response = (Response) res.getNote(ADAPTER_NOTES);

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

    if (connector.getXpoweredBy()) {
    response.addHeader("X-Powered-By", POWERED_BY);
    }

    boolean comet = false;
    boolean async = false;
    boolean postParseSuccess = false;

    try {
    // Parse and set Catalina and configuration specific
    // request parameters
    req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
    postParseSuccess = postParseRequest(req, request, res, response);
    if (postParseSuccess) {
    //check valves if we support async
    request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
    // Calling the container
    connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
    ......
    }

    }
    ......
    } catch (IOException e) {
    // Ignore
    } finally {
    ......
    }
    }

connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
pass request to container

Mapper Listener

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Override
public final synchronized void start() throws LifecycleException {

if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
......
}

if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}

try {
setStateInternal(LifecycleState.STARTING_PREP, null, false);
startInternal();
if (state.equals(LifecycleState.FAILED)) {
// This is a 'controlled' failure. The component put itself into the
// FAILED state so call stop() to complete the clean-up.
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
......
}
}
  1. Regist Connector itself
  2. Add Listener for module
  3. Add Containers’ mapping relation

Connctor start finished, damn!!!