Getting Bad Request only on Gingerbread phones and simulator - android

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?

Related

Android could not receive form submit response

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.

how to parse server socket input stream to get json object?

In my android app, I have a listener thread which listens for anything posted over a server socket. Once something is posted (I use HttpRequester tool to POST any message), I get the input stream. I am able to see my json inside the input stream but cannot find a way to parse to get the json object out of it.
Here is my code sample doing it:
iMessageListenerThread = new Thread() {
public void run(){
// wait till user connects to server
try
{
iServerSocket = new ServerSocket();
iServerSocket.setReuseAddress(true);
iServerSocket.bind(new InetSocketAddress(mPort));
Log.d(TAG,"Server Socket opened" );
try {
for (;;) {
Socket ss = iServerSocket.accept(); // unblocks when connection is requested
// test if something received, if so, display this
InputStream localInputStream = ss.getInputStream();
if(localInputStream == null){
Log.d(TAG,"LOCAL INPUT STREAM NULL" );
continue;
}
//localInputStream = new GZIPInputStream(localInputStream);
String resultstring = convertStreamToString(localInputStream);
-----
-----
private String convertStreamToString(InputStream is) {
String line = "";
StringBuilder total = new StringBuilder();
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
try {
while ((line = rd.readLine()) != null) {
total.append(line+"\n");
total.append(line);
}
} catch (Exception e) {
Toast.makeText(this, "Stream Exception", Toast.LENGTH_SHORT).show();
}
return total.toString();
}
In the HttpRequester tool, i put the following url:
http://xxx.xxx.xx.xxx:8000/powerstate
and in the content section, I put this json:
{"powerstate":"on"}
This is my response string containging the json object:
I/RecorderService( 1518): ...resultstring : POST /powerstate HTTP/1.1
I/RecorderService( 1518): POST /powerstate HTTP/1.1Host: 192.168.21.240:8000
I/RecorderService( 1518): Host: xxx.xxx.xx.xxx:8000User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:30.0) Gecko/20100101 Firefox/30.0
I/RecorderService( 1518): User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:30.0) Gecko/20100101 Firefox/30.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
I/RecorderService( 1518): Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Language: en-US,en;q=0.5
I/RecorderService( 1518): Accept-Language: en-US,en;q=0.5Accept-Encoding: gzip, deflate
I/RecorderService( 1518): Accept-Encoding: gzip, deflateContent-Type: application/json; charset=UTF-8
I/RecorderService( 1518): Content-Type: application/json; charset=UTF-8Content-Length: 19
I/RecorderService( 1518): Content-Length: 19Connection: keep-alive
I/RecorderService( 1518): Connection: keep-alivePragma: no-cache
I/RecorderService( 1518): Pragma: no-cacheCache-Control: no-cache
I/RecorderService( 1518): Cache-Control: no-cache
I/RecorderService( 1518): {"powerstate":"on"}
Can someone help me to know how to extract json object from this inputstream

Caching of responses in HttpUrlConnection of android

In my app , i have implemented HttpResponseCache to cache the responses so that it could be used instead of hitting the server. For a particular api , the server returns the header Cache-Control as no-cache;must-revalidate. It has the header ETag also. The problem is the response of this api is not cached. As a result each time I request the api , server returns 200.
Does no-cache,must-revalidate mean the response won't/shouldn't be cached ?
Please find below the request and response headers of the http request :
Request Headers :
GET HTTP/1.1
User-Agent
Accept application/json
Cache-Control max-age=0
Cookie
Host
Connection Keep-Alive
Accept-Encoding gzip
Response Headers :
HTTP/1.1 200 OK
Server Apache-Coyote/1.1
Cache-Control no-cache, must-revalidate
ETag "c683a0301c68c566fcc706f5cd82f1f8"
Content-Type application/json;charset=UTF-8
Transfer-Encoding chunked
Content-Encoding gzip
Vary Accept-Encoding
Date Mon, 24 Feb 2014 04:44:03 GMT
Sending HTTP_GET request :
URL url = new URL(this.url);
HttpURLConnection conn = null;
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(timeout);
conn.setConnectTimeout(timeout);
conn.setInstanceFollowRedirects(true);
conn.setRequestProperty("User-Agent", this.userAgent);
conn.setUseCaches(true);
conn.setDefaultUseCaches(true);
conn.connect();
this.responseCode = conn.getResponseCode();
this.extractResponseHeaders(conn.getHeaderFields());
InputStream inputStream = null;
if (this.responseCode >= 400 ) {
inputStream = conn.getErrorStream();
} else {
inputStream = conn.getInputStream();
}
try {
if(null != inputStream){
this.response = convertToString(inputStream);
}
} catch (JSONException e) {
e.printStackTrace();
}

Android: HTTP POST response - 401

I have faced with one problem - returning 401 error msg when you already signed in and trying to get access to another secured pages. The mystic of this problem for me is that I can get access to another secured pages after you are signed in if check it via Firefox RestCLient or via iOS app but cannot get access via Chrome Advanced Rest Client and Android app. However, content-type and other necessary params are set the same in both web tools and apps. I have tried to set different auth headers with encoded login:pass but it doesnt help and it doesnt need because it should work without it, I think(at least FF and iOS app work without this header). What`s gonna be wrong?
Response headers of Chrome:
401 Unauthorized
Loading time:
29
Request headers
Content-Type: application/x-www-form-urlencoded
Response headers
Date: Mon, 04 Mar 2013 10:01:02 GMT
Server: Apache/2.2.20 (Ubuntu)
X-Powered-By: PHP/5.3.6-13ubuntu3.9
Set-Cookie: peachy=qg3mjvchjh1oionqlhhv0jrn71; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Vary: Accept-Encoding
Content-Length: 96
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=utf-8
Response headers of Firefox:
Status Code: 200 OK
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection: Keep-Alive
Content-Length: 202
Content-Type: application/json
Date: Mon, 04 Mar 2013 09:51:09 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Keep-Alive: timeout=5, max=100
Pragma: no-cache
Server: Apache/2.2.20 (Ubuntu)
X-Powered-By: PHP/5.3.6-13ubuntu3.9
That is my peace of Restful code in Android app:
public String serverRequest(int action, Bundle params) {
if (action == 0) {
if (Const.DEBUG_ENABLED)
Log.e(TAG, "You did not pass action.");
return "You did not pass action.";
}
try {
HttpRequestBase request = null;
HttpPost postRequest = null;
switch (action) {
case SIGN_IN:
request = new HttpPost();
request.setURI(new URI(SIGNIN_URL));
request.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
postRequest = (HttpPost) request;
if (params != null) {
UrlEncodedFormEntity formEntity = new
UrlEncodedFormEntity(paramsToList(params));
postRequest.setEntity(formEntity);
}
break;
case SIGN_OUT:
request = new HttpPost();
request.setURI(new URI(SIGNOUT_URL));
request.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
break;
case BANK_CARD_VERIFY:
request = new HttpPost();
request.setURI(new URI(BANK_CARD_VERIFY_URL));
request.setHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
postRequest = (HttpPost) request;
if (params != null) {
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(paramsToList(params));
postRequest.setEntity(formEntity);
}
break;
}
if (request != null) {
DefaultHttpClient client = new DefaultHttpClient();
if (Const.DEBUG_ENABLED)
Log.d(TAG, "Executing request: " + actionToString(action) + ": " + urlToString(action));
HttpResponse response = client.execute(request);
StatusLine responseStatus = response.getStatusLine();
int statusCode = responseStatus != null ? responseStatus.getStatusCode() : 0;
Log.d(TAG, "Status code: " + statusCode);
}
}
(sign in and sign out are public, bank_verify is secured page. Android app has the same response headers like chrome). It seems there is something problem with session or something else but I`m not sure exactly.
EDIT:
It seems I have found what`s the problem here. In Android app I create a new HttpCLient object due to it all old data is losed. But another question - how to make this HttpCLient reusable?
Problem found. I reuse httpClient everytime in spite of that to use only one which has all session data everytime. Just implement if statement to check if I have already httpclient object then you it otherwise create a new.

How can I get a missing cookie with android ? In Firefox yes appear

I can get a cookie with firefox, but not with android.
This is the code:
HttpParams httpparams = new BasicHttpParams();
httpparams.setParameter(ClientPNames.COOKIE_POLICY,CookiePolicy.BROWSER_COMPATIBILITY);
HttpGet httpget = new HttpGet(sURL);
httpget.setParams(httpparams);
httpget.setHeader("User-Agent","Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2");
httpget.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
httpget.setHeader("Accept-Language", "es-mx,es;q=0.8,en-us;q=0.5,en;q=0.3");
httpget.setHeader("Accept-Encoding", "gzip,deflate");
httpget.setHeader("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
httpget.setHeader("Keep-Alive", "115");
BasicHttpResponse response = (BasicHttpResponse) httpclient.execute(httpget);
Using wireshrack I saw:
GET /login.php HTTP/1.1
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: es-mx,es;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Host: www.conquerclub.com
Connection: Keep-Alive
and the response is using my code:
HTTP/1.1 200 OK
Date: Mon, 09 Aug 2010 05:41:38 GMT
Server: Apache/2.0.52 (Red Hat)
X-Powered-By: PHP/5.2.13
Set-Cookie: referer=%5Bdirect%5D; path=/
Set-Cookie: referer60=%5Bdirect%5D; expires=Wed, 08-Sep-2010 05:41:38 GMT; path=/
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html
but whith Firefox the response is
HTTP/1.1 200 OK
Date: Mon, 09 Aug 2010 05:11:13 GMT
Server: Apache/2.0.52 (Red Hat)
X-Powered-By: PHP/5.2.13
Set-Cookie: referer=%5Bdirect%5D; path=/
Set-Cookie: referer60=%5Bdirect%5D; expires=Wed, 08-Sep-2010 05:11:13 GMT; path=/
Set-Cookie: PHPSESSID=sv8f6ro571t9rv999mu6jtkbu3; path=/; HttpOnly
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Encoding: gzip
Vary: Accept-Encoding
Content-Length: 4324
Connection: close
Content-Type: text/html
again using wireshark to see firefox I got this:
GET / HTTP/1.1
Host: www.conquerclub.com
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: es-mx,es;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
It is the same, but the server don't respond this line
Set-Cookie: PHPSESSID=sv8f6ro571t9rv999mu6jtkbu3; path=/; HttpOnly
why? any idea would be very well receive.
I came across a similar problem with android. This happens because of the cookie origin policy in Browser compatibility mode. The HTTP client in android is rejecting the cookie because the cookie's path does not match the current requested document's path.
e.g. if your cookie path is: /mypath, and your cookie is originating from: /mypath/myAdditionalPath, the http client will reject the cookie (internally throwing a MalformedCookieException). But browsers will accept such cookies.
To workaround this problem in your code, you will have to implement your own cookie spec the one that extends from BrowserCompatSpec. Here's some sample skeleton code:
public class MyCookieSpec extends BrowserCompatSpec {
#Override
public void validate(Cookie cookie, CookieOrigin origin) throws
MalformedCookieException {
if(cookie == null) {
throw new IllegalArgumentException("Cookie cannot be null");
}
if(origin == null) {
throw new IllegalArgumentException("Cookie origin cannot be null");
}
// use these logs to see what is the difference between paths of the
// cookieOrigin and the cookie.
String pth = cookie.getPath();
Log.i(TAG, "Cookie ====================================> " + cookie);
Log.i(TAG, "CookieOrigin ====================================> " + origin);
// Check if the cookie is from the same domain, if so return silently
// or else throw a MalformedCookieException
}
#Override
public boolean match(Cookie cookie, CookieOrigin origin) {
if(cookie == null) {
throw new IllegalArgumentException("Cookie cannot be null");
}
if(origin == null) {
throw new IllegalArgumentException("Cookie origin cannot be null");
}
if(Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Matching cookie " + cookie + " with origin " + origin);
}
// if the cookie is originating from the same domain as of the origin
// return true or return false. Be careful here and only return true if
// the cookie is originating from the same domain as that of what is in the
// cookie's path
return true;
}
}
To register and use this cookie spec with your http client, use the following:
DefaultHttpClient httpClient = new DefaultHttpClient(cm, params);
httpClient.getCookieSpecs().register("myspec", new CookieSpecFactory() {
public CookieSpec newInstance(HttpParams hp) {
return new MyCookieSpec();
}
});
httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY, "myspec");
Sorry guys. I realized that i made a mistake
With firefox, I request www.conquerclub.com with path "/"
BUT using my code I request www.conquerclub.com with path "/login.php"
After I deleted it "login.php", the request return 3 cookies

Categories

Resources