Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android POST request using HttpUrlConnection getting "already connected"

I'm trying to make a POST call using HttpUrlConnection but with no success. I'm getting 'IllegalStateException: already connected' error message frequently. I'm not interested in reusing the connection. Please check my code and tell me if I'm doing anything wrong:

public static final int CONNECTION_TIME_OUT = 10000;

public SimpleResponse callPost(String urlTo, Map<String, String> params) {
    System.setProperty("http.keepAlive", "false");
    HttpURLConnection conn = null;
    SimpleResponse response = new SimpleResponse(0, null);
    try {
        URL url = new URL(urlTo);
        conn = (HttpURLConnection) url.openConnection();
        conn.setUseCaches(false);
        conn.setAllowUserInteraction(false);
        conn.setConnectTimeout(CONNECTION_TIME_OUT);
        conn.setReadTimeout(CONNECTION_TIME_OUT);
        conn.setInstanceFollowRedirects(false);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Connection", "close");
        conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded");
        conn.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
        conn.setDoOutput(true);

        OutputStream os = conn.getOutputStream();
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
        writer.write(paramsToString(params));
        writer.flush();
        writer.close();
        os.close();

        int responseCode = conn.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK) {
            InputStream in = conn.getInputStream();
            String result = StringUtils.fromInputStream(in);
            response = new SimpleResponse(responseCode, result);
            in.close();
        } else {
            response = new SimpleResponse(responseCode, null);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    if (conn != null) {
        conn.disconnect();
    }
    return response;
}

private String paramsToString(Map<String, String> params) {
        if (params == null || params.isEmpty()) {
            return "";
        }
        Uri.Builder builder = new Uri.Builder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            builder.appendQueryParameter(entry.getKey(), entry.getValue());
        }
        return builder.build().getEncodedQuery();
    }

Update:

Works sometimes, and sometimes doesn't!
Works on some projects, on others doesn't!
The same exact code, and each time the same exception: Already connected
Why I'm not able to get a new fresh connection each time?

like image 526
Hamzeh Soboh Avatar asked Feb 05 '17 12:02

Hamzeh Soboh


2 Answers

I think your problem is this:

        conn.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));

I can't see where postDataBytes is declared, but since you're processing the parameters in paramsToString, my guess is that they have no relationship.

Now, I'm not an expert on RFC 2616 (HTTP), but what I think is happening is that the length of postDataBytes is larger than your request size, so the server is not disconnecting the socket on its end. Those URLConnection objects are pooled, so when you go to get a connection object, its values have been cleaned up for reuse, but the actual connection is still open.

Here's some code I think you should try. If it doesn't fix your problem, I don't get rep bonus, but it's still definitely more correct than what you have:

private static final String CHARSET = "ISO-8859-1";  // or try "UTF-8"

public SimpleResponse callPost(String urlTo, Map<String, String> params) {

// get rid of this...
//    System.setProperty("http.keepAlive", "false");

    HttpURLConnection conn = null;
    SimpleResponse response = new SimpleResponse(0, null);
    try {
        URL url = new URL(urlTo);
        conn = (HttpURLConnection) url.openConnection();
        conn.setUseCaches(false);
        conn.setAllowUserInteraction(false);
        conn.setConnectTimeout(CONNECTION_TIME_OUT);
        conn.setReadTimeout(CONNECTION_TIME_OUT);
        conn.setInstanceFollowRedirects(false);
        conn.setRequestMethod("POST");
// ... and get rid of this
//        conn.setRequestProperty("Connection", "close");
        conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded; charset=" + CHARSET);

        String content = paramsToString(params);
        int length = content.getBytes(Charset.forName(CHARSET)).length;
        conn.setRequestProperty("Content-Length", Integer.toString(length));
        conn.setDoOutput(true);

        OutputStream os = conn.getOutputStream();
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, CHARSET));
        writer.write(content);
        writer.flush();
        writer.close();
        os.close();

        int responseCode = conn.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK) {
            InputStream in = conn.getInputStream();
            String result = StringUtils.fromInputStream(in);
            response = new SimpleResponse(responseCode, result);
            in.close();
        } else {
            response = new SimpleResponse(responseCode, null);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    if (conn != null) {
        conn.disconnect();
    }
    return response;
}

I apologize for any compile errors. I'm useless without an IDE. I proofed it the best I could.

I used the Latin-1 encoding. If that doesn't do it for you, you can try UTF-8.

Another thing you can try is giving up on the content length altogether and calling

        conn.setChunkedStreamingMode(0);

And yes, I realize that the getBytes() call and OutputStreamWriter are duplicating the same process. You can work on that once you get this problem fixed.

like image 123
kris larson Avatar answered Nov 02 '22 23:11

kris larson


I cannot seem to figure out why you get an "Already connected error" but here's the code I use to make POST requests. I close the input/output streams and the connection after sending the request , that may help you (though i'm not sure exactly)

try {
        URL url = new URL(stringURL);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
        connection.setRequestProperty("Accept","application/json");
        //connection.setRequestProperty("Content-Length", Integer.toString(urlParameters.getBytes().length));
        //connection.setRequestProperty("Connection", "Keep-Alive");
        connection.setReadTimeout(10*1000);
        connection.setUseCaches(false);
        connection.setDoInput(true);
        connection.setDoOutput(true);

        //Request
        DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
        wr.writeBytes(params[1]);
        wr.flush();
        wr.close();

        //Response
        InputStream is = connection.getInputStream();
        BufferedReader rd = new BufferedReader(new InputStreamReader(is));
        String line;
        response = new StringBuffer();
        //Expecting answer of type JSON single line {"json_items":[{"status":"OK","message":"<Message>"}]}
        while ((line = rd.readLine()) != null) {
            response.append(line);
        }
        rd.close();
        System.out.println(response.toString()+"\n");
        connection.disconnect(); // close the connection after usage

    } catch (Exception e){
        System.out.println(this.getClass().getSimpleName() + " ERROR - Request failed");
    }
like image 39
BMU Avatar answered Nov 02 '22 22:11

BMU