I have a server that uses spring security remember-me-authentication to which I login using Android DefaultHttpClient in a POST method.
I am able to successfully login and even able to retrieve the session cookies created (In my case a jsessionid cookie and a spring security remember me cookie).
But the weird thing is after executing the POST method like
mResponse = mDefaultHttpClient.execute(mHttpPost)
I am able to retrieve the cookies, but only using getCookieStore method in my DefaultHttpClient like
mDefaultHttpClient.getCookieStore()
not using getAllHeaders method of the HttpResponse object
headers = mResponse.getAllHeaders();
HeaderIterator headerIterrator = new BasicHeaderIterator(headers, null);
while (headerIterrator.hasNext()) {
Header header = headerIterrator.nextHeader();
headerStringBuilder.append(" " + header.getName() + ":" + header.getValue());
}
Log.e("Post response headers: ", headerStringBuilder.toString());
I get some headers back in here( Server, X-powered-By, Date, Content-Type, Content-Length) but not the Set-Cookie header or a few others( Access-control-Allow-* etc)
Thanks for any help!
UPDATE: Looks like the DefaultHttpClient does not expose some of the headers. I tried adding response interceptor (like shown below) and getAllHeaders returned all the headers I wanted. Thanks for reading my question!
mDefaultHttpClient.addResponseInterceptor(new HttpResponseInterceptor() {
public void process(final HttpResponse response, final HttpContext context)
throws HttpException, IOException {
Header[] headers = response.getAllHeaders();
for (int i = 0; i < headers.length; i++) {
Header header = headers[i];
Log.e("HTTP: ", "name: " + header.getName());
Log.e("HTTP: ", "value: " + header.getValue());
}
}
});
Did you try using HttpURLConnection rather than HttpClient? HttpClient, as the name says, is a client, then it manages cookies for you, HttpURLConnection does not.
Doc says "HTTP clients encapsulate a smorgasbord of objects required to execute HTTP requests while handling cookies, authentication, connection management, and other features."
if you set cookie in server side , like session_start() in php , the Set-Cookie have show in the headers , but if you have don't set any cookie , the Set-Cookie have not shown.
after:
05-25 17:23:16.616: I/HTTP:(575): name: Set-Cookie
05-25 17:23:16.616: I/HTTP:(575): value: PHPSESSID=g29i01av8ddvpgsi7lpaj12lc3; path=/
Related
I am working on a project which was working fine before Christmas but suddenly doesn't without any changes being made.
The project involves a C++ which listens on a particular port and listens to post requests in a REST API to process the data and store in a database.
It consists of an Android library which gathers information and then sends this as an HTTP POST to the rest API on the C++ app.
The C++ app prints out the HTTP response that was received straight from receiving it on the socket before any processing done. First the android app has to send an initalisation request to the C++ app, the C++ prints the request and shows post data was sent, and successfully initalises and sends a response back to android including a session cookie. I then re-use the HttpClient within Android to post the next request which contains a fair amount of data but this request doesn't work.
When stepping through the android library I can see the post values have been successfully set and are being used to perform the HTTP request, however, the C++ app only receives the HTTP headers, not any post data.
If in the second request, I replace the post data with only a couple of post fields, the C++ then sees the post data, so it looks like the DefaultHTTPClient in Android isn't sending the post data if the post data is quite large.
Below is how I am posting the data in Android
if (httpClient == null)
{
//AndroidHttpClient client = new AndroidHttpClient();
httpClient = new DefaultHttpClient();
}
HttpParams httpParams = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(httpParams, 3000);
HttpConnectionParams.setSoTimeout(httpParams, 3000);
HttpPost httpPost = new HttpPost(serverURL);
//httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");
httpPost.setHeader("Authorisation-Token", authorisationToken);
httpPost.setHeader("Connection", "close");
//httpPost.setHeader("Cookie", "SESSIONID=zd8d5n3kucysl4idug1911m7ye");
httpPost.setEntity(new UrlEncodedFormEntity(postData, "UTF-8"));
String headers = "";
responseHandler = new ResponseHandler<String>()
{
#Override
public String handleResponse(HttpResponse httpResponse) throws ClientProtocolException, IOException
{
return EntityUtils.toString(httpResponse.getEntity());
}
};
responseBody = httpClient.execute(httpPost, responseHandler);
Log.d("Response", responseBody);
//httpClient = null;
if (responseBody != null && !responseBody.isEmpty())
{
httpClient.getConnectionManager().closeIdleConnections(0, TimeUnit.NANOSECONDS);
JSONObject jsonObject = new JSONObject(responseBody);
return jsonObject;
}
else
{
httpClient.getConnectionManager().closeIdleConnections(0, TimeUnit.NANOSECONDS);
return null;
}
As an example the post data is being created as follows:
postData = new ArrayList<>();
postData.add(new BasicNameValuePair("Test", "Item 1"));
postData.add(new BasicNameValuePair("Test 2", "Item 2"));
and the above is passed in to the execute command of the ASyncTask.
Below is how I am receiving the data on the socket from C++
string LinuxSocket::receiveDataOnSocket(int *socket)
{
string receiveData = "";
char * temp = NULL;
int bytesReceived = 0;
do
{
bytesReceived = recv(*socket, this->buffer, this->bufferLength, 0);
if (bytesReceived < 0)
{
stringstream logstream;
logstream << "Failed to read data on socket. Error: " << strerror(bytesReceived);
this->bitsLibrary->writeToLog(logstream.str(), "LinuxSocket", "receiveDataOnSocket");
this->closeSocket(socket);
throw SocketException(strerror(bytesReceived));
}
//If we got here then we should be able to get some data
temp = new char[bytesReceived + 1];
strncpy(temp, this->buffer, bytesReceived);
temp[bytesReceived] = '\0';
receiveData.append(temp);
delete[] temp;
temp = NULL;
memset(this->buffer, 0, this->bufferLength);
} while (bytesReceived == this->bufferLength);
return receiveData;
}
The post data that I am sending is a follows:
POST /crash HTTP/1.1
User-Agent: My Android User Agent
Authorisation-Token: DlzSIkx4ro*OatHCV6epfWY0F
Connection: close
Content-Length: 1231
Content-Type: application/x-www-form-urlencoded
Host: 192.168.1.123:500
Cookie: SESSIONID=6rc5q1db0z8lupe5uij5ten3mw
Cookie2: $Version=1
Severity=Critical&DeviceID=36e85611db16c7fa&VersionName=1.5&DeviceType=Android&ROMBuild=sdk_gphone_x86-userdebug+8.0.0+OSR1.170901.056+4497355+dev-keys&KernelVersion=3.18.81%2B&DeviceBrand=google&DeviceModel=Android+SDK+built+for+x86&APILevel=26&ScreenResolution=1080+x+1776&Locale=English&MobileNetwork=Android&CrashType=Handled&ExceptionType=java.lang.Exception&Stacktrace=java.lang.Exception%3A+Standard+Exception+been+thrown%0A%09at+com.MyCompany.MyApp.MainActivity%242.onClick%28MainActivity.java%3A67%29%0A%09at+android.view.View.performClick%28View.java%3A6256%29%0A%09at+android.view.View%24PerformClick.run%28View.java%3A24701%29%0A%09at+android.os.Handler.handleCallback%28Handler.java%3A789%29%0A%09at+android.os.Handler.dispatchMessage%28Handler.java%3A98%29%0A%09at+android.os.Looper.loop%28Looper.java%3A164%29%0A%09at+android.app.ActivityThread.main%28ActivityThread.java%3A6541%29%0A%09at+java.lang.reflect.Method.invoke%28Native+Method%29%0A%09at+com.android.internal.os.Zygote%24MethodAndArgsCaller.run%28Zygote.java%3A240%29%0A%09at+com.android.internal.os.ZygoteInit.main%28ZygoteInit.java%3A767%29%0A&CustomProperty=%7B%22Test+Non+Json+Property%22%3A%22Here+is+my+value%22%7D&AppID=15869700
As an example the post data that successfully works is a follows:
POST /initialise HTTP/1.1
Authorisation-Token: DlzSIkx4ro*OatHCV6epfWY0F
Connection: close
Content-Length: 48
Content-Type: application/x-www-form-urlencoded
Host: 192.168.1.123:500
User-Agent: My Android User Agent
ApplicationID=15869700&DeviceID=36e85611db16c7fa
I've also captured the request from Android and sent it from a Rest API Test Client Insomnia.Rest and sent this to the Rest API and the C++ successfully sees all of the post data, so it looks like the problem is the Android library won't send post data if its of a certain size.
Is this the case, and how can I get round this?
I found the issue with this, there were two separate things that I've done to resolve this.
The issue with no post data at all being sent, I changed from the org.apache.DefaultHTTPClient to the OkHTTPClient. From looking at Google it looks like the apache version has been deprecated and the OkHTTPClient is the preferred HTTP client anyway. This then sent some post data but not everything. This brings me to the second fix (I don't think the issue was actually with the apache client, I think it was my C++ receiving data on the socket).
The second fix I did was to change my receiveDataOnSocket function in the C++ app.
When I've worked on socket to socket communication then normally if the buffer is full, then usually I can expect to receive more data, if the buffer is only partly full, then all data is sent and I return the received data.
It looks like HTTP doesn't necessarily send as much as the buffer on receiving socket can handle, I've therefore changed my receive data on socket to process the data as its being received to look for the Content-Length header, and the length of the body that I may have, then on each subsequent recv call, I increment the current body count by the receivedBytes count and if the body length is equal to the content length I stop receiving data on the socket and return the HTTP request.
The php files on my server are password protect with htaccess. How can I make request to these files through my android app?
I couldnt find any relevant answers with google search.
Here you can find your answer:
Basic HTTP Authentication on Android
Basically:
HttpUriRequest request = new HttpGet(YOUR_URL); // Or HttpPost(), depends on your needs
String credentials = YOUR_USERNAME + ":" + YOUR_PASSWORD;
String base64EncodedCredentials = Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
request.addHeader("Authorization", "Basic " + base64EncodedCredentials);
HttpClient httpclient = new DefaultHttpClient();
httpclient.execute(request);
// You'll need to handle the exceptions thrown by execute()
You can replace the last line with:
EDITED:
You can try someting like this:
try {
HttpResponse response = httpclient.execute(request);
//this is the login response, logged so you can see it - just use the second part of the log for anything you want to do with the data
Log.d("Login: Response", EntityUtils.toString(response.getEntity()));
} catch (IOException e) {
//if something went badly wrong
}
So you can view your response, maybe the problem is parsing the JSON.
Assuming your PHP files are protected by HTTP Basic Authentication, you can request your php files using this special URL syntax:
http://validuser:validpassword#www.domain.com/validfile.php
i am sending data from an android device to my cakephp website through HTTP Post in json object ..like this
HttpPost post = new HttpPost("https://www.mywebtest.com/test");
and then i decode the json and extract the data like this
if ($this->request->isPost()){
$json = $this->request->data('json');
$data = json_decode($json, TRUE);
but at the moment i am not checking that whether the http post or data is coming from my android app or someone else .so if someone knows the url he can do something malicious ..because at times now the url which i have written in httpPost when i typed this url in my browser there is nothing is shown on the browser.. i want to display some kind of error or 404 page if some one typed the url in browser.. Essentially, you can say i am creating with CakePHP is an API, What i want to do is secure the API so only my app can execute requests.so i want to implement some type of authentication .. doing some research i come up to a solution that pass another parameter as a secret token from android app and then check token from my webapp and then extract data .. i dont know how can i pass another parameter in httppost and then check it on my webpage .. and also if that possible i want to randomly generate the token and encrypt it on every request and then decrypt it through key in webapp whenever the data is posting from android to webapp.. if someone has done before this or have an idea then please share code or any link.
Create a JsonObject for parameters,
JSONObject parametersList = new JSONObject();
put your parameters into that JSONObject
parametersList.put("testID", 123);
int androidValue = 231231231; // a identifer or password your choice.
parametersList.put("androidValue", androidValue);
and add list to your HttpPost
HttpPost request = new HttpPost("https://www.mywebtest.com/test");
request.setHeader(HTTP.CONTENT_TYPE, "application/json; charset=utf-8");
request.setEntity(new StringEntity(parameters.toString(), HTTP.UTF_8));
On the php side;
<?php
$postdata = $HTTP_RAW_POST_DATA;
$data = json_decode($postdata);
$id = $data->testID;
$androidValue = $data->androidValue;
if(androidValue == 231231231)
{
$json = "{d:[{sample1:'".$id."',sample2:'value12'}]}";
$response = $_GET["callback"] . $json;
echo $response;
}
else
{
// send failed data
}
?>
And this how to get respond on android side
HttpResponse response = client.execute(request);
final int statusCode = response.getStatusLine().getStatusCode();
final String jsonResponse = EntityUtils.toString(response.getEntity());
if (statusCode != HttpStatus.SC_OK) {
Log.w(TAG, "Error in web request: " + statusCode);
Log.w(TAG, jsonResponse);
return null;
} else {
Log.i(TAG, jsonResponse);
return jsonResponse;
}
I hope this will help.
Try with RequestHandler isMobile().
You will get true, if request is from mobile phone.
http://book.cakephp.org/2.0/en/core-libraries/components/request-handling.html#RequestHandlerComponent::isMobile
In my android application I am trying to cache the response of Http Client. I am testing this task using facebook graph api and have the following url: https://graph.facebook.com/riz.ahmed.52
For the first time I get the "first_name" and display it. Then I change the First Name of my facebook profile and call the same link again. I am expecting to get the old/cached "first_name" but I get the updated one. The console always shows the "The response came from an upstream server" message when I call the url.
My code for Http Client is as follows:
CacheConfig cacheConfig = new CacheConfig();
cacheConfig.setMaxCacheEntries(1000);
cacheConfig.setMaxObjectSizeBytes(8192);
//HttpClient httpclient = new CachingHttpClient(new DefaultHttpClient(), cacheConfig);
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
// Updated code [START]
httpclient.addResponseInterceptor(new HttpResponseInterceptor() {
public void process(
final HttpResponse response,
final HttpContext context) throws HttpException, IOException {
response.removeHeader(response.getFirstHeader("Pragma"));
response.removeHeader(response.getFirstHeader("Expires"));
}
});
// Updated code [END]
HttpGet httpget = new HttpGet(url);
// Execute HTTP Get Request
HttpResponse response = httpclient.execute(httpget, localContext);
HttpEntity entity = response.getEntity();
String res = EntityUtils.getContentCharSet(entity);
CacheResponseStatus responseStatus = (CacheResponseStatus) localContext.getAttribute(
CachingHttpClient.CACHE_RESPONSE_STATUS);
switch (responseStatus) {
case CACHE_HIT:
System.out.println("A response was generated from the cache with no requests " +
"sent upstream");
break;
case CACHE_MODULE_RESPONSE:
System.out.println("The response was generated directly by the caching module");
break;
case CACHE_MISS:
System.out.println("The response came from an upstream server");
break;
case VALIDATED:
System.out.println("The response was generated from the cache after validating " +
"the entry with the origin server");
break;
}
I am using Android 2.3.3. Please let me know what I am missing here
The page you are loading specifies a Expires:Sat, 01 Jan 2000 00:00:00 GMT header, i.e. it's always considered stale and must always be re-fetched.
Edit:
Also returns a Pragma: no-cache apparently. Basically, it's telling your HTTP client to never cache this page. You may be able to remove these headers with a HttpResponseInterceptor if you're dead-set on caching the response.
#2 Edit:
Using http-clientcache-4.2.jar is going to be problematic as it is not completely compatible with the version of the HTTP client packaged with the Android SDK - you're going to get NoClassDefFoundErrors and similar nonsense when using it.
However - if you "build-your-own" by downloading the source for clientcache-4.2 and strip out any unfulfilled references (such as refactoring the package name of the commons logging) & killing of all the annotations sprinkled throughout the code (etc.) you can probably get a working version. If you do, this worked:
class MakeCacheable implements HttpResponseInterceptor {
public static MakeCacheable INSTANCE = new MakeCacheable();
public void process(HttpResponse resp, HttpContext ctx) throws HttpException, IOException {
resp.removeHeaders("Expires");
resp.removeHeaders("Pragma");
resp.removeHeaders("Cache-Control");
}
}
Injected into the DefaultHttpClient used by the CachingHttpClient like so:
DefaultHttpClient realClient = new DefaultHttpClient();
realClient.addResponseInterceptor(MakeCacheable.INSTANCE, 0); // This goes first
CachingHttpClient httpClient = new CachingHttpClient(realClient, cacheConfig);
If an entry is cached or not is decided by the ResponseCachingPolicy which unfortunately is a final in the CachingHttpClient, but looking through it will show all the headers that need to go to make an un-cacheable entry cacheable.
I am trying to invoke a private web-service in which there's one link I've to access using GET method. While using the direct URL on browser (needs to login first), I get the data in JSON format. The URL I am invoking is like this
http://www.example.com/trip/details/860720?format=json
The url is working fine, but when I invoke this using HttpGet, I am getting the HTML coding of the webpage, instead of the JSON String. The code I am using is as follows:
private String runURL(String src,int id) { //src="http://www.example.com/trip/details/"
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet(src);
String responseBody="";
BasicHttpParams params=new BasicHttpParams();
params.setParameter("domain", token); //The access token I am getting after the Login
params.setParameter("format", "json");
params.setParameter("id", id);
try {
httpget.setParams(params);
HttpResponse response = httpclient.execute(httpget);
responseBody = EntityUtils.toString(response.getEntity());
Log.d("runURL", "response " + responseBody); //prints the complete HTML code of the web-page
} catch (Exception e) {
e.printStackTrace();
}
return responseBody;
}
Can you tell me what am I doing wrong here??
Try specify Accept & Content-Type in you http header:
httpget.setHeader("Accept", "application/json"); // or application/jsonrequest
httpget.setHeader("Content-Type", "application/json");
Note that you can use tools like wireshark capture and analyse the income and outcome http package, and figure out the exact style of the http header that returns your json response from a standard browser.
Update:
You mentioned need login first when using browser, the html content returned is probably the login page (if use basic authentication type, it returns a short html response with status code 401, so a modern browser knows how to handle, more specifically, pop up login prompt to user), so the first try would be checking the status code of your http response:
int responseStatusCode = response.getStatusLine().getStatusCode();
Depend on what kind of authentication type you use, you probably need specify login credentials in your http request as well, something like this (if it is a basic authentication):
httpClient.getCredentialsProvider().setCredentials(
new AuthScope("http://www.example.com/trip/details/860720?format=json", 80),
new UsernamePasswordCredentials("username", "password");