3

I am developing an Android app and have already found out that different Android versions have a different ways on how they handle Http(s)URLConnections (http://stackoverflow.com/q/9556316/151682).

I ran into the issue that Android 4 performs a POST request over HTTPS nicely, adding headers like Content-Type automatically when running the code below.

However, on Android 2.3.5 (device & emulator), any write to the output stream seems to be ignored - I debugged it with the web proxy Charles and while all headers are sent, the data written to the output stream is not sent along...

Anyone knows how to solve this?

Note: As the API I am developing against has only a self-signed certificate, I need to disable certification validation at this time.

TIA, Patrick

Update In the meantime, I also have tried to following, to no avail:

  • Calling close() after the flush() call on the BufferedOutputStream
  • Calling close() on both the OutputStream and the BufferedOutputStream
  • Using a OutputStreamWriter instead
  • Not calling close() before calling getInputStream()

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setConnectTimeout(CONNECT_TIMEOUT);
    
    connection.setDoOutput(true); // Triggers POST.
    connection.setRequestMethod("POST");
    int contentLength = 0;
    if(body != null) {
        contentLength = body.getBytes().length;
    }
    
    // Workarounds for older Android versions who do not do that automatically (2.3.5 for example)
    connection.setRequestProperty(HTTP.TARGET_HOST, url.getHost());
    connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
    
    // Set SSL Context -- Development only
    if(context != null && connection instanceof HttpsURLConnection){
        HttpsURLConnection conn = (HttpsURLConnection)connection;
        conn.setSSLSocketFactory(context.getSocketFactory());
        conn.setHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });
    }
    
    try{
        // Add headers
        if(headers != null){
            for (NameValuePair nvp : headers) {
                if(nvp != null){
                    connection.setRequestProperty(nvp.getName(), nvp.getValue());
                }
            }
        }
    
        connection.setFixedLengthStreamingMode(contentLength);
        OutputStream outputStream = null;
        try {
            if(body != null){
                outputStream = connection.getOutputStream();
                BufferedOutputStream stream = new BufferedOutputStream(outputStream);
                stream.write(body.getBytes()); // <<<< No effect ?!
                stream.flush();
    
            }
        } finally {
            if (outputStream != null) 
                try { 
                    outputStream.close(); 
                }
            catch (IOException logOrIgnore) {
                // ...
            }
        }
    
        InputStream inputStream = connection.getInputStream();
    
        // .... Normal case ....
    
    }
    catch(IOException e){
        // ... Exception! Check Error stream and the response code ...
    
    
    }
    finally{
        connection.disconnect();
    }
    

    }

Patrick
  • 4,720
  • 4
  • 41
  • 71
  • Just checking all bases since not all of your code is shown. Have you verified `body != null` and `contentLength > 0`? Do you actually log/check for any exceptions that might be occurring in the try block? What is the server response code? – kaliatech Apr 20 '12 at 16:25
  • server response code is 400 - bad request, I do log them, but for brevity's sake omitted them here... It fails at "getInputStream". body is checked for null-ness before writing to the stream. ContentLength is not explicitly checked here (thx for the hint), but it conforms with the body passed, whenever I verifies it. – Patrick Apr 21 '12 at 18:10

2 Answers2

6

To me it seems that the order in which you call DoSetOutput etc. is the reason for the strange behaviour...

Some links/source with working code for HTTP POST on Android:

Community
  • 1
  • 1
Yahia
  • 69,653
  • 9
  • 115
  • 144
  • Hi Yahia - thx for your input. I guessed something like this, but I can't find a hint that I misorder the calls... The API Doc is not really explicit in its sample code. I am currently comparing it to the samples you provide, but do you have an intuition which call might be in the wrong place? – Patrick Apr 14 '12 at 18:04
  • 1
    @Patrick you are welcome :-) I am not really sure... but I would recommend trying the `DefaultHttpClient`-based samples... – Yahia Apr 14 '12 at 18:07
  • I actually really wanted to use HttpURLConnection since their use is specifically recommended for new Applications by the Android team... (http://android-developers.blogspot.com/2011/09/androids-http-clients.html) But I'd have save a lot of hassle with DefaultHttpClient, that is true ;) – Patrick Apr 14 '12 at 18:11
  • @Patrick most of the samples above use `HttpURLConnection` and some can even be used as a copy&paste replacement for your code... – Yahia Apr 14 '12 at 18:21
  • @Patrick in the link you provided they explicitely statet that `DefaultHttpClient` has fewer bugs... – Yahia Apr 14 '12 at 18:23
  • "...has fewer bugs on Eclair and Froyo." but I am targeting Gingerbread, for which they recommend HttpURLConnection... ;) I am trying to use the samples, but some of them don't use request parameters, none uses the the hostname verifier. So it is quite hard to figure out where to put that again ;) – Patrick Apr 14 '12 at 18:59
4

Ah, I finally found it. Looking in the wrong place... The problem was the headers:

My code above actually works fine - the cause is that I included a header which is Base64 encoded. On my Android 2.3.5 the specified option (Base64.DEFAULT) seems to insert an extra newline at the end which ends the request prematurely, giving me no time to send the actual body. On Android 4, the Default seems to be changed to something like BASE64.NO_WRAP as explained in the post linked below...

This was actually already answered here.

Thanks for your efforts.

Community
  • 1
  • 1
Patrick
  • 4,720
  • 4
  • 41
  • 71