tomcat-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Chris Pettitt" <cpett...@gmail.com>
Subject Comet processor blocks if chunk includes final CRLF
Date Wed, 12 Dec 2007 16:46:36 GMT
Hello,

I'm using Tomcat 6.0.14 and its CometProcessor to create a long
running connection between an HTTP client and my server. The client
posts requests to the server using chunked transfer encoding (each
chunk is a complete XML request processed by the servlet).

I'm using a servlet that is heavily derived from the
example[1] on Tomcat's website. I've found that this implementation
blocks the worker thread - which I would not expect - if the client
sends a complete chunk and then wait some time before sending the next
chunk.

I'm using the W3C specification[2] of a chunk:

    chunk-size CRLF
    chunk-data CRLF

Surprisingly, if I send a chunk that does not include the last CRLF,
then the CometProcessor works correctly and does not block.

My analysis of the blocking condition follows:

ChunkedInputFilter.doRead() reads everything in the chunk except the
last CRLF. It defers reading of the last CRLF until the next read()
call by setting needCRLFParse to true at line 157. Calling
ChunkedInputFilter.available() before receiving new data will
therefore return 2, because the CRLF at the end of the chunk have not
yet been read.

The implementation of
org.apache.catalina.connector.InputBuffer.available() always delegates
to coyoteRequest.getAvailable() (in my environment, state is not
BYTE_STATE or CHAR_STATE during this call).

    // org.apache.catalina.connector.InputBuffer, ~ line 260
    public int available() {
        int available = 0;
        if (state == BYTE_STATE) {
            available = bb.getLength();
        } else if (state == CHAR_STATE) {
            available = cb.getLength();
        }
        if (available == 0) {
            coyoteRequest.action(ActionCode.ACTION_AVAILABLE, null);
            available = (coyoteRequest.getAvailable() > 0) ? 1 : 0; //
 <--- delegates here
        }
        return available;
    }

The value that is returned by coyoteRequest.getAvailable() is set by
Http11NioProcessor (see processing of ActionCode.ACTION_AVAILABLE at
line 1218) to the value returned by its inputBuffer.available(). This
method delegates to the ChunkedInputFilter, which returns 2,
indicating two available bytes (CR, LF). Ultimately the
InputBuffer.available() method above returns 1, because
coyoteRequest.getAvailable() returned a non-zero value.

Because in.available() in the attached servlet is non-zero, the
servlet calls read() again. This time the call blocks, because when it
calls read ChunkedInputFilter.doRead() finishes reading the CRLF it
has in its buffer, it calls parseChunkHeader which makes a blocking
call to readBytes().

Is this misuse of CometProcessor? Is there best practice guidance
for managing long running connections with multiple requests with
Tomcat?

Thanks,
Chris

[1]: http://tomcat.apache.org/tomcat-6.0-doc/aio.html
[2]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1

---------------------------------------------------------------------
To start a new topic, e-mail: users@tomcat.apache.org
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Mime
View raw message