Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android HttpPost message won't send its payload across the wire

I'm trying to send a simple string as the contents of a HttpPost message.

The problem is, the body of the HttpPost message never makes it to the wire. (Says the Wireshark capture). The header looks just fine though (including the correctly calculated Content-Length.

Here's what the code looks like:

String url = "http://1.2.3.4/resource";
HttpClient client = new DefaultHttpClient();
String cmd = "AT+AVLPOS\r\n";
StringEntity se = new StringEntity(cmd);
se.setContentType("text/plain");  

HttpPost request = new HttpPost(url);
request.setHeader("Content-Type","text/plain");
request.setEntity(se);

HttpResponse response = client.execute(request);
[...]

The string should be ASCII-encoded, but that's a detail at this point.

This is what shows up in WireShark: -> note that lines marked with + are what's sent, and - is what's received.

+POST /resource HTTP/1.1
+Content-Type: text/plain
+Content-Length: 11
+Host: 1.2.3.4
+Connection: Keep-Alive
+User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)
+Expect: 100-Continue

-HTTP/1.1 200 OK
-Content-Type: text/plain
-Transfer-Encoding: chunked

-4
-OK

This is what should show up (wrote a very simple console app in C# to do this, it just works):

+POST /resource HTTP/1.1
+Content-Type: text/plain
+Host: 1.2.3.4
+Content-Length: 11
+Expect: 100-continue
+Connection: Keep-Alive
+
-HTTP/1.1 200 OK
-Content-Type: text/plain
-Transfer-Encoding: chunked
-
+AT+AVLPOS
+
-4
-OK
-
-48
-$AVTMR,99999999,204810,A,1234.2218,N,0123.1051,E,0,20,150811,0,REQ*69
-
-0
-

Any suggestions?

like image 655
Cristian Diaconescu Avatar asked Aug 16 '11 12:08

Cristian Diaconescu


1 Answers

I've figured it out, AND I've learned something today.

Long story short: disable the HttpClient's HTTP Post expect-continue handshake, by setting one of its parameters, this will send the whole request message in one chunk.

//set up HttpPost request as before
HttpClient client = new DefaultHttpClient();
client.getParams().setBooleanParameter("http.protocol.expect-continue", false);
HttpResponse response = client.execute(request);
[...]

Now here's how I got there, maybe this will help someone someday.

First I derived from a HttpEntityWrapper and used that as my request entity to see what gets called when, and found out that the Entity's writeTo(OutputStream) method was never called at all.

Then I started to look at why, in the case of the "correct" behaviour, the POST request wasn't sent all at once, and instead, the request headers were sent, then the response header is received, THEN the request body is sent.

IT's all got to do with the HTTP Post expect-continue handshake. Read more about it on Haacked. If the expect-continue header is sent in a request, the Http server SHOULD reply with a 100 Continue message signifying "OK, I will accept your message", or with an error, stopping the possibly long POST message in its tracks.

Unfortunately, the web server I run against is a bare bones implementation that runs on a chip, and it sends the wrong reply (200 OK instead of 100 Continue).
The default implementation of the .NET Http Client seems to be more forgiving here: it treats the 200 message as 100 Continue, shrugs, and gets on its way to send the request body.

Not so with the Http client implementation of Android (API level 7).

Next thing I tried was to disable the expect-continue handshake completely, in order to make the HttpClient send the whole request. To my surprise and joy, this was handled fine by the web server, which replied with the information I wanted. Yay!

like image 113
Cristian Diaconescu Avatar answered Sep 30 '22 19:09

Cristian Diaconescu