I am trying to maintain a logged-in user session between my Android app and my Drupal website. In my research, it comes down to sending cookie(s) back to Drupal but I am struggling to implement it. How can I make a start on this?
Just in case anyone else got the same issue, I had similar problem and I was able to solve it by the following code:
1- Define CookieManager and CookieStore in your class
CookieManager cookieManager;
CookieStore cookieStore;
2- Add default cookie handler, e.g. in the class constructor or in OnCreate method
cookieManager = new CookieManager();
CookieHandler.setDefault(cookieManager);
3- Use the cookie storage when you do HTTP request
public byte[] openURI(String uri) {
try {
URI uriObj = new URI(uri);
DefaultHttpClient client = new DefaultHttpClient();
// Use the cookieStor with the request
if (cookieStore == null) {
cookieStore = client.getCookieStore();
} else {
client.setCookieStore(cookieStore);
}
HttpGet getRequest = new HttpGet(uriObj);
HttpResponse response = client.execute(getRequest);
// Read the response data
InputStream instream = response.getEntity().getContent();
int contentLength = (int) response.getEntity().getContentLength();
byte[] data = new byte[contentLength];
instream.read(data);
response.getEntity().consumeContent();
return data ;
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
I am pretty sure that if you use the HttpClient provided with the Android APIs it should do session management with cookies for you until you close the connection manually.
If I am wrong on this, then you can easily work around this by implementing your own cookie store using the CookieStore interface or the BasicCookieStore class. If all else fails, you can store cookies manually and set cookies in the header each time you make a HTTP request.
I am not sure how this might change for your particular problem though but this should most likely work considering the description of the problem you gave.
Related
I am developing an android app where user logs on to his/her account. After logging in I will receive XSRF token and Laravel Session Id to recognise the specific user. I have to send these tokens for every request I send to the API's to get the appropriate information. But when I am sending the required details as shown in the image, I am getting HTMl file as response instead of getting JSON Object. I was seriously stuck at this problem. Correct Solution may take forward the whole app.
class RegisterConnection extends AsyncTask<String,String,JSONObject> {
protected JSONObject doInBackground(String... arg0) {
JSONObject output = new JSONObject();
DefaultHttpClient client = new DefaultHttpClient();
HttpConnectionParams.setConnectionTimeout(client.getParams(), 5000); //Timeout Limit
HttpResponse response = null;
try {
HttpGet get = new HttpGet(statsURL);
get.setHeader("Accept", "application/json");
CookieStore store = new BasicCookieStore();
BasicClientCookie cookie1 = new BasicClientCookie("XSRF-TOKEN", XSRF);
BasicClientCookie cookie2 = new BasicClientCookie("laravel_session", laravel);
store.addCookie(cookie1);
store.addCookie(cookie2);
client.setCookieStore(store);
response = client.execute(get);
if(response!=null){
InputStream in = response.getEntity().getContent();
String resultstring = Utilities.convertStreamToString(in);
Log.i("Result1", resultstring);
output = new JSONObject(resultstring);
Log.i("Result2", output.toString());
}
} catch(Exception e) {
e.printStackTrace();
try {
output.put("sai","error");
Log.i("MainActivity", output.toString());
} catch (JSONException e1) {
e1.printStackTrace();
}
return output;
}
return output;
}
These are the server requirements
http://imgur.com/OY9Q673
This is the Output received
http://imgur.com/IB5AEcT
As far as I can tell, there is nothing wrong with your Android client code.
You are getting HTML from the server so the main reason could be that your Laravel server is rendering the views and sending you back html instead of JSON. Instead of rendering the views on the server, you should send JSON response on your Laravel server side.
Add Jsoup dependency in your gradle file
implementation 'org.jsoup:jsoup:1.11.2'
Document document = Jsoup.parse("http://imgur.com/IB5AEcT");
Elements el = doc.select("button");
Log.i("..........",""+el.attr("data-invite-details"));
Jsoup tutorial
http://jsoup.org/apidocs/org/jsoup/Jsoup.html
Here's the question in simplest way.
I create a HTTPS connection to my server through proxy using HttpUrlConnection Object.
My proxy closes the connection but my code still tries to reuse the same connection. And so I get EOFException.
How do I handle such cases?
I'd recommend disabling the http.keepalive system property. The performance section of the documentation indicates that socket connections will be reused when possible. It sounds like it is trying to reuse the connection, but the proxy has already closed it. On this post, Darrell also indicates that changing the system property solved his problem.
System.setProperty("http.keepAlive", "false");
Turns out they've fixed this issue in Android on Jan 8th [1]. The fix basically marks the old connection as recycled and internally retries the request.
To fix this for now, I would suggest retrying requests if an EOFException is encountered with a retry limit to prevent stackoverlows.
https://android.googlesource.com/platform/libcore/+/19aa40c81c48ff98ccc7272f2a3c41479b806376
I had this problem with normal HTTP connections as well. The first request went OK, the second failed with an EOFException.
Eventuelly I fixed it by removing...
urlConnection.setChunkedStreamingMode(0);
...from the HttpUrlConnection.
I could be that the webserver I'm calling can't handle chuncked data well. Don't know.
If you don't want to reuse the connection then release it.
finally {
urlConnection.disconnect();
}
You can use this method to pick data from server then you convert the inputs trim to string then you can parse for further use.`
public static InputStream getUrlData(final String url)
throws URISyntaxException, ClientProtocolException, IOException {
final DefaultHttpClient client = new DefaultHttpClient();
final HttpGet method = new HttpGet(new URI(url));
final HttpResponse res = client.execute(method);
return res.getEntity().getContent();
}
Maybe httpClient "has more bugs" and is deprecated, but this problem with JellyBean is a showstopper. I am using Ksoap2 so I tried all the suggested answers that I could.
System.setProperty("http.keepAlive", "false");
httpTransportSE.getServiceConnection().setRequestProperty("Connection", "close");
httpTransportSE.getServiceConnection().disconnect();
Nothing worked - my solution was to rollback the version of Ksoap2 I'm using from 3.1.1 to 2.6.5. Using 2.6.5 the problem is substantially reduced. Still testing but maybe even solved.
I found that retrying the connection fixes the issue as seen here: https://stackoverflow.com/a/20302767/2520390
Make sure you close off the connection before your recursive call.
Also, I added the following to the connection to close the connection, though I'm not sure if it helps:
if (retries > 0) {
connection.setRequestProperty("Connection", "close");
}
You shouldn't be attempting to reuse the same HttpURLConnection instance. The docs in the very bottom line of the "Class Overview" say
Each instance of HttpURLConnection may be used for one
request/response pair.
Keep-Alive connections work at a different level, see the disconnect docs:
http://developer.android.com/reference/java/net/HttpURLConnection.html#disconnect()
Unlike other Java implementations, this will not necessarily close
socket connections that can be reused. You can disable all connection
reuse by setting the http.keepAlive system property to false before
issuing any HTTP requests.
So you should always use a fresh HttpURLConnection and let the socket pool handle re-use.
There were apparently bugs with keep-alive connections pre-Froyo (2.2) so it is recommended to disable keep-alive on those old devices.
In my case the EOFException was caused by my server not sending a full response, see the details here: https://stackoverflow.com/a/27845172/2335025
You shouldn't be attempting to reuse the same HttpURLConnection instance. The docs in the very bottom line of the "Class Overview" say
Each instance of HttpURLConnection may be used for one
request/response pair.
Keep-Alive connections work at a different level, see the disconnect docs:
http://developer.android.com/reference/java/net/HttpURLConnection.html#disconnect()
Unlike other Java implementations, this will not necessarily close
socket connections that can be reused. You can disable all connection
reuse by setting the http.keepAlive system property to false before
issuing any HTTP requests.
So you should always use a fresh HttpURLConnection and let the socket pool handle re-use. There are perhaps issues if it tries to reuse a socket that has been closed by the server, which the answers to this question deal with: Android HttpUrlConnection EOFException
There were apparently bugs with keep-alive connections pre-Froyo (2.2) so it is recommended to disable keep-alive on those old devices.
In my case the EOFException was caused by my server not sending a full response, see the details here: https://stackoverflow.com/a/27845939/2335025
if (Build.VERSION.SDK != null
&& Build.VERSION.SDK_INT > 13) {
con.setRequestProperty("Connection", "close");
}
Try this code:`
Httppost method:
HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, TIMEOUT_MILLISEC);
HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT_MILLISEC);
HttpClient client = new DefaultHttpClient(httpParams);
HttpPost request = new HttpPost("put_url");
request.setHeader("Content-Type", "application/xml");
String file = resourceXml();
StringEntity se = null;
try {
se = new StringEntity(file);
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
se.setContentEncoding("UTF-8");
se.setContentType("application/xml");
request.setEntity(se);
HttpResponse response = null;
try {
response = client.execute(request);
} catch (ClientProtocolException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
HttpEntity entity = response.getEntity();
InputStream is = null;
try {
is = entity.getContent();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
String _response = convertStreamToString(is);
Log.i(TAG, "Response:" + _response);
// Check if server response is valid code
int res_code = response.getStatusLine().getStatusCode();
Log.i(TAG, "status_code" + res_code);
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
`
to convert stream to string:`
private static String convertStreamToString(InputStream is) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is),
8192);
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append((line + "\n"));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}`
I'm trying to simulate a process like "cache validation" in my application.
I will download a new version of my webapplication on the device (android-based), but I only want to download a new version of the files, based in a etag comparision.
Does anyone a example of how use the Etag mechanism in Android?
You can access the ETag field from a HttpURLConnection object such as like this:
HttpURLConnection conn = (HttpURLConnection)new URL(url).openConnection();
String etag = conn.getHeaderField("ETag");
Of course, you will need to make sure that the server you are testing this against supports ETags.
Maybe class "HttpRequest" from this library (kevinsawicki) will help you.
For example:
File latest = new File("/data/cache.json");
HttpRequest request = HttpRequest.get("http://google.com");
//Copy response to file
request.body(latest);
//Store eTag of response
String eTag = request.eTag();
//Later you can check if changes exist
boolean unchanged = HttpRequest.get("http://google.com")
.ifNoneMatch(eTag)
.notModified();
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
try {
HttpResponse response = httpClient.execute(httpPost);
Log.d("Http Response:", response.getFirstHeader("etag").toString());
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
You can refer to the specific implement of ShallowEtagHeaderFilter performing etag generation and validation in Spring.
In my android app i want to maintain the session id for at least 30 days even if the app quits. I am using DefaultHttpClient. When i send first request to the server it returns session id, i have to use this returned session id for all other request.
You can set a Shared Preference to maintain your session ID. You can use a second pref to store the date of the last connection so you are able to reset the session ID after 30 days of inactivity.
Right U can save your Session ID in SharedPref, However there are other value that need to be saved , if your cookies expires u can relogin again with same credential again , other wise u can use persistence cookies (becoz user can kill the apps anytime ), below code snippets is only for till user used app (user does kill the app process )
private static CookieStore cookieStore = new BasicCookieStore();
InputStream is = null;
try {
final DefaultHttpClient httpClient = new DefaultHttpClient();
httpClient.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true);
Log.d("vipin", url.toString());
HttpPost httpPost = new HttpPost(url);
/** maintain session if logged in */
if (SouqApplication.getBooleanValue(Constants.IS_LOGGED_IN))
httpPost.setHeader(Constants.PHPSESSID, SouqApplication.getStringValue(Constants.SESSION_ID));
//httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");
HttpContext localContext = new BasicHttpContext();
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
httpClient.setCookieStore(cookieStore);
HttpResponse httpResponse = httpClient.execute(httpPost, localContext);
setCookies(httpClient.getCookieStore().getCookies());
HttpEntity httpEntity = httpResponse.getEntity();
String errorHandlling = EntityUtils.toString(httpEntity);
return errorHandlling ;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
} catch (ClientProtocolException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
And for a Better approach U can use a ready cake popular library
Persistent Cookie Storage with PersistentCookieStore
http://loopj.com/android-async-http/
I'm building an android app which should perform a GET on my site to get two cookies and then perform a post to the same site with these cookies.
As mentioned I start of with the GET and I'm using org.apache.http.client.HttpClient to perform this operation.
String requiredCookies = "";
HttpContext localContext = null;
System.out.println("------------------GET----------------------");
HttpClient httpClient = new DefaultHttpClient();
HttpGet get = new HttpGet("www.mysitegeturl.com");
//Creating a local instance of cookie store.
CookieStore cookieJar = new BasicCookieStore();
// Creating a local HTTP context
localContext = new BasicHttpContext();
// Bind custom cookie store to the local context
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieJar);
HttpResponse response;
try {
response = httpClient.execute(get, localContext);
HttpEntity entity = response.getEntity();
System.out.println(response.getStatusLine());
if (entity != null) {
System.out.println("Response content length: " + entity.getContentLength());
}
//Do this so that Java.net impl should work
List<Cookie> cookies = cookieJar.getCookies();
for (int i = 0; i < cookies.size(); i++) {
requiredCookies += cookies.get(i).getName()+"="+cookies.get(i).getValue()+";";
}
if (entity != null) {
entity.consumeContent();
}
} catch (ClientProtocolException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.println("------------------GET-END---------------------");
So far so good. Don't mind the requiredCookies line yet, it will be used in the Java.net impl since I can't get the HttpClient one to work =(.
Let's take a look at the non working HttpClient Post part.
System.out.println("------------------HttpClient - POST----------------------");
HttpPost post = new HttpPost("www.mysiteposturl.com");
//Params
HttpParams params = new BasicHttpParams();
params.setParameter("foo", "post");
params.setParameter("bar", "90");
params.setParameter("action", "search");
post.setParams(params);
post.setHeader("Content-Type", "application/x-www-form-urlencoded");
try {
HttpResponse response2 = httpClient.execute(post, localContext);
System.out.println(response2.getStatusLine());
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("------------------POST END---------------------");
What happens now is that I perform a POST with the localContext where the cookies are stored. This doesn't work. I get a HTTP/1.1 401 No session. Since I had no luck with this I tried another approach(java.net.HttpURLConnection). Remember I still use the same GET part
URL url = new URL("www.mysiteposturl");
HttpURLConnection connection = null;
String dataString = "bar=90&foo=post&action=search";
try {
connection = (HttpURLConnection)url.openConnection();
connection.setRequestProperty("Cookie", requiredCookies);
//Set to POST
connection.setDoOutput(true);
Writer writer = new OutputStreamWriter(connection.getOutputStream());
writer.write(dataString);
writer.flush();
writer.close();
connection.connect();
if (connection.getResponseCode() == 200 || connection.getResponseCode() == 201) {
System.out.println(connection.getContent().toString());
} else {
System.out.println("Error");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("------------------POST END---------------------");
And VIOLA a 200 is displayed and everything works like a charm. What do you guys think? Could someone please provide me with an answer because I can't figure it out.
The problem appears to be that you have two different host names in the setup. This will cause HTTP Client to not send cookies for a different host. You could try changing the domain of the cookies in the cookie store, or using the same host for GET and POST. Additionally you could manually add the cookies to the headers in HTTP Client as you did in the HttpURLConnection example.
I guess it was a mistake that you used two completely different domains for your two requests — i.e. you were trying to mask your real URL? If not, then that's why you're not getting any cookies. If were just trying to mask your URL, well that's why example.com exists. :)
Alternatively, and this is completely off the top of my head from code I wrote last week — it worked fine across multiple GETs, POSTs and subdomains:
DefaultHttpClient httpClient = new DefaultHttpClient();
CookieStore cookieJar = new BasicCookieStore();
httpClient.setCookieStore(cookieJar);
i.e. I'm explicitly using a DefaultHttpClient, which I believe has those extra get/setters for the cookie store. I don't think I used any context objects either.