Bug 14282

Summary: mod_jk/ajp13 returns wrong response after bad chunk-encoding request
Product: Tomcat Connectors Reporter: Dan Chow <dchow>
Component: CommonAssignee: Tomcat Developers Mailing List <dev>
Status: RESOLVED WORKSFORME    
Severity: major    
Priority: P3    
Version: unspecified   
Target Milestone: ---   
Hardware: PC   
OS: Linux   

Description Dan Chow 2002-11-06 00:17:26 UTC
We currently are running Apache 1.3.26 which forwards requests to our tomcat
4.0.4 server using mod_jk/ajp13. After making the following request:

GET /index.jsp HTTP/1.1\x0d\x0aHost: host1.foo.c
om\x0d\x0aTransfer-Encoding: Chunked\x0d\x0a\x0d\x0aAAAAAAAA\x0d\x0a\x0d\x0a

which returns a 404, we begin to see an intermittent problem where valid request
results in the response of another request being returned instead of the correct
one. 

We have seen this problem in Tomcat 4.0.6 and 4.1.12.  I've looked in the apache
bug database and in the tomcat mailing list and seen similar issues, but most of
those were in tomcat 3 and we do not see the same behavior in tomcat 3.
Comment 1 Dan Chow 2002-11-06 18:40:55 UTC
Just to clarify the behavior that we are seeing is that the request/response 
pairs are being mixed up.
Comment 2 Henri Gomez 2002-11-07 13:33:05 UTC
There was some fixes recently in the ajp13 java code, 
so could you try with a TC 4 from CVS ?
Comment 3 Henri Gomez 2002-11-20 10:03:03 UTC
I didn't see such behaviour, Apache 1.3 return me error 400, Bad request.

I mark this to WORKSFORME until someone provide a reproductable test pattern....

Comment 4 Dan Chow 2002-11-21 18:23:59 UTC
I will try the latest tomcat and let you know if I still have this problem. Here
is how we are able to reproduce it:

I have a servlet:
import javax.servlet.http.HttpServlet;

import java.io.PrintWriter;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import org.apache.log4j.Category;
import java.util.Enumeration;

public class LoadServlet extends HttpServlet
{
    public void init(ServletConfig config) throws ServletException
    {
        Category logger = Category.getRoot();
        logger.info("LoadServlet::init(): Loading LoadServlet!");
    }

    public void service(HttpServletRequest request, HttpServletResponse response) 
    {
        Category logger = Category.getRoot();
        StringBuffer url = new StringBuffer();
        url.append("LoadServlet::service(): Request URL (" + ((HttpServletRequest) 
request).getRequestURI() + "?");
        Enumeration enum = request.getParameterNames();
            
        while (enum.hasMoreElements())
        {
            String key = (String) enum.nextElement();
            url.append(key + "=" + request.getParameter(key));
        }

        logger.info(url.toString() + ")");

        // Take the request and response to it.
        String id = request.getParameter("ID");
        logger.info("LoadServlet::service(): Got ID parameter(" + id + ")");
        StringBuffer responseString = new StringBuffer().append("<HTML>The ID you p
assed in is: " + id + "</HTML>\n");
        logger.info("LoadServlet::service(): Response (" + responseString + ")");
        response.addHeader("ID" , java.net.URLEncoder.encode(id));

        try
        {
            PrintWriter pw = response.getWriter();
            pw.write(responseString.toString());
        }
        catch(IOException e)
        {
            logger.error(e.getMessage(), e);
        }
    }
}

running on tomcat that just returns the parameter(ID) in both the header and the
body of the response.  I also have a perl script:

#!/usr/local/bin/perl -w
use IO::Socket;
unless (@ARGV > 1) { die "Usage: ./syncsvt.pl: <host> <port>\n";}
($host) = $ARGV[0];
($port) = $ARGV[1];
    $remote = IO::Socket::INET->new( Proto     => "tcp",
                                     PeerAddr  => $host,
                                     PeerPort  => "$port",
                                    );
    unless ($remote) { die "Cannot connect to $host\n" }
    $remote->autoflush(1);
    print $remote "GET /index.jsp HTTP/1.1\x0d\x0aHost:
host1.foo.com\x0d\x0aTransfer-Encoding:
Chunked\x0d\x0a\x0d\x0aAAAAAAAA\x0d\x0a\x0d\x0a";

    while ( <$remote> ) { print }
    close $remote;

that invokes my chunked encoding request. 

To reproduce the problem, I first run the perl script and hit the apache server
on port 80 and get back the 404 response saying index.jsp is not found.  Then
with two browsers open, I invoke the LoadServlet servlet with the parameter
ID=<value> through apache using HTTPS 4-5 times on each browser repeatedly.
After a while, one of my requests will return the 404 response for the
chunked-encoding request I made earlier from my perl script. After that, every
once in a while, I will get back the response of one of my previous requests. 

A tcpdump of my tomcat server on port 8009 shows that it is writing the correct
response back, but the mod_jk log in debug mode shows that it receieved the
response of a previous request.  

Modifying the mod_jk shared lib to close the socket in ajp_done():

        // close the socket
        jk_close_socket(p->sd, l);
        p->sd = -1;

seems to fix this problem because the socket is no longer reused, but I'm still
not sure what causes the problem and closing the socket each time takes away the
advantages of reusing the socket. 
Comment 5 Dan Chow 2002-11-21 23:57:49 UTC
The latest version of mod_jk in CVS seems to fix this behavior.  I no longer 
see the mixed request response pairs during my test. However, my chunked 
encoding request now hangs.  I have to either ctrl-c the request or wait for 
the socket timeout to close the connection.