hc-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Carl D'Halluin (JIRA)" <j...@apache.org>
Subject [jira] [Commented] (HTTPCLIENT-1376) HttpClient incorrectly reuses HTTP/1.1 connection - the response body of the first request is considered as the response for the second request
Date Wed, 19 Jun 2013 16:25:20 GMT

    [ https://issues.apache.org/jira/browse/HTTPCLIENT-1376?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13688121#comment-13688121
] 

Carl D'Halluin commented on HTTPCLIENT-1376:
--------------------------------------------

// Java client source code:
package com.amplidata.csg.backend;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

public class HTTP11Test {

    private final static String HOST = "127.0.0.1";
    private final static int PORT = 8777;

    private static String doRequest(final HttpClient client, boolean expectAuthentication,
boolean processBody)
        throws ClientProtocolException, IOException {

        final String url = "http://" + HOST + ":" + PORT + "/"
            + (expectAuthentication ? "authenticated.html" : "non-authenticated.html");
        System.out.println("\nREQUEST: GET " + url);

        final HttpResponse resp = client.execute(new HttpGet(url));

        final String statusLine = resp.getStatusLine().toString();
        System.out.println("RESPONSE STATUS: " + statusLine);
        if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
            System.out.println("--> BUG! Response status " + resp.getStatusLine().getStatusCode());

        if (processBody) {
            final BufferedReader br = new BufferedReader(new InputStreamReader(resp.getEntity().getContent()));
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println("RESPONSE BODY:   " + line);
            }
        } else {
            EntityUtils.consumeQuietly(resp.getEntity());
            System.out.println("RESPONSE BODY - SKIPPED");
        }
        return statusLine;
    }

    private static void printLine() {
        System.out.println("\n=======================");
    }

    public static void main(String[] args) throws ClientProtocolException, IOException {
        final DefaultHttpClient client = new DefaultHttpClient();
        client.getCredentialsProvider().setCredentials(new AuthScope(HOST, PORT),
            new UsernamePasswordCredentials("user", "pass"));

        boolean authenticated;
        boolean processBodyExplicitly;

        // No authentication; no explicit reading of response body.
        // The HTTP/1.1 connection is not reused, which is correct
        authenticated = false;
        processBodyExplicitly = false;
        doRequest(client, authenticated, processBodyExplicitly);
        doRequest(client, authenticated, processBodyExplicitly);
        printLine();

        // No authentication; with explicit reading & logging of response body.
        // The HTTP/1.1 connection is not reused, which is correct
        authenticated = false;
        processBodyExplicitly = true;
        doRequest(client, authenticated, processBodyExplicitly);
        doRequest(client, authenticated, processBodyExplicitly);
        printLine();

        // Authentication; with explicit reading & logging of response body.
        // The HTTP/1.1 connection is not reused, which is correct
        authenticated = true;
        processBodyExplicitly = true;
        doRequest(client, authenticated, processBodyExplicitly);
        doRequest(client, authenticated, processBodyExplicitly);
        printLine();

        // Authentication; no explicit reading of response body.
        // The HTTP/1.1 connection IS REUSED, which is NOT correct
        // The body of the first response is used as a response for the second request
        authenticated = true;
        processBodyExplicitly = false;
        doRequest(client, authenticated, processBodyExplicitly);
        doRequest(client, authenticated, processBodyExplicitly); // BUG: we get a 400 response
but should get a 200
    }
}

                
> HttpClient incorrectly reuses HTTP/1.1 connection - the response body of the first request
is considered as the response for the second request
> -----------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: HTTPCLIENT-1376
>                 URL: https://issues.apache.org/jira/browse/HTTPCLIENT-1376
>             Project: HttpComponents HttpClient
>          Issue Type: Bug
>    Affects Versions: 4.2.4
>            Reporter: Carl D'Halluin
>
> In the following scenario, the HttpClient incorrectly reuses the HTTP/1.1 stream, and
actually considers the response body of the first request, to be the HTTP response to a second
request.
> Details:
> 1. Client does a simple HTTP/1.1 GET request
> 2. Server responds with a 401 and a WWW-Authenticate header
> 3. Client repeats the GET request but now with an Authorization header
> 4. Server responds with a HTTP/1.1 200 OK but there is no content length. The server
sends a response body
> 5. HttpClient ignores the response body by closing the entity content InputStream (or
call EntityUtils.consume)
> 6. Client sends a new HTTP/1.1 GET request (totally unrelated to the previous one)
> 7. Client erroneously considers the response body received in step 5 as the response
to step 6
> Step 2 marks the HTTP connection as reusable. Step 4 should explicitly mark the connection
as non-reusable since the only thing the client can do to such an ugly reponse (no content-length)
is read until EOF. However, that does not happen in step 4. Hence in step 5 the code sees
that the connection is reusable, and doesnt bother consuming the response content at all.
The body is reused in step 7
> Here we see 4 times 2 subsequent request
> - first 2 are no authentication no streaming: HttpClient correctly does not reuse connection
(see port number)
> - next 2 are no authentication with streaming: HttpClient correctly does not reuse connection
> - next 2 are with authentication with streaming: HttpClient correctly does not reuse
connection
> - final 2 are with authentication no streaming: Bug - HttpClient tries to reuse connection
given error. Closing the HttpResponse InputStream does not correctly close the underlying
Socket
> {code}
> REQUEST: GET http://127.0.0.1:8777/non-authenticated.html
> RESPONSE STATUS: HTTP/1.1 200 OK - client is 127.0.0.1:49603
> RESPONSE BODY - SKIPPED
> REQUEST: GET http://127.0.0.1:8777/non-authenticated.html
> RESPONSE STATUS: HTTP/1.1 200 OK - client is 127.0.0.1:49604
> RESPONSE BODY - SKIPPED
> =======================
> REQUEST: GET http://127.0.0.1:8777/non-authenticated.html
> RESPONSE STATUS: HTTP/1.1 200 OK - client is 127.0.0.1:49605
> RESPONSE BODY:   HTTP/1.1 400 This is a request body - client is 127.0.0.1:49605
> RESPONSE BODY:   
> REQUEST: GET http://127.0.0.1:8777/non-authenticated.html
> RESPONSE STATUS: HTTP/1.1 200 OK - client is 127.0.0.1:49606
> RESPONSE BODY:   HTTP/1.1 400 This is a request body - client is 127.0.0.1:49606
> RESPONSE BODY:   
> =======================
> REQUEST: GET http://127.0.0.1:8777/authenticated.html
> RESPONSE STATUS: HTTP/1.1 200 OK - client is 127.0.0.1:49607
> RESPONSE BODY:   HTTP/1.1 400 This is a request body - client is 127.0.0.1:49607
> RESPONSE BODY:   
> REQUEST: GET http://127.0.0.1:8777/authenticated.html
> RESPONSE STATUS: HTTP/1.1 200 OK - client is 127.0.0.1:49608
> RESPONSE BODY:   HTTP/1.1 400 This is a request body - client is 127.0.0.1:49608
> RESPONSE BODY:   
> =======================
> REQUEST: GET http://127.0.0.1:8777/authenticated.html
> RESPONSE STATUS: HTTP/1.1 200 OK - client is 127.0.0.1:49609
> RESPONSE BODY - SKIPPED
> REQUEST: GET http://127.0.0.1:8777/authenticated.html
> RESPONSE STATUS: HTTP/1.1 400 This is a request body - client is 127.0.0.1:49609
> --> BUG! Response status 400
> RESPONSE BODY - SKIPPED
> {code}

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@hc.apache.org
For additional commands, e-mail: dev-help@hc.apache.org


Mime
View raw message