I have a python webserver, which is listening for connections, and responds to them based on the request. The portion of code on python server of interest is POST request (http://192.168.0.1/conf_wifi_app.html). It takes 4 arguments
username (admin)
password (admin)
essid (Home wifi network SSID)
passphrase (Home wifi network password)
On python server, after the post body parameters are validated, a response is to be sent like so (notice I've put logs for debugging):
json_response = '{"error":false,' + '"code":"' + str(activation_code) + '","mac":"' + str(macaddress) + '","message":"Device configured successfully"}'
bytes_sent = client_s.send(json_response)
client_s.close()
print("Bytes sent " + str(bytes_sent))
print("json_response : " + json_response)
where client_s is the client socket. "send" function on socket should send the response (json_response), and then we close the socket. Logs print the number of bytes which is actually sent.
The client responds perfectly well when POST request is done from the web browser or from postman plugin. Just for some reference, I've put the raw request when invoked from postman plugin on chrome browser:
POST /conf_wifi_app.html HTTP/1.1
Host: 192.168.0.1
Connection: keep-alive
Content-Length: 67
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/68.0.3440.75 Chrome/68.0.3440.75 Safari/537.36
Cache-Control: no-cache
Origin: chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop
Postman-Token: 4f4a14a7-857d-666f-a2db-279731c83b4a
Content-Type: application/x-www-form-urlencoded
Accept: /
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
essid=NETGEAR-XXXX&passphrase=XXXXXXXX&username=admin&password=admin&submit=submit
The response is received when the POST request is made from web browser (or postman). Now I was trying to create an android app which does the same POST request as follows:
HttpURLConnection urlConnection = null;
try {
Map<String,Object> params = new LinkedHashMap<>();
params.put("user", Constants.DEVICE_DEFAULT_USER);
params.put("username", Constants.DEVICE_DEFAULT_USER);
params.put("password", Constants.DEVICE_DEFAULT_PASSWORD);
params.put("essid", homeWifiSSID.replaceAll("^\"|\"$", ""));
params.put("passphrase", homeWifiPassword);
StringBuilder urlParameters = new StringBuilder();
for (Map.Entry<String,Object> param : params.entrySet()) {
if (urlParameters.length() != 0) urlParameters.append('&');
urlParameters.append(URLEncoder.encode(param.getKey(), "UTF-8"));
urlParameters.append('=');
urlParameters.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
}
byte[] postData = urlParameters.toString().getBytes("UTF-8");
int postDataLength = postData.length;
URL url = new URL(Constants.DEVICE_CONFIG_URL);
urlConnection = (HttpURLConnection) network.openConnection(url);
urlConnection.setDoInput( true );
urlConnection.setDoOutput( true );
urlConnection.setInstanceFollowRedirects( false );
urlConnection.setRequestMethod( "POST" );
urlConnection.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded");
urlConnection.setRequestProperty( "charset", "utf-8");
urlConnection.setRequestProperty( "Content-Length", Integer.toString( postDataLength ));
urlConnection.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8");
urlConnection.setRequestProperty("Accept-Encoding", "gzip, deflate");
urlConnection.setUseCaches( false );
urlConnection.setConnectTimeout(OuroborosAPI.CONNECTION_TIMEOUT);
urlConnection.setReadTimeout(OuroborosAPI.CONNECTION_TIMEOUT);
try( DataOutputStream wr = new DataOutputStream( urlConnection.getOutputStream())) {
wr.write( postData );
wr.flush();
wr.close();
}
Reader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
for (int c; (c = in.read()) >= 0;) {
System.out.print((char) c);
}
}
catch (Exception e) {
}
From android app, the post raw data received is as follows:
POST /conf_wifi.html HTTP/1.1
Content-Type: application/x-www-form-urlencoded
charset: utf-8
Content-Length: 85
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1.1; Moto G (5S) Plus Build/NPSS26.116-61-11)
Host: 192.168.0.1
Connection: Keep-Alive
Accept-Encoding: gzip
essid=NETGEAR-XXXX&passphrase=XXXXXXXX&username=admin&password=admin&submit=submit
The python socket in this case does send data (as confirmed from the logs), but the android errors out saying unexpected end of string.
I've literally tried every thing (like adding extra headers, etc) but to no avail. Please help or suggest where I may be going wrong.
The problem was not that I was not sending \n ended lines, or not using readline()
I was not sending the HTML raw headers like so
HTTP/1.1 200 OK
Content-Length: 9328
Content-Type: text/html
... Actual content/payload....
Once I sent the headers also, the code worked without any problems.
Related
I want to send a simple POST command to upload files. How can I write the code in android developer? Code should give the following POST request
POST /macros/s/AqtUErjtk/postform?no&func=uploadFiles HTTP/1.1
Host: xyz.website.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: multipart/form-data; boundary=---------------------------236532717524914
Content-Length: 337
Cookie:
NID=106=gWQeUVIa2phdDJeXYdRFSPTnsklPrFVRwphw3G685QYZlDiZz7NK5PoJVEd1FYL6IqYoJ9fEtVHf0sKBIHq1wD1xr
Connection: close
Upgrade-Insecure-Requests: 1
-----------------------------236532717524914
Content-Disposition: form-data; name="_1_myFile"; filename="textfile.txt"
Content-Type: text/plain
Here is the text file content yoyoyoyoyoy
-----------------------------236532717524914--
In the following link you will see an example in which a POST is made:
https://community.particle.io/t/example-android-application-post-get/9355
This is the exact code extracted from the link above:
class PostClient extends AsyncTask<String, Void, String> {
public String doInBackground(String... IO) {
// Predefine variables
String io = new String(IO[0]);
URL url;
try {
// Stuff variables
url = new URL("https://api.spark.io/v1/devices/YOURCOREID/SCL/");
String param = "access_token=YOURACCESSTOKEN¶ms=d7,"+io;
Log.d(TAG, "param:" + param);
// Open a connection using HttpURLConnection
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
con.setReadTimeout(7000);
con.setConnectTimeout(7000);
con.setDoOutput(true);
con.setDoInput(true);
con.setInstanceFollowRedirects(false);
con.setRequestMethod("POST");
con.setFixedLengthStreamingMode(param.getBytes().length);
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
// Send
PrintWriter out = new PrintWriter(con.getOutputStream());
out.print(param);
out.close();
con.connect();
BufferedReader in = null;
if (con.getResponseCode() != 200) {
in = new BufferedReader(new InputStreamReader(con.getErrorStream()));
Log.d(TAG, "!=200: " + in);
} else {
in = new BufferedReader(new InputStreamReader(con.getInputStream()));
Log.d(TAG, "POST request send successful: " + in);
};
} catch (Exception e) {
Log.d(TAG, "Exception");
e.printStackTrace();
return null;
}
// Set null and we´e good to go
return null;
}
}
}
I hope it helps.
I have an app that at the moment I'm just sending some dummy data through a POST request to a Flask server. The flask server is not seeing the post data. When I send it to a sinatra server the post data is there no problem.
Android Code:
URL url = new URL(imageRequests[0].getUrl());
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
String urlParameters = "param1=a¶m2=b¶m3=c";
System.out.println(urlParameters);
urlConnection.setDoOutput(true);
urlConnection.setChunkedStreamingMode(0);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream ());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
Flask Code:
#app.route('/', methods=['POST'])
def submit():
print request.form
return "Uploaded"
Flask Output:
Loading from savePath test.tree
* Running on http://0.0.0.0:5000/
ImmutableMultiDict([])
10.100.85.69 - - [25/Jul/2014 17:09:07] "POST / HTTP/1.1" 200 -
Sinatra Code:
post '/' do
puts params
"Uploaded"
end
Sinatra Output:
== Sinatra/1.4.5 has taken the stage on 4567 for development with backup from WEBrick
[2014-07-25 17:07:27] INFO WEBrick::HTTPServer#start: pid=18674 port=4567
{"param1"=>"a", "param2"=>"b", "param3"=>"c"}
10.100.85.69 - - [25/Jul/2014 17:07:35] "POST / HTTP/1.1" 200 12 0.0053
10.100.85.69 - - [25/Jul/2014:17:07:34 BST] "POST / HTTP/1.1" 200 12
- -> /
I'm really confused as to why Sinatra is getting the post data, but flask isn't. My only guess is that the POST request from android is not quite right and that sinatra is more forgiving on that.
Is this the case?
Edit: Output from netcat listening over the port
POST / HTTP/1.1
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.4.4; Nexus 7 Build/KTU84P)
Host: 10.100.85.210:5000
Connection: Keep-Alive
Accept-Encoding: gzip
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: chunked
1b
?param1=a¶m2=b¶m3=c
0
Changed it to this. It seems to have been a problem with chunking the output
URL url = new URL(imageRequests[0].getUrl());
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
String urlParameters = "param1=value¶m2=value";
System.out.println(urlParameters);
urlConnection.setDoOutput(true);
urlConnection.setRequestProperty("Content-Length", String.valueOf(urlParameters.getBytes().length));
urlConnection.setRequestMethod("POST");
OutputStream os = urlConnection.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
writer.write(urlParameters);
writer.close();
I am facing a weird problem. I've a piece of code that makes a call to a server. The code works fine when I use Kitkat emulator. However, the same code does not work on Gingerbread phones or emulator. I always get 400 Bad Request from the server. I checked on the server. The error is:
client sent HTTP/1.1 request without hostname (see RFC2616 section 14.23):
Apache is not configured as virtual host.
here is the code I am using:
public void makeServerCall(String serverUrl, String postString) {
URL url = new URL(serverURL + ESS_AUTHENTICATE_URL);
connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod(HTTP_POST);
//forcing user agent to a well known user agent with a hope it will work :-)
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; U; PPC; en-US; rv:1.3.1)");
String base64Tenant = "Basic " + Base64.encodeToString( auth.getBytes(), Base64.DEFAULT);
connection.setRequestProperty(AUTHENTICATE_HEADER, base64Tenant);
connection.addRequestProperty("Cache-Control", "no-cache");
connection.addRequestProperty("Pragma", "no-cache");
connection.setUseCaches(false);
connection.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Accept-Charset", "UTF-8");
System.setProperty("http.keepAlive", "false");
connection.setFixedLengthStreamingMode(postString.getBytes().length);
// Send request
PrintWriter out = new PrintWriter(connection.getOutputStream());
out.print(postString);
out.close();
int respCode = connection.getResponseCode();
Log.i(TAG, "Response code = " + respCode);
if (respCode != 200) {
is = connection.getErrorStream();
if (is != null) {
String response = getStringFromStream(is);
Log.d(TAG, "Error occured: " + response);
is.close();
}
}
else {
// process success Response
}
}
Here is the wireshark capture I got when I run the code on Kitkat simulator
POST /removed_path HTTP/1.1
Accept-Charset: UTF-8
User-Agent: Mozilla/5.0 (Macintosh; U; PPC; en-US; rv:1.3.1)
Authorization: Basic ASVGQVVMVEASDFw==
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/x-www-form-urlencoded
Host: correcthost.myserver.com
Connection: Keep-Alive
Accept-Encoding: gzip
Content-Length: 49
this is the post stringHTTP/1.1 200 OK
Date: Tue, 04 Feb 2014 07:52:42 GMT
Here is the wireshark capture from Gingerbread
POST /removed_path HTTP/1.1
Accept-Charset: UTF-8
User-Agent: Mozilla/5.0 (Macintosh; U; PPC; en-US; rv:1.3.1)
Authorization: Basic ASVGQVVMVEASDFw==
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/x-www-form-urlencoded
Content-Length: 49
Host: correcthost.myserver.com
Connection: Keep-Alive
Accept-Encoding: gzip
The only difference between the two wiresharks difference is the order of content-length.
I've access to server too. I am not sure, I can change anything there because it is working for recent versions of Android and iOS. Any ideas on what is going wrong?
The funny thing is: I may have accidentally found the solution for Is it possible to send HTTP request using plain socket connection and receive response without headers?. I hope I'm overlooking something.
Anyway, I'm connecting to a web server, send a HTTP request, receive the response and close the connection. The actual HTTP conversation (tcpdump on the server side) looks like this:
GET /blah.php?param1=test1t¶m2=test2 HTTP/1.0
HTTP/1.1 200 OK
Date: Sat, 22 Sep 2012 10:16:46 GMT
Server: Apache/2.2.17 (Ubuntu)
X-Powered-By: PHP/5.3.5-1ubuntu7.8
Vary: Accept-Encoding
Content-Length: 17
Connection: close
Content-Type: text/html
<pre>173364</pre>
Cool. It works... so far. Now this is the code. The string szRetval contains only "<pre>173364</pre>".
Socket s = new Socket("1.2.3.4", 80);
//DataInputStream in = new DataInputStream(s.getInputStream());
BufferedReader bin = new BufferedReader(new InputStreamReader(s.getInputStream()));
DataOutputStream out = new DataOutputStream(s.getOutputStream());
out.writeBytes(szRequest);
String szRetval = "";
String szLine = "";
while((szLine=bin.readLine())!=null) {
szRetval += szLine;
}
s.close();
return szRetval;
As you can see from the code sample, I've already switched from DataInputStream to BufferedReader. It makes no difference, so I guess this has to do with the Socket class itself.
Why o why are the http response headers not returned by a (raw) socket??
Android 2.3.3, Network I/O in AsyncTask, not using proxy.
I have a simple WCF web service on my machine which I have developed to serve Android and IOS devices.
The service has a single method as following :
[OperationContract]
[WebInvoke(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped, UriTemplate = "/?message={message}")]
string ServiceMessage(string message);
I have 3 clients , one .NET test client using HttpWebRequest which works fine , one IOS client which works fine and one Android client which I have developed with the HttpPost and HttpClient classes that fails.
Using Fiddler reverse proxy I have debugged the output of the .net client :
POST http://127.0.0.1:8888/Service1.svc/?message=_|JSON MESSAGE BODY|_HTTP/1.1
Content-Type: application/json; charset=utf-8
Host: 127.0.0.1:8888
Content-Length: 338
Expect: 100-continue
Connection: Keep-Alive
_|JSON MESSAGE BODY|_
On the other hand , this is the output of the Android HTTP Post :
POST http://10.0.2.2:8888/Service1.svc/ HTTP/1.1
Content-Type: application/json; charset=utf-8
Content-Length: 139
Host: 10.0.2.2:8888
Connection: Keep-Alive
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)
Expect: 100-Continue
message=_|JSON MESSAGE BODY|_
As you can see , .Net puts the message parameter on the Post at the top and does not
put the message variable name at the bottom while Android does not put the message body at the Post at the top and does put the message variable name at the bottom.
This is my Android post code ,
HttpPost httpPost = new HttpPost(url);
String messageBody = "message=" + jsonMessageParameter;
StringEntity entity = new StringEntity(messageBody);
httpPost.setEntity(entity);
httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
HttpResponse response = hc.execute(httpPost);
InputStream content = response.getEntity().getContent();
String json = ConvertStreamToString(content);
When calling with this code , the server method is called but the message method parameter is null.
I tried playing with the Uri.Builder class to also make the android post put the message at the header , but doesnt quite work.
If can someone help me out here , I am stuck on this for hours over hours.
Thank you in advance ,
James
EDIT :
I changed the Android code to :
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
nameValuePairs.add(new BasicNameValuePair("message", jsonMessageParameter));
HttpPost httpPost = new HttpPost(url);
UrlEncodedFormEntity urlEncodedFromEntity = new UrlEncodedFormEntity(
nameValuePairs);
urlEncodedFromEntity.setContentType(new BasicHeader("Content-Type",
"application/json; charset=utf-8"));
httpPost.setEntity(urlEncodedFromEntity);
InputStream postStream = httpPost.getEntity().getContent();
String postOutput = ConvertStreamToString(postStream);
HttpResponse response = hc.execute(httpPost);
InputStream content = response.getEntity().getContent();
String json = ConvertStreamToString(content);
But still the Fiddler monitoring is as following :
POST http://10.0.2.2:8888/Service1.svc/ HTTP/1.1
Content-Length: 189
Content-Type: application/json; charset=utf-8
Host: 10.0.2.2:8888
Connection: Keep-Alive
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)
Expect: 100-Continue
message=_|MESSAGE_JSON|_
There a number of points here.
First, you specifically added message= to your Android POST body:
String messageBody = "message=" + jsonMessageParameter;
This can be also problematic since you specified Content-Type: application/json but by adding message= you are not providing a valid JSON object.
Second, why does the .Net implementation replicate the JSON object both as parameter in the URL and in the body? This looks strange and very uncommon for a POST request, and can cause problem if your JSON object makes the URL exceed the maximum URL length.
So I would try removing message= in the body and removing the JSON object as a URL parameter, since servers processing POSTs should read the body and not the URL.
HttpPost is for posting, afaik.
String url;
input = url + URLEncoder.encode(messageBody, "utf-8");
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet(input);
InputStream stream = httpclient.execute(httpget).getEntity().getContent();
String s = streamToString(stream);
JSONObject jObject = new JSONObject(s);
This very exact code (plus some try/catch) works for me for querying GoogleBooks, probably you can adapt it to your service with little effort?
Best regards.