Mark,

On 3/9/15 1:23 PM, Mark Thomas wrote:
> On 9 March 2015 16:35:57 GMT+00:00, Christopher Schultz 
> <ch...@christopherschultz.net> wrote:
>> Mark,
>>
>> On 3/9/15 12:32 PM, ma...@apache.org wrote:
>>> Author: markt
>>> Date: Mon Mar  9 16:32:43 2015
>>> New Revision: 1665297
>>>
>>> URL: http://svn.apache.org/r1665297
>>> Log:
>>> Follow up to r1665062
>>> Callers assume that output() will fully write the data that is passed
>> to it. Ensure that this is the case when the AJP message size is larger
>> than the output buffer.
>>>
>>> Modified:
>>>    
>> tomcat/tc7.0.x/trunk/java/org/apache/coyote/ajp/AjpNioProcessor.java
>>>
>>> Modified:
>> tomcat/tc7.0.x/trunk/java/org/apache/coyote/ajp/AjpNioProcessor.java
>>> URL:
>> http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/ajp/AjpNioProcessor.java?rev=1665297&r1=1665296&r2=1665297&view=diff
>>>
>> ==============================================================================
>>> ---
>> tomcat/tc7.0.x/trunk/java/org/apache/coyote/ajp/AjpNioProcessor.java
>> (original)
>>> +++
>> tomcat/tc7.0.x/trunk/java/org/apache/coyote/ajp/AjpNioProcessor.java
>> Mon Mar  9 16:32:43 2015
>>> @@ -302,24 +302,29 @@ public class AjpNioProcessor extends Abs
>>>          ByteBuffer writeBuffer =
>>>                 
>> socketWrapper.getSocket().getBufHandler().getWriteBuffer();
>>>  
>>> -        int toWrite = Math.min(length, writeBuffer.remaining());
>>> -        writeBuffer.put(src, offset, toWrite);
>>> -        
>>> -        writeBuffer.flip();
>>> -
>>> -        long writeTimeout = att.getWriteTimeout();
>>> -        Selector selector = null;
>>> -        try {
>>> -            selector = pool.get();
>>> -        } catch ( IOException x ) {
>>> -            //ignore
>>> -        }
>>> -        try {
>>> -            pool.write(writeBuffer, socketWrapper.getSocket(),
>> selector,
>>> -                    writeTimeout, true);
>>> -        }finally { 
>>> -            writeBuffer.clear();
>>> -            if ( selector != null ) pool.put(selector);
>>> +        int left = length;
>>> +        int written = 0;
>>> +        while (left > 0) {
>>> +            int toWrite = Math.min(left, writeBuffer.remaining());
>>> +            writeBuffer.put(src, offset, toWrite);
>>> +            
>>> +            writeBuffer.flip();
>>> +    
>>> +            long writeTimeout = att.getWriteTimeout();
>>> +            Selector selector = null;
>>> +            try {
>>> +                selector = pool.get();
>>> +            } catch ( IOException x ) {
>>> +                //ignore
>>> +            }
>>> +            try {
>>> +                written = pool.write(writeBuffer,
>> socketWrapper.getSocket(),
>>> +                        selector, writeTimeout, true);
>>> +            } finally { 
>>> +                writeBuffer.clear();
>>> +                if ( selector != null ) pool.put(selector);
>>> +            }
>>> +            left -= written;
>>>          }
>>>      }
>>
>> Okay, yes, this was more like what I was expecting to see. I'll give it
>> another try.
> 
> Glad to see it is working now. The next jobs are a) documenting the
> expected behavior re full vs partial write and b) take a look at trying
> to reduce the number of array copies (or equivalent) that the I/O codes
> does.

I'm not sure it's really worth it to fix, at least not for Tomcat 7/8.
I'd focus on Tomcat 9 where you guys have been doing good work (except
for removing BIO! Boo! Hiss! ... yeah, I know why and I agree but I'm
still not terribly happy about it).

Definitely reducing otherwise useless copying is good, but don't kill
yourself over it in stable versions unless there is a specific
complaint. There are too many opportunities for breakage. I'm actually
shocked that nobody found this problem before. I guess not that many
people use mod_jk, or maybe most of them use it with the default packet
size.

I only found this because of two events that occurred very far apart in
time:

1a. I was doing testing on client certificate handling ... years ago.
Client certificates tend to grow large and since the whole request
headers must fit into a single AJP packet, I had to increase
max_packet_size in mod_jk. I did this for my "template" worker, to
affect all workers that derive from it.
1b. I changed my "client certificate test" application to use a matching
max packet size to 1a.

2. Recent use of GWT had started sending rather large payloads back and
forth to our servers. (Yes, I'm also curious why GWT, supposedly to
improve performance, is now sending more data to and from the server
than just refreshing the page... but where was I?) This notified us that
the packetSize in Tomcat was incorrect.

Adjusting the packetSize in Tomcat immediately caused all hell to break
loose.

Well, at any rate, I'm glad it's fixed, now.

Thanks,
-chris

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to