8

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?

Hamzeh Soboh
  • 7,572
  • 5
  • 43
  • 54
  • Sure, there is a problem with activity, please add your code. – W4R10CK Feb 05 '17 at 12:56
  • @W4R10CK I'm just making a call inside AsyncTask – Hamzeh Soboh Feb 09 '17 at 10:28
  • @HamzehSoboh which line is throwing already connected? – Laurentiu L. Feb 09 '17 at 10:53
  • Btw, that conn.disconnect() block should really be done in a finally block. It's probably why you get the errors, as there might be some other exception that proceed already connected message and then all subsequent ones will fail. – Laurentiu L. Feb 09 '17 at 10:59
  • After this line `conn = (HttpURLConnection) url.openConnection();` directly `conn.connected` value is `true` and any operation like `setUseCaches` or `setDoOutput` will through the exception. – Hamzeh Soboh Feb 09 '17 at 11:02
  • Then I bet it's because you don't close the connection in the finally block. – Laurentiu L. Feb 09 '17 at 11:04
  • https://www.tbray.org/ongoing/When/201x/2012/01/17/HttpURLConnection review and also consider the app eng links included at bottom. maybe the sample code can help u spot something or maybe the app eng stuff uses boilerplate that will solve u issue... – Robert Rowntree Feb 09 '17 at 11:04
  • Thanks all, but it's not about calling `disconnect`, it's being called indeed. The weird thing is that even for the first time I'm getting an exception. Also I've changed the url and for the first call, I've got that exception! I'm thinking about just moving to apache `HttpClient` :( – Hamzeh Soboh Feb 09 '17 at 11:44
  • this question is being asked over and over; this is the most likely explanation: http://stackoverflow.com/questions/29906562/illegal-state-exception-already-connected-when-using-httpurlconnection/38072310#38072310 – Martin Zeitler Feb 15 '17 at 11:39
  • Why do you want to use conventional http post method, instead you can go for network library like Volley or Retrofit – Sri Kanth Feb 15 '17 at 13:11
  • Thanks syslogic, I've almost passed by every possible similar discussion, but I think my case is different. – Hamzeh Soboh Feb 15 '17 at 13:24
  • Thanks Sri Kanth, it still an open choice to use Retrofit. But I thought of creating my own module to customize it fit my needs. Also using native modules to reduce apk size and avoid enabling multi-dex. – Hamzeh Soboh Feb 15 '17 at 13:29
  • Y dont you try to `conn.connect()` after posting all the output stream . Try this if it works , I will post the reason of it. – Nouman Ghaffar Feb 16 '17 at 06:23

3 Answers3

3

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.

kris larson
  • 30,387
  • 5
  • 62
  • 74
  • Thanks very much for your answer! But can you imagine that your code and mine crashes at `conn.setUseCaches` and on the very first request! I started to believe that a request by a third party is not disconnected. I'm importing firebase crash reporting and google analytics into my project. But I'm not sure that is the reason. What makes me think so is that when I create a new project and execute the same exact code it works flawlessly! – Hamzeh Soboh Feb 12 '17 at 08:04
  • If your code works by itself in a new project, then you need to take that project and add your firebase or analytics code back in a little at a time until you find the change that causes your code to crash. *Something* is causing open connections to be returned to your connection pool, you need to find out what it is. – kris larson Feb 13 '17 at 03:02
  • OK kris, you got the points, but I didn't accept the answer since it still anonymous to me what causes this issue and why this happens randomly! Thanks. – Hamzeh Soboh Feb 17 '17 at 06:31
2

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");
    }
BMU
  • 429
  • 2
  • 9
0

There is some tuts in online that might help you

  1. Android Httpurlconnection Post and Get Request Tutorial here

  2. How to use HttpURLConnection POST data to web server? here

  3. Android POST and GET Request using HttpURLConnection Tutorial here

Hemanth S
  • 669
  • 6
  • 16