1

My Android code sends data all the time and it seems to be working until a totally unexpected IOException is thrown, freezing everything. I don't understand why I can send several post requests without problem until the Exception appears, almost randomly. Here's my code:

public class HttpsClient {

private final static String TAG = "HttpsClient";

private final static String TOKEN_HEADER_KEY = "Token";

private final String urlString;

private SSLContext sslContext;

// application-specific HTTP header
private String TokenHeaderValue = null;



public HttpsClient(String host, String path) {
    // this.sslContext will be init'ed in open()
    this.urlString = "https://" + host + ":443/" + path;
}


public boolean open() {
    try {
        this.sslContext = SSLContext.getInstance("TLS");
        this.sslContext.init(null, null, new java.security.SecureRandom());
        return true;
    } catch (NoSuchAlgorithmException e) {
        Logger.e(TAG, "NoSuchAlgorithmException:");
    } catch (KeyManagementException e) {
        Logger.e(TAG, "KeyManagementException:");
    }

    return false;
}


public byte[] send(byte[] req) {

    Logger.d(TAG, "sending " + Utils.byteArrayToString(req) + " to " + this.urlString);

    URL url;
    try {
        url = new URL(this.urlString);
    } catch (MalformedURLException e) {
        Logger.e(TAG, "MalformedURLException:");
        return null;
    }
    HttpsURLConnection connection;
    try {
        connection = (HttpsURLConnection) url.openConnection();
    } catch (IOException e) {
        Logger.e(TAG, "send IOException 1 " + ((null == e.getMessage()) ? e.getMessage() : ""));
        e.printStackTrace();
        return null;
    }

    connection.setDoInput(true);
    connection.setDoOutput(true);
    connection.setRequestProperty("Connection", "close");
    try {
        connection.setRequestMethod("POST");

    } catch (ProtocolException ignored) { }
    connection.setSSLSocketFactory(this.sslContext.getSocketFactory());
    connection.setReadTimeout(3000);



    if ( this.TokenHeaderValue != null )
        connection.setRequestProperty(TOKEN_HEADER_KEY, this.TokenHeaderValue);



    {
        final Map<String, List<String>> requestProps = connection.getRequestProperties();
        Logger.d(TAG, requestProps.size() + " Request header(s):");
        for (Map.Entry<String, List<String>> entry : requestProps.entrySet())
            for (String value : entry.getValue())
                Logger.d(TAG, " " + entry.getKey() + ": <" + value + ">");
    }

    try {
        // open up the output stream of the connection 
        DataOutputStream output = new DataOutputStream(connection.getOutputStream()); 

        // write out the data 
        output.write(req, 0, req.length);
        output.flush();


        Logger.i(TAG, "Response Code: " + connection.getResponseCode());
        Logger.i(TAG, "Response Message: " + connection.getResponseMessage()); 
    } catch (SocketTimeoutException e) {
        Logger.e(TAG, "SocketTimeoutException:" + ((null == e.getMessage()) ? e.getMessage() : ""));
        return null;
    } catch (IOException e) { // FAILS HERE !!!!!!!
        Logger.e(TAG, "send IOException 2 " + ((null == e.getMessage()) ? e.getMessage() : ""));
        return null;
    }


    final Map<String, List<String>> responseHeaderFields = connection.getHeaderFields();
    Logger.d(TAG, responseHeaderFields.size() + " Response header(s):");
    for (Map.Entry<String, List<String>> entry : responseHeaderFields.entrySet()) {
        final String key = entry.getKey();
        if ( (null != key) && key.equals(TOKEN_HEADER_KEY) )
            this.TokenHeaderValue = entry.getValue().get(0);
        for (String value : entry.getValue())
            Logger.d(TAG, " " + key + ": <" + value + ">");
    }


    // read response
    ArrayList<Byte> response = new ArrayList<Byte>();

    try {
        DataInputStream input = new DataInputStream(connection.getInputStream()); 

        // read in each character until end-of-stream is detected
        for( int c = input.read(); c != -1; c = input.read() ) {
            response.add((byte) c);
        }
        Logger.w(TAG, "Https connection is " + connection);
        connection.disconnect();
        Logger.w(TAG, "Https connection is " + connection);
        input.close();

    } catch (IOException e) {
        Logger.e(TAG, "send IOException 3 " + ((null == e.getMessage()) ? e.getMessage() : ""));
        return null;
    }

    if ( 0 == response.size() ) {
        Logger.w(TAG, "response is null");

        return null;
    }

    // else

    byte[] result = new byte[response.size()];
    for (int i = 0; i < result.length; i++)
        result[i] = response.get(i).byteValue();

    Logger.i(TAG, "Response payload: " + Utils.byteArrayToString(result));



    return result;          
}
}

I repeat: code works 'til after many post send, then crashes 'cause of an IO Exception. Moreover, server is fine, there's something wrong in my code.

Here's the entire error stack trace:

send IOException 2 

0: org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_read_byte(Native Method)

1: org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLInputStream.read(OpenSSLSocketImpl.java:783)

2: org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.readLine(HttpURLConnectionImpl.java:671)

3: org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.readResponseHeaders(HttpURLConnectionImpl.java:699)

4: org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getFromNetwork(HttpURLConnectionImpl.java:1088)

5: org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.retrieveResponse(HttpURLConnectionImpl.java:1041)

6: org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:736)

7: org.apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:146)

8: com.bitstorms.gui.channels.HttpsClient.send(HttpsClient.java:117)

9: com.bitstorms.gui.apps.Application$MyRunnable.run(Application.java:81)

10: java.lang.Thread.run(Thread.java:1020)

1 Answer 1

1

The suspicious point is that you never properly release streams. For instance, you create DataOutputStream output, use it, but never close. Try to change the code that uses finally to close the stream:

DataOutputStream output = null;
try {
    output = new DataOutputStream(connection.getOutputStream());
    ...
} catch (...) {
    ...
} finally {
    if (output != null) {
         try {
             output.close();
         } catch (IOException ignored) {}
    }
}

The same should be done with DataInputStream input and the connection itself (it should be guaranteedly closed in some global finally section).

Sign up to request clarification or add additional context in comments.

14 Comments

I tried to put the finally section you're talking about to close streams (input also) but nothing, code's behaviour is the same... I don't understand...
@FrancescoNovecentoNicolosi: Then I am out of ideas. Could you post the error stacktrace (you can edit your initial post and append it with the stacktrace). Probably it could give some ideas.
Just edited my question. Provided the entire stack trace, did you see it?
I'm using a Samsung Galaxy tablet to try this client, with Android 3.2, are there some known bug in that release about httpsurlconnection?
@FrancescoNovecentoNicolosi: Just checked the stack trace. As you see it happens at the native method NativeCrypto.SSL_read_byte(). I have no idea why it fails. Its API doc just says it may throw the IOException, but without any explanation of possible reason(s).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.