You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by bu...@apache.org on 2002/10/09 18:58:31 UTC

DO NOT REPLY [Bug 13463] New: - Request/Response race condition when doing multiple requests on the same connection.

DO NOT REPLY TO THIS EMAIL, BUT PLEASE POST YOUR BUG 
RELATED COMMENTS THROUGH THE WEB INTERFACE AVAILABLE AT
<http://nagoya.apache.org/bugzilla/show_bug.cgi?id=13463>.
ANY REPLY MADE TO THIS MESSAGE WILL NOT BE COLLECTED AND 
INSERTED IN THE BUG DATABASE.

http://nagoya.apache.org/bugzilla/show_bug.cgi?id=13463

Request/Response race condition when doing multiple requests on the same connection.

           Summary: Request/Response race condition when doing multiple
                    requests on the same connection.
           Product: Commons
           Version: Nightly Builds
          Platform: PC
        OS/Version: Windows XP
            Status: NEW
          Severity: Normal
          Priority: Other
         Component: HttpClient
        AssignedTo: commons-dev@jakarta.apache.org
        ReportedBy: mike.vannoord@brainna.com


If one tries to do multiple request over the same socket connection a race 
condition occurs in the input/output streams.
eg. 
-- Some request -->
<- HTTP/1.1 200 OK
<- Some: Headers
<- 
<- The body.

-- Next request -->
<- HTTP/1.1 200 OK
<- More: Headers
<- 
<- Some data.

If the second request is sent, but the second response isn't yet received 
before the client starts to try to read it, it'll get 
a "org.apache.commons.httpclient.HttpRecoverableException: Error in parsing the 
status  line from the response: unable to find line starting with "HTTP/"" 
exception (it will think "The body." is part of the second response).

The following code will reproduce the problem:

import java.io.*;
import java.net.*;
import java.util.*;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.*;

public class HttpClientRaceBug {
    public static void main(String[] args) {
        try {
            SimpleHttpServer.listen(8987);
            HttpClient client = new HttpClient();
            client.startSession("localhost", 8987);
            client.getState().setCredentials("Test Realm",  
                new UsernamePasswordCredentials("foo", "bar"));
            
            for (int i = 0; i < 100; i++) {
                GetMethod meth = new GetMethod();
                client.executeMethod(meth);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    private static final class SimpleHttpServer implements Runnable {
        private Socket socket;
        public SimpleHttpServer(Socket socket) {
            this.socket = socket;
        }
        public static void listen(final int port) {
            Thread server = new Thread() {
                public void run() {
                    try {
                        ServerSocket ss = new ServerSocket(port);
                        while (true) {
                            new Thread(new 
                                SimpleHttpServer(ss.accept())).start();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            
            server.setDaemon(true);
            server.start();
        }
        public void run() {
            try {
                BufferedReader in = new BufferedReader(new 
                    InputStreamReader(this.socket.getInputStream()));
                
                int len = 0;
                boolean auth = false;
                String line;
                while ((line = in.readLine()) != null) {
                    System.out.println("> " + line);
                    
                    if (line.trim().equals("")) {
                        in.read(new char[len]);
                        doOutput(auth);
                        auth = false;
                        len = 0;
                        
                    } else if (line.indexOf(':') > -1) {
                        StringTokenizer tok = new StringTokenizer(line, ":");
                        String key = tok.nextToken().toLowerCase();
                        if (key.equals("content-length")) {
                            len = Integer.parseInt(tok.nextToken().trim());
                        } else if (key.equals("authorization")) {
                            auth = true;
                        }
                    }
                }
            } catch (Exception e) {}
        }
        private static int count = 0;
        public void doOutput(boolean authorized) throws IOException {
            Writer out = new OutputStreamWriter(this.socket.getOutputStream());
            count++;
            
            String id = (count < 100) ? 
                ((count < 10) ? "00" + count : "0" + count) : "" + count;
            if (authorized) {
                write(out, "HTTP/1.1 200 OK\r\n");
            } else {
                write(out, "HTTP/1.1 401 Unauthorized\r\n");
            }
            write(out, "WWW-Authenticate: Basic realm=\"Test Realm\"\r\n");
            write(out, "Response-Id: " + id + "\r\n");
            write(out, "Content-Type: text/html; charset=iso-8859-1\r\n");
            write(out, "Content-Length: 17\r\n\r\n");
            write(out, "My Response (" + id + ")");
            out.close();
        }
        private void write(Writer out, String text) throws IOException {
            System.out.print("< " + text);
            out.write(text);
        }
    }
}

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>