Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A persistent CookieStore implementation for use in Android with HTTPUrlConnection or OkHttp 2. -- For a OkHttp 3 persistent CookieJar implementation you can use this library: https://github.com/franmontiel/PersistentCookieJar
/*
* Copyright (c) 2015 Fran Montiel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class PersistentCookieStore implements CookieStore {
private static final String TAG = PersistentCookieStore.class
.getSimpleName();
// Persistence
private static final String SP_COOKIE_STORE = "cookieStore";
private static final String SP_KEY_DELIMITER = "|"; // Unusual char in URL
private static final String SP_KEY_DELIMITER_REGEX = "\\"
+ SP_KEY_DELIMITER;
private SharedPreferences sharedPreferences;
// In memory
private Map<URI, Set<HttpCookie>> allCookies;
public PersistentCookieStore(Context context) {
sharedPreferences = context.getSharedPreferences(SP_COOKIE_STORE,
Context.MODE_PRIVATE);
loadAllFromPersistence();
}
private void loadAllFromPersistence() {
allCookies = new HashMap<URI, Set<HttpCookie>>();
Map<String, ?> allPairs = sharedPreferences.getAll();
for (Entry<String, ?> entry : allPairs.entrySet()) {
String[] uriAndName = entry.getKey().split(SP_KEY_DELIMITER_REGEX,
2);
try {
URI uri = new URI(uriAndName[0]);
String encodedCookie = (String) entry.getValue();
HttpCookie cookie = new SerializableHttpCookie()
.decode(encodedCookie);
Set<HttpCookie> targetCookies = allCookies.get(uri);
if (targetCookies == null) {
targetCookies = new HashSet<HttpCookie>();
allCookies.put(uri, targetCookies);
}
// Repeated cookies cannot exist in persistence
// targetCookies.remove(cookie)
targetCookies.add(cookie);
} catch (URISyntaxException e) {
Log.w(TAG, e);
}
}
}
@Override
public synchronized void add(URI uri, HttpCookie cookie) {
uri = cookieUri(uri, cookie);
Set<HttpCookie> targetCookies = allCookies.get(uri);
if (targetCookies == null) {
targetCookies = new HashSet<HttpCookie>();
allCookies.put(uri, targetCookies);
}
targetCookies.remove(cookie);
targetCookies.add(cookie);
saveToPersistence(uri, cookie);
}
/**
* Get the real URI from the cookie "domain" and "path" attributes, if they
* are not set then uses the URI provided (coming from the response)
*
* @param uri
* @param cookie
* @return
*/
private static URI cookieUri(URI uri, HttpCookie cookie) {
URI cookieUri = uri;
if (cookie.getDomain() != null) {
// Remove the starting dot character of the domain, if exists (e.g: .domain.com -> domain.com)
String domain = cookie.getDomain();
if (domain.charAt(0) == '.') {
domain = domain.substring(1);
}
try {
cookieUri = new URI(uri.getScheme() == null ? "http"
: uri.getScheme(), domain,
cookie.getPath() == null ? "/" : cookie.getPath(), null);
} catch (URISyntaxException e) {
Log.w(TAG, e);
}
}
return cookieUri;
}
private void saveToPersistence(URI uri, HttpCookie cookie) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(uri.toString() + SP_KEY_DELIMITER + cookie.getName(),
new SerializableHttpCookie().encode(cookie));
editor.apply();
}
@Override
public synchronized List<HttpCookie> get(URI uri) {
return getValidCookies(uri);
}
@Override
public synchronized List<HttpCookie> getCookies() {
List<HttpCookie> allValidCookies = new ArrayList<HttpCookie>();
for (URI storedUri : allCookies.keySet()) {
allValidCookies.addAll(getValidCookies(storedUri));
}
return allValidCookies;
}
private List<HttpCookie> getValidCookies(URI uri) {
List<HttpCookie> targetCookies = new ArrayList<HttpCookie>();
// If the stored URI does not have a path then it must match any URI in
// the same domain
for (URI storedUri : allCookies.keySet()) {
// Check ith the domains match according to RFC 6265
if (checkDomainsMatch(storedUri.getHost(), uri.getHost())) {
// Check if the paths match according to RFC 6265
if (checkPathsMatch(storedUri.getPath(), uri.getPath())) {
targetCookies.addAll(allCookies.get(storedUri));
}
}
}
// Check it there are expired cookies and remove them
if (!targetCookies.isEmpty()) {
List<HttpCookie> cookiesToRemoveFromPersistence = new ArrayList<HttpCookie>();
for (Iterator<HttpCookie> it = targetCookies.iterator(); it
.hasNext(); ) {
HttpCookie currentCookie = it.next();
if (currentCookie.hasExpired()) {
cookiesToRemoveFromPersistence.add(currentCookie);
it.remove();
}
}
if (!cookiesToRemoveFromPersistence.isEmpty()) {
removeFromPersistence(uri, cookiesToRemoveFromPersistence);
}
}
return targetCookies;
}
/* http://tools.ietf.org/html/rfc6265#section-5.1.3
A string domain-matches a given domain string if at least one of the
following conditions hold:
o The domain string and the string are identical. (Note that both
the domain string and the string will have been canonicalized to
lower case at this point.)
o All of the following conditions hold:
* The domain string is a suffix of the string.
* The last character of the string that is not included in the
domain string is a %x2E (".") character.
* The string is a host name (i.e., not an IP address). */
private boolean checkDomainsMatch(String cookieHost, String requestHost) {
return requestHost.equals(cookieHost) || requestHost.endsWith("." + cookieHost);
}
/* http://tools.ietf.org/html/rfc6265#section-5.1.4
A request-path path-matches a given cookie-path if at least one of
the following conditions holds:
o The cookie-path and the request-path are identical.
o The cookie-path is a prefix of the request-path, and the last
character of the cookie-path is %x2F ("/").
o The cookie-path is a prefix of the request-path, and the first
character of the request-path that is not included in the cookie-
path is a %x2F ("/") character. */
private boolean checkPathsMatch(String cookiePath, String requestPath) {
return requestPath.equals(cookiePath) ||
(requestPath.startsWith(cookiePath) && cookiePath.charAt(cookiePath.length() - 1) == '/') ||
(requestPath.startsWith(cookiePath) && requestPath.substring(cookiePath.length()).charAt(0) == '/');
}
private void removeFromPersistence(URI uri, List<HttpCookie> cookiesToRemove) {
SharedPreferences.Editor editor = sharedPreferences.edit();
for (HttpCookie cookieToRemove : cookiesToRemove) {
editor.remove(uri.toString() + SP_KEY_DELIMITER
+ cookieToRemove.getName());
}
editor.apply();
}
@Override
public synchronized List<URI> getURIs() {
return new ArrayList<URI>(allCookies.keySet());
}
@Override
public synchronized boolean remove(URI uri, HttpCookie cookie) {
Set<HttpCookie> targetCookies = allCookies.get(uri);
boolean cookieRemoved = targetCookies != null && targetCookies
.remove(cookie);
if (cookieRemoved) {
removeFromPersistence(uri, cookie);
}
return cookieRemoved;
}
private void removeFromPersistence(URI uri, HttpCookie cookieToRemove) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.remove(uri.toString() + SP_KEY_DELIMITER
+ cookieToRemove.getName());
editor.apply();
}
@Override
public synchronized boolean removeAll() {
allCookies.clear();
removeAllFromPersistence();
return true;
}
private void removeAllFromPersistence() {
sharedPreferences.edit().clear().apply();
}
}
/*
* Copyright (c) 2011 James Smith <james@loopj.com>
* Copyright (c) 2015 Fran Montiel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Based on the code from this stackoverflow answer http://stackoverflow.com/a/25462286/980387 by janoliver
* Modifications in the structure of the class and addition of serialization of httpOnly attribute
*/
public class SerializableHttpCookie implements Serializable {
private static final String TAG = SerializableHttpCookie.class
.getSimpleName();
private static final long serialVersionUID = 6374381323722046732L;
private transient HttpCookie cookie;
// Workaround httpOnly: The httpOnly attribute is not accessible so when we
// serialize and deserialize the cookie it not preserve the same value. We
// need to access it using reflection
private Field fieldHttpOnly;
public SerializableHttpCookie() {
}
public String encode(HttpCookie cookie) {
this.cookie = cookie;
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ObjectOutputStream outputStream = new ObjectOutputStream(os);
outputStream.writeObject(this);
} catch (IOException e) {
Log.d(TAG, "IOException in encodeCookie", e);
return null;
}
return byteArrayToHexString(os.toByteArray());
}
public HttpCookie decode(String encodedCookie) {
byte[] bytes = hexStringToByteArray(encodedCookie);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
bytes);
HttpCookie cookie = null;
try {
ObjectInputStream objectInputStream = new ObjectInputStream(
byteArrayInputStream);
cookie = ((SerializableHttpCookie) objectInputStream.readObject()).cookie;
} catch (IOException e) {
Log.d(TAG, "IOException in decodeCookie", e);
} catch (ClassNotFoundException e) {
Log.d(TAG, "ClassNotFoundException in decodeCookie", e);
}
return cookie;
}
// Workaround httpOnly (getter)
private boolean getHttpOnly() {
try {
initFieldHttpOnly();
return (boolean) fieldHttpOnly.get(cookie);
} catch (Exception e) {
// NoSuchFieldException || IllegalAccessException ||
// IllegalArgumentException
Log.w(TAG, e);
}
return false;
}
// Workaround httpOnly (setter)
private void setHttpOnly(boolean httpOnly) {
try {
initFieldHttpOnly();
fieldHttpOnly.set(cookie, httpOnly);
} catch (Exception e) {
// NoSuchFieldException || IllegalAccessException ||
// IllegalArgumentException
Log.w(TAG, e);
}
}
private void initFieldHttpOnly() throws NoSuchFieldException {
fieldHttpOnly = cookie.getClass().getDeclaredField("httpOnly");
fieldHttpOnly.setAccessible(true);
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeObject(cookie.getName());
out.writeObject(cookie.getValue());
out.writeObject(cookie.getComment());
out.writeObject(cookie.getCommentURL());
out.writeObject(cookie.getDomain());
out.writeLong(cookie.getMaxAge());
out.writeObject(cookie.getPath());
out.writeObject(cookie.getPortlist());
out.writeInt(cookie.getVersion());
out.writeBoolean(cookie.getSecure());
out.writeBoolean(cookie.getDiscard());
out.writeBoolean(getHttpOnly());
}
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
String name = (String) in.readObject();
String value = (String) in.readObject();
cookie = new HttpCookie(name, value);
cookie.setComment((String) in.readObject());
cookie.setCommentURL((String) in.readObject());
cookie.setDomain((String) in.readObject());
cookie.setMaxAge(in.readLong());
cookie.setPath((String) in.readObject());
cookie.setPortlist((String) in.readObject());
cookie.setVersion(in.readInt());
cookie.setSecure(in.readBoolean());
cookie.setDiscard(in.readBoolean());
setHttpOnly(in.readBoolean());
}
/**
* Using some super basic byte array &lt;-&gt; hex conversions so we don't
* have to rely on any large Base64 libraries. Can be overridden if you
* like!
*
* @param bytes byte array to be converted
* @return string containing hex values
*/
private String byteArrayToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte element : bytes) {
int v = element & 0xff;
if (v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString();
}
/**
* Converts hex values from strings to byte array
*
* @param hexString string of hex-encoded values
* @return decoded byte array
*/
private byte[] hexStringToByteArray(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
.digit(hexString.charAt(i + 1), 16));
}
return data;
}
}
@imbeyondboredom

This comment has been minimized.

Show comment
Hide comment
@imbeyondboredom

imbeyondboredom Dec 3, 2014

Just a comment because I stumbled across this code. On line https://gist.github.com/franmontiel/ed12a2295566b7076161#file-persistentcookiestore-java-L133 the code checks if ((storedUri.getPath() == null || storedUri.getPath().equals("/")) && storedUri.getHost().equals(uri.getHost()))

That basically means that if the path is not null and doesn't equal "/" then it will never match. This should be fixed before you use this code.

Just a comment because I stumbled across this code. On line https://gist.github.com/franmontiel/ed12a2295566b7076161#file-persistentcookiestore-java-L133 the code checks if ((storedUri.getPath() == null || storedUri.getPath().equals("/")) && storedUri.getHost().equals(uri.getHost()))

That basically means that if the path is not null and doesn't equal "/" then it will never match. This should be fixed before you use this code.

@franmontiel

This comment has been minimized.

Show comment
Hide comment
@franmontiel

franmontiel Dec 4, 2014

@imbeyondboredom you are totally right, thanks for the advice. Now should be fixed.

Owner

franmontiel commented Dec 4, 2014

@imbeyondboredom you are totally right, thanks for the advice. Now should be fixed.

@tibro4u

This comment has been minimized.

Show comment
Hide comment
@tibro4u

tibro4u Jan 29, 2015

When retrieving cookies via getValidCookies(URI uri) , its not detecting the sub-domain perfectly .
So I have changed
if ((storedUri.getPath() == null || storedUri.getPath().equals("/")) || storedUri.getPath().equals(uri.getPath()))
to -
if ((storedUri.getPath() == null || storedUri.getPath().equals("/")) || storedUri.getPath().equals(uri.getPath()) || uri.getPath().startsWith(storedUri.getPath())).

Problem Case -
URI was - localhost/api-testing/session.php

uri.getPath() returing - api-testing/session.php
storedUri.getPath() returning - api-testing/ (as it was saved via cookie.getPath())

so getValidCookies(URI uri) not returning cookie for localhost/api-testing/session.php

forked here - https://gist.github.com/tibro4u/7ae579c301f4a4e8262a

tibro4u commented Jan 29, 2015

When retrieving cookies via getValidCookies(URI uri) , its not detecting the sub-domain perfectly .
So I have changed
if ((storedUri.getPath() == null || storedUri.getPath().equals("/")) || storedUri.getPath().equals(uri.getPath()))
to -
if ((storedUri.getPath() == null || storedUri.getPath().equals("/")) || storedUri.getPath().equals(uri.getPath()) || uri.getPath().startsWith(storedUri.getPath())).

Problem Case -
URI was - localhost/api-testing/session.php

uri.getPath() returing - api-testing/session.php
storedUri.getPath() returning - api-testing/ (as it was saved via cookie.getPath())

so getValidCookies(URI uri) not returning cookie for localhost/api-testing/session.php

forked here - https://gist.github.com/tibro4u/7ae579c301f4a4e8262a

@morganwu

This comment has been minimized.

Show comment
Hide comment
@morganwu

morganwu Feb 11, 2015

fieldHttpOnly should not be redeclared in initFieldHttpOnly method, or there will be NPE.

fieldHttpOnly should not be redeclared in initFieldHttpOnly method, or there will be NPE.

@franmontiel

This comment has been minimized.

Show comment
Hide comment
@franmontiel

franmontiel Feb 16, 2015

@morganwu under what circumstances initFieldHttpOnly throws a NullPointerException?
Looking at my code I can see that if httpOnly field does not exists it will throw a NoSuchFieldException that is correctly catched.

Owner

franmontiel commented Feb 16, 2015

@morganwu under what circumstances initFieldHttpOnly throws a NullPointerException?
Looking at my code I can see that if httpOnly field does not exists it will throw a NoSuchFieldException that is correctly catched.

@ayau

This comment has been minimized.

Show comment
Hide comment
@ayau

ayau Mar 17, 2015

@franmontiel He's saying instead of having
Field fieldHttpOnly = ...
on line 90, you should have
fieldHttpOnly = ...

otherwise the instance variable fieldHttpOnly is never set and will always be null.

ayau commented Mar 17, 2015

@franmontiel He's saying instead of having
Field fieldHttpOnly = ...
on line 90, you should have
fieldHttpOnly = ...

otherwise the instance variable fieldHttpOnly is never set and will always be null.

@franmontiel

This comment has been minimized.

Show comment
Hide comment
@franmontiel

franmontiel Mar 20, 2015

@morganwu @ayau I can't believe I didn't see it! Now it is fixed. Thanks for your comments.

Owner

franmontiel commented Mar 20, 2015

@morganwu @ayau I can't believe I didn't see it! Now it is fixed. Thanks for your comments.

@franmontiel

This comment has been minimized.

Show comment
Hide comment
@franmontiel

franmontiel May 2, 2015

I've published a new revision to fix the bug pointed out by @tibro4u and others that I found, all of them related to domain matching and path matching. Now the code should adhere to the rfc6265 specification.

Owner

franmontiel commented May 2, 2015

I've published a new revision to fix the bug pointed out by @tibro4u and others that I found, all of them related to domain matching and path matching. Now the code should adhere to the rfc6265 specification.

@chujj

This comment has been minimized.

Show comment
Hide comment
@chujj

chujj May 9, 2015

Hi, @franmontiel
I have a question.
At https://gist.github.com/franmontiel/ed12a2295566b7076161#file-persistentcookiestore-java-L169,
Why the compare use startsWith(), I think it should endsWith().
We are test requestHost as subdomain, right?

chujj commented May 9, 2015

Hi, @franmontiel
I have a question.
At https://gist.github.com/franmontiel/ed12a2295566b7076161#file-persistentcookiestore-java-L169,
Why the compare use startsWith(), I think it should endsWith().
We are test requestHost as subdomain, right?

@franmontiel

This comment has been minimized.

Show comment
Hide comment
@franmontiel

franmontiel May 10, 2015

Hi @chujj, It seems that you are right, the startsWith() check shouldn't exists but there is no need to change it for endsWith() because it is already checked in the next line. I'm going to publish a new revision just removing the startsWith() call. Thanks!

Owner

franmontiel commented May 10, 2015

Hi @chujj, It seems that you are right, the startsWith() check shouldn't exists but there is no need to change it for endsWith() because it is already checked in the next line. I'm going to publish a new revision just removing the startsWith() call. Thanks!

@seanpont

This comment has been minimized.

Show comment
Hide comment
@seanpont

seanpont May 13, 2015

Great work. There's a small bug in PersistentCookieStore on line 170: it assumes that the requestHost is longer than the cookieHost, which is not necessarily true. This will cause an OutOfBounds exception if you have, for instance, "foo.example.com" in the cookie store and the request host is "example.com". A simple fix might be:
private boolean checkDomainsMatch(String cookieHost, String requestHost) {
return requestHost.equals(cookieHost) || requestHost.endsWith("." + cookieHost);
}

Great work. There's a small bug in PersistentCookieStore on line 170: it assumes that the requestHost is longer than the cookieHost, which is not necessarily true. This will cause an OutOfBounds exception if you have, for instance, "foo.example.com" in the cookie store and the request host is "example.com". A simple fix might be:
private boolean checkDomainsMatch(String cookieHost, String requestHost) {
return requestHost.equals(cookieHost) || requestHost.endsWith("." + cookieHost);
}

@seanpont

This comment has been minimized.

Show comment
Hide comment
@seanpont

seanpont May 15, 2015

Would you be so kind as to attach a license to this work? Thanks.

Would you be so kind as to attach a license to this work? Thanks.

@franmontiel

This comment has been minimized.

Show comment
Hide comment
@franmontiel

franmontiel May 30, 2015

@seanpont, fix appplied and license attached.
Thanks for your contribution, very simple and elegant (I almost end up doing a whole class for a simple check...)

Owner

franmontiel commented May 30, 2015

@seanpont, fix appplied and license attached.
Thanks for your contribution, very simple and elegant (I almost end up doing a whole class for a simple check...)

@pppedrillo

This comment has been minimized.

Show comment
Hide comment
@pppedrillo

pppedrillo Aug 1, 2015

Great stuff, thanks!
A small bug at PersistentCookieStore on line 203:
there must be ..... && requestPath.substring(cookiePath.length()).charAt(0) == '/');
instead of && requestPath.substring(cookiePath.length() - 1).charAt(0) == '/');

Great stuff, thanks!
A small bug at PersistentCookieStore on line 203:
there must be ..... && requestPath.substring(cookiePath.length()).charAt(0) == '/');
instead of && requestPath.substring(cookiePath.length() - 1).charAt(0) == '/');

@franmontiel

This comment has been minimized.

Show comment
Hide comment
@franmontiel

franmontiel Aug 21, 2015

@tassa69 you're correct. Thanks

Owner

franmontiel commented Aug 21, 2015

@tassa69 you're correct. Thanks

@StErMi

This comment has been minimized.

Show comment
Hide comment
@StErMi

StErMi Sep 1, 2015

@franmontiel maybe you should create a repository in order to track all the changes and let us fork it ;)

StErMi commented Sep 1, 2015

@franmontiel maybe you should create a repository in order to track all the changes and let us fork it ;)

@gowthamlakshman

This comment has been minimized.

Show comment
Hide comment
@gowthamlakshman

gowthamlakshman Sep 5, 2015

Excellent work, really helped me, thanks..

Excellent work, really helped me, thanks..

@thintsa

This comment has been minimized.

Show comment
Hide comment
@thintsa

thintsa Sep 21, 2015

If you run into problems with proguard (you will, reflection doesn't work when minifying/using proguard) when using this gist add this line to your proguard config:
-keep public class package.SerializableHttpCookie { *; }

thintsa commented Sep 21, 2015

If you run into problems with proguard (you will, reflection doesn't work when minifying/using proguard) when using this gist add this line to your proguard config:
-keep public class package.SerializableHttpCookie { *; }

@kosiara

This comment has been minimized.

Show comment
Hide comment
@kosiara

kosiara Sep 21, 2015

Thanks, it worked great in my case

kosiara commented Sep 21, 2015

Thanks, it worked great in my case

@shortstuffsushi

This comment has been minimized.

Show comment
Hide comment
@shortstuffsushi

shortstuffsushi Sep 27, 2015

I'm curious, what is the point of the add/remove cycle here, are you trying to remove a potentially older cookie or something like that?

I'm curious, what is the point of the add/remove cycle here, are you trying to remove a potentially older cookie or something like that?

@franmontiel

This comment has been minimized.

Show comment
Hide comment
@franmontiel

franmontiel Sep 29, 2015

@shortstuffsushi it is just to assure that the newest cookie replace a possible older one.

Owner

franmontiel commented Sep 29, 2015

@shortstuffsushi it is just to assure that the newest cookie replace a possible older one.

@linakis

This comment has been minimized.

Show comment
Hide comment
@linakis

linakis Oct 3, 2015

Thanks for that @thintsa I was about to lose my mind.

linakis commented Oct 3, 2015

Thanks for that @thintsa I was about to lose my mind.

@BufferedOverfl0wed

This comment has been minimized.

Show comment
Hide comment
@BufferedOverfl0wed

BufferedOverfl0wed Oct 8, 2015

I could easily change this so that it would work for a non-android application right? From what I understand SharedPreferences is like a settings file or something, So instead of that I would just write to a file correct?

I could easily change this so that it would work for a non-android application right? From what I understand SharedPreferences is like a settings file or something, So instead of that I would just write to a file correct?

@franmontiel

This comment has been minimized.

Show comment
Hide comment
@franmontiel

franmontiel Oct 10, 2015

@innsr you are right, the code could be changed to use a more standard persistence method.

Owner

franmontiel commented Oct 10, 2015

@innsr you are right, the code could be changed to use a more standard persistence method.

@theyann

This comment has been minimized.

Show comment
Hide comment
@theyann

theyann Nov 17, 2015

Hello @franmontiel and thank you so very much for this.
I don't have much to say to improve this as it seems to be already pretty well done.
Just a very few minor stuff:

  • avoid the use of iterators, you can favor old school for loops to avoid instantiating iterators. EDIT: nevermind, you're using sets, I thought you were using lists :)
  • when instantiating a collection, no need to specify the generic argument (List cookies = new ArrayList<>() is good enough :) ).
  • line 146 of the PersistentCookieStore file: targetCookies can never null, so no need to check for it

Questions:

  • line 73 and 74 doing a remove and an add seems strange to me, is it really more efficient than checking wether cookie is already in the map ?
  • line 161 of the PersistenCookieStore file: you return a new ArrayList<>(targetCookies) ... why not return targetCookies that was just instantiated earlier and never reassigned to anything. Is there a reason for copying all the objects again? EDIT: never mind this either, still using sets instead of lists. In this case though, I believe you could just create a list for targetCookies as the values already come from a set, could you have a possibility of redundance?

Thanks in advance if you can answer these silly questions :)
Again, this work is very appreciated !

theyann commented Nov 17, 2015

Hello @franmontiel and thank you so very much for this.
I don't have much to say to improve this as it seems to be already pretty well done.
Just a very few minor stuff:

  • avoid the use of iterators, you can favor old school for loops to avoid instantiating iterators. EDIT: nevermind, you're using sets, I thought you were using lists :)
  • when instantiating a collection, no need to specify the generic argument (List cookies = new ArrayList<>() is good enough :) ).
  • line 146 of the PersistentCookieStore file: targetCookies can never null, so no need to check for it

Questions:

  • line 73 and 74 doing a remove and an add seems strange to me, is it really more efficient than checking wether cookie is already in the map ?
  • line 161 of the PersistenCookieStore file: you return a new ArrayList<>(targetCookies) ... why not return targetCookies that was just instantiated earlier and never reassigned to anything. Is there a reason for copying all the objects again? EDIT: never mind this either, still using sets instead of lists. In this case though, I believe you could just create a list for targetCookies as the values already come from a set, could you have a possibility of redundance?

Thanks in advance if you can answer these silly questions :)
Again, this work is very appreciated !

@franmontiel

This comment has been minimized.

Show comment
Hide comment
@franmontiel

franmontiel Nov 23, 2015

Hi @theyann, thanks for your code review. Here are some answers:

when instantiating a collection, no need to specify the generic argument (List cookies = new ArrayList<>() is good enough :) ).

I originally wrote this in a Java 6 project and that is why I didn't use the diamond operator.

line 146 of the PersistentCookieStore file: targetCookies can never null, so no need to check for it

Changed in the last revision. Now an empty check is done.

line 73 and 74 doing a remove and an add seems strange to me, is it really more efficient than checking wether cookie is already in the map ?

If a previously stored cookie is received again the old one should be overwritten by the most recent cookie (in order to maintain the most recent expire date). To do that is necessary to remove and then add the new cookie to the set.

line 161 of the PersistenCookieStore file: you return a new ArrayList<>(targetCookies) ... why not return targetCookies that was just instantiated earlier and never reassigned to anything. Is there a reason for copying all the objects again? EDIT: never mind this either, still using sets instead of lists. In this case though, I believe you could just create a list for targetCookies as the values already come from a set, could you have a possibility of redundance?

Changed in the last revision to directly use a list.

Owner

franmontiel commented Nov 23, 2015

Hi @theyann, thanks for your code review. Here are some answers:

when instantiating a collection, no need to specify the generic argument (List cookies = new ArrayList<>() is good enough :) ).

I originally wrote this in a Java 6 project and that is why I didn't use the diamond operator.

line 146 of the PersistentCookieStore file: targetCookies can never null, so no need to check for it

Changed in the last revision. Now an empty check is done.

line 73 and 74 doing a remove and an add seems strange to me, is it really more efficient than checking wether cookie is already in the map ?

If a previously stored cookie is received again the old one should be overwritten by the most recent cookie (in order to maintain the most recent expire date). To do that is necessary to remove and then add the new cookie to the set.

line 161 of the PersistenCookieStore file: you return a new ArrayList<>(targetCookies) ... why not return targetCookies that was just instantiated earlier and never reassigned to anything. Is there a reason for copying all the objects again? EDIT: never mind this either, still using sets instead of lists. In this case though, I believe you could just create a list for targetCookies as the values already come from a set, could you have a possibility of redundance?

Changed in the last revision to directly use a list.

@lgalant

This comment has been minimized.

Show comment
Hide comment
@lgalant

lgalant Dec 26, 2015

Thanks for sharing, excelent work !!
I'm having a problem though, I'm getting NPE when I enable ProGuard. I followed @thintsa advice but it doesn't solve my problem.

This is part of the stack trace:
Caused by: java.lang.NullPointerException
at com.ati.Utils.PersistentCookieStore.getValidCookies(PersistentCookieStore.java:167)
at com.ati.Utils.PersistentCookieStore.get(PersistentCookieStore.java:134)
at java.net.CookieManager.get(CookieManager.java:112)
at com.squareup.okhttp.internal.http.HttpEngine.networkRequest(HttpEngine.java:719)
at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:225)

line 167 is when it calls if (currentCookie.hasExpired())

Tried adding all kinds of stuff to proguard-rules (even including -dontobfuscate) with no luck. Works great with proguard off
Any help will be much appreciated!

lgalant commented Dec 26, 2015

Thanks for sharing, excelent work !!
I'm having a problem though, I'm getting NPE when I enable ProGuard. I followed @thintsa advice but it doesn't solve my problem.

This is part of the stack trace:
Caused by: java.lang.NullPointerException
at com.ati.Utils.PersistentCookieStore.getValidCookies(PersistentCookieStore.java:167)
at com.ati.Utils.PersistentCookieStore.get(PersistentCookieStore.java:134)
at java.net.CookieManager.get(CookieManager.java:112)
at com.squareup.okhttp.internal.http.HttpEngine.networkRequest(HttpEngine.java:719)
at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:225)

line 167 is when it calls if (currentCookie.hasExpired())

Tried adding all kinds of stuff to proguard-rules (even including -dontobfuscate) with no luck. Works great with proguard off
Any help will be much appreciated!

@lgalant

This comment has been minimized.

Show comment
Hide comment
@lgalant

lgalant Dec 28, 2015

OK, please disregard my previous comment.
I found I was calling CookieHandler.setDefault(..) with a new PersistentCookieStore every time I made a call to my server API. Fixed problem by making sure setDefault() is called only once in the app.
Not sure how Proguard got into the middle of this, but I'm happy the issue is solved!

lgalant commented Dec 28, 2015

OK, please disregard my previous comment.
I found I was calling CookieHandler.setDefault(..) with a new PersistentCookieStore every time I made a call to my server API. Fixed problem by making sure setDefault() is called only once in the app.
Not sure how Proguard got into the middle of this, but I'm happy the issue is solved!

@AnswerZhao

This comment has been minimized.

Show comment
Hide comment
@AnswerZhao

AnswerZhao Jan 6, 2016

thank you very much , it help me so much. awesome code.

thank you very much , it help me so much. awesome code.

@1f7

This comment has been minimized.

Show comment
Hide comment
@1f7

1f7 Feb 4, 2016

thank all very much, very useful solution. i use with OkHttp3

1f7 commented Feb 4, 2016

thank all very much, very useful solution. i use with OkHttp3

@franmontiel

This comment has been minimized.

Show comment
Hide comment
@franmontiel

franmontiel Feb 11, 2016

@1f7 I'm glad that this solution is working for you.

I've just released a new library based on the OkHttp 3 new CookieJar. You can find it here and you will be able to get rid of the okhttp-urlconnection dependency.

Owner

franmontiel commented Feb 11, 2016

@1f7 I'm glad that this solution is working for you.

I've just released a new library based on the OkHttp 3 new CookieJar. You can find it here and you will be able to get rid of the okhttp-urlconnection dependency.

@Rotemy

This comment has been minimized.

Show comment
Hide comment
@Rotemy

Rotemy Feb 16, 2016

Thank you very much it works great.

May I suggest that you will upload this to maven and allow us to use gradle. I hate that I have external code in my project as a raw java file.

Thanks!

Rotemy commented Feb 16, 2016

Thank you very much it works great.

May I suggest that you will upload this to maven and allow us to use gradle. I hate that I have external code in my project as a raw java file.

Thanks!

@mrmaffen

This comment has been minimized.

Show comment
Hide comment
@mrmaffen

mrmaffen Mar 22, 2016

@lgalant Proguard obfuscates your SerializableHttpCookie objects.
Add this to your proguard config to avoid this obfuscation problem:

-keepnames class * implements java.io.Serializable

-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    !static !transient ;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

@franmontiel If Proguard is not configured correctly your PersistentCookieStore will fail in a silent and confusing manner:
HttpCookie cookie will be null here https://gist.github.com/franmontiel/ed12a2295566b7076161#file-persistentcookiestore-java-L47 because an InvalidClassException is being catched in https://gist.github.com/franmontiel/ed12a2295566b7076161#file-serializablehttpcookie-java-L64. Further down the line this will result in a null object being added to the allCookies Map, which in turn will cause a NPE in https://gist.github.com/franmontiel/ed12a2295566b7076161#file-persistentcookiestore-java-L150 as described by @lgalant

@lgalant Proguard obfuscates your SerializableHttpCookie objects.
Add this to your proguard config to avoid this obfuscation problem:

-keepnames class * implements java.io.Serializable

-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    !static !transient ;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

@franmontiel If Proguard is not configured correctly your PersistentCookieStore will fail in a silent and confusing manner:
HttpCookie cookie will be null here https://gist.github.com/franmontiel/ed12a2295566b7076161#file-persistentcookiestore-java-L47 because an InvalidClassException is being catched in https://gist.github.com/franmontiel/ed12a2295566b7076161#file-serializablehttpcookie-java-L64. Further down the line this will result in a null object being added to the allCookies Map, which in turn will cause a NPE in https://gist.github.com/franmontiel/ed12a2295566b7076161#file-persistentcookiestore-java-L150 as described by @lgalant

@heqingbao

This comment has been minimized.

Show comment
Hide comment
@heqingbao

heqingbao Mar 28, 2016

Why the stream object in encode/decode methods does not require close?

Why the stream object in encode/decode methods does not require close?

@JensMetzner

This comment has been minimized.

Show comment
Hide comment
@JensMetzner

JensMetzner Apr 19, 2016

Why do you use your own checkDomainsMatch(String, String) and not the function of java.net.HttpCookie HttpCookie.domainMatches(String, String)?

Why do you use your own checkDomainsMatch(String, String) and not the function of java.net.HttpCookie HttpCookie.domainMatches(String, String)?

@chandraveerr

This comment has been minimized.

Show comment
Hide comment
@chandraveerr

chandraveerr May 18, 2016

@hintsa : thanks man saved me from great deal of headache

@hintsa : thanks man saved me from great deal of headache

@leviyehonatan

This comment has been minimized.

Show comment
Hide comment
@leviyehonatan

leviyehonatan May 23, 2016

the solution you have offered does not respect the expiry of cookies, i have created a nice kotlin version of it which does

https://gist.github.com/leviyehonatan/0c53e89864a0890c2e524d87c6c70c2a

the solution you have offered does not respect the expiry of cookies, i have created a nice kotlin version of it which does

https://gist.github.com/leviyehonatan/0c53e89864a0890c2e524d87c6c70c2a

@manikantagarikipati

This comment has been minimized.

Show comment
Hide comment
@manikantagarikipati

manikantagarikipati Jul 4, 2016

Should i have a single instance of the ClearableCookieJar? or else the PersistentCookieJar is taking care of it ? Sorry if my question sounds stupid.

Should i have a single instance of the ClearableCookieJar? or else the PersistentCookieJar is taking care of it ? Sorry if my question sounds stupid.

@franmontiel

This comment has been minimized.

Show comment
Hide comment
@franmontiel

franmontiel Jul 10, 2016

@manikantagarikipati it might be possible that your are asking about the PersistentCookieJar library?
In any case you should add the CookieStore(this gist) or the CookieJar to the OkHttpClient and you should be using one single OkHttp client.

Owner

franmontiel commented Jul 10, 2016

@manikantagarikipati it might be possible that your are asking about the PersistentCookieJar library?
In any case you should add the CookieStore(this gist) or the CookieJar to the OkHttpClient and you should be using one single OkHttp client.

@nuald

This comment has been minimized.

Show comment
Hide comment
@nuald

nuald Dec 7, 2016

I've fixed few issues:

Please feel free to grab the changes from my fork: https://gist.github.com/nuald/ad776c9f7f52d3f6865142bda58c6d3f

nuald commented Dec 7, 2016

I've fixed few issues:

Please feel free to grab the changes from my fork: https://gist.github.com/nuald/ad776c9f7f52d3f6865142bda58c6d3f

@hoomanv

This comment has been minimized.

Show comment
Hide comment
@hoomanv

hoomanv Dec 7, 2016

If by HttpCookie you mean java.net.HttpCookie then there is a huge mistake in this code.
The java.net.HttpCookie has a private final field called "whenCreated" that is set at consturction time and is used to calculate hasExpired().
Your code is not serializing that value hence after reloading cookies from the persistence all of them get new extended lifetime.

EDIT: I just saw the comment above by nuald and seems someone else has already detected this bug

EDIT2: I was looking for a way to implement persistent cookie store in standard java api not android. The bug that I explained refers to that of the standard java api.

hoomanv commented Dec 7, 2016

If by HttpCookie you mean java.net.HttpCookie then there is a huge mistake in this code.
The java.net.HttpCookie has a private final field called "whenCreated" that is set at consturction time and is used to calculate hasExpired().
Your code is not serializing that value hence after reloading cookies from the persistence all of them get new extended lifetime.

EDIT: I just saw the comment above by nuald and seems someone else has already detected this bug

EDIT2: I was looking for a way to implement persistent cookie store in standard java api not android. The bug that I explained refers to that of the standard java api.

@torv

This comment has been minimized.

Show comment
Hide comment
@torv

torv Dec 11, 2016

@mrmaffen build fail with yours, i change to below:

-keepnames class * implements java.io.Serializable

-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient ;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}

torv commented Dec 11, 2016

@mrmaffen build fail with yours, i change to below:

-keepnames class * implements java.io.Serializable

-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient ;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}

@agent10

This comment has been minimized.

Show comment
Hide comment
@agent10

agent10 Dec 19, 2016

FYI, isHttpOnly() and setHttpOnly() methods were added to 24 API version.

agent10 commented Dec 19, 2016

FYI, isHttpOnly() and setHttpOnly() methods were added to 24 API version.

@agent10

This comment has been minimized.

Show comment
Hide comment
@agent10

agent10 Dec 19, 2016

I found strange situation in getValidCookies() method.
storedUri may match with uri but allCookies.get(storedUri) return null value.
It leads to crash in:

HttpCookie currentCookie = it.next();
if (currentCookie.hasExpired()) {

I have no Proguard option and I set CookieStore only once..

agent10 commented Dec 19, 2016

I found strange situation in getValidCookies() method.
storedUri may match with uri but allCookies.get(storedUri) return null value.
It leads to crash in:

HttpCookie currentCookie = it.next();
if (currentCookie.hasExpired()) {

I have no Proguard option and I set CookieStore only once..

@yeshengwu

This comment has been minimized.

Show comment
Hide comment
@yeshengwu

yeshengwu Feb 6, 2017

@agent10
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.net.HttpCookie.hasExpired()' on a null object reference
device:NEXUS 4
client code:
in application onCreate:
CookieManager manager = new CookieManager(
new PersistentCookieStore(this),
CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault(manager);

if I comment SerializableHttpCookie.java npe bug will not appear;
line 127 writeObject method out.writeBoolean(getHttpOnly());
line 144 readObject method setHttpOnly(in.readBoolean());

@agent10
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.net.HttpCookie.hasExpired()' on a null object reference
device:NEXUS 4
client code:
in application onCreate:
CookieManager manager = new CookieManager(
new PersistentCookieStore(this),
CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault(manager);

if I comment SerializableHttpCookie.java npe bug will not appear;
line 127 writeObject method out.writeBoolean(getHttpOnly());
line 144 readObject method setHttpOnly(in.readBoolean());

@ggeetha

This comment has been minimized.

Show comment
Hide comment
@ggeetha

ggeetha Feb 24, 2017

Hi,

While uploading a file using okHttp, facing the following issue. Pls help me to sort it out.
Mentioned the issues:

SerializableHttpCookie: java.lang.NoSuchFieldException: httpOnly
at java.lang.Class.getDeclaredField(Class.java:546)
at in.xxxx.xxxx.SerializableHttpCookie.initFieldHttpOnly(SerializableHttpCookie.java:98)
at in.xxxx.xxxx.SerializableHttpCookie.setHttpOnly(SerializableHttpCookie.java:88)
at in.xxxx.xxxx.SerializableHttpCookie.readObject(SerializableHttpCookie.java:131)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1357)
at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1269)
at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1858)
at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2006)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1963)
at in.xxxx.xxxx.SerializableHttpCookie.decode(SerializableHttpCookie.java:62)
at in.xxxx.xxxx.PersistentCookieStore.loadAllFromPersistence(PersistentCookieStore.java:53)
at in.xxxx.xxxx.PersistentCookieStore.(PersistentCookieStore.java:39)

System.err: java.lang.NullPointerException
System.err: at in.xxxx.xxxx.PersistentCookieStore.(PersistentCookieStore.java:37)
System.err: at in.xxxx.xxxx.RequestManager.upload(RequestManager.java:204)

ggeetha commented Feb 24, 2017

Hi,

While uploading a file using okHttp, facing the following issue. Pls help me to sort it out.
Mentioned the issues:

SerializableHttpCookie: java.lang.NoSuchFieldException: httpOnly
at java.lang.Class.getDeclaredField(Class.java:546)
at in.xxxx.xxxx.SerializableHttpCookie.initFieldHttpOnly(SerializableHttpCookie.java:98)
at in.xxxx.xxxx.SerializableHttpCookie.setHttpOnly(SerializableHttpCookie.java:88)
at in.xxxx.xxxx.SerializableHttpCookie.readObject(SerializableHttpCookie.java:131)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at java.io.ObjectInputStream.readObjectForClass(ObjectInputStream.java:1357)
at java.io.ObjectInputStream.readHierarchy(ObjectInputStream.java:1269)
at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1858)
at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2006)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1963)
at in.xxxx.xxxx.SerializableHttpCookie.decode(SerializableHttpCookie.java:62)
at in.xxxx.xxxx.PersistentCookieStore.loadAllFromPersistence(PersistentCookieStore.java:53)
at in.xxxx.xxxx.PersistentCookieStore.(PersistentCookieStore.java:39)

System.err: java.lang.NullPointerException
System.err: at in.xxxx.xxxx.PersistentCookieStore.(PersistentCookieStore.java:37)
System.err: at in.xxxx.xxxx.RequestManager.upload(RequestManager.java:204)

@CapnSpellcheck

This comment has been minimized.

Show comment
Hide comment
@CapnSpellcheck

CapnSpellcheck Jul 24, 2017

@mrmaffen @torv I also get a build error with these Proguard rules, on the static transient line:
Warning: Exception while processing task java.io.IOException: proguard.ParseException: Expecting java type before ';' in line 32 of file '/Users/julian/AndroidStudioProjects/Twinkle/app/proguard-rules.pro'

It seems like both of you have this line.
Update: I think it's because the real line keeps getting filtered: try quoting it:
!static !transient <fields>;

CapnSpellcheck commented Jul 24, 2017

@mrmaffen @torv I also get a build error with these Proguard rules, on the static transient line:
Warning: Exception while processing task java.io.IOException: proguard.ParseException: Expecting java type before ';' in line 32 of file '/Users/julian/AndroidStudioProjects/Twinkle/app/proguard-rules.pro'

It seems like both of you have this line.
Update: I think it's because the real line keeps getting filtered: try quoting it:
!static !transient <fields>;

@TristanWiley

This comment has been minimized.

Show comment
Hide comment
@TristanWiley

TristanWiley Aug 11, 2017

Been trying to fix the httpOnly problem on API level 18. Thoughts?

08-11 15:36:11.243 3613-3644/me.shreyasr.chatse W/SerializableHttpCookie: java.lang.NullPointerException
                                                                              at me.shreyasr.chatse.network.cookie.SerializableHttpCookie.getHttpOnly(SerializableHttpCookie.java:100)
                                                                              at me.shreyasr.chatse.network.cookie.SerializableHttpCookie.writeObject(SerializableHttpCookie.java:142)
                                                                              at java.lang.reflect.Method.invokeNative(Native Method)
                                                                              at java.lang.reflect.Method.invoke(Method.java:525)
                                                                              at java.io.ObjectOutputStream.writeHierarchy(ObjectOutputStream.java:1055)
                                                                              at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1406)
                                                                              at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1673)
                                                                              at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1519)
                                                                              at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1483)
                                                                              at me.shreyasr.chatse.network.cookie.SerializableHttpCookie.encode(SerializableHttpCookie.java:69)
                                                                              at me.shreyasr.chatse.network.cookie.PersistentCookieStore.saveToPersistence(PersistentCookieStore.java:139)
                                                                              at me.shreyasr.chatse.network.cookie.PersistentCookieStore.add(PersistentCookieStore.java:132)
                                                                              at java.net.CookieManager.put(CookieManager.java:188)
                                                                              at com.squareup.okhttp.internal.http.HttpEngine.receiveHeaders(HttpEngine.java:1054)
                                                                              at com.squareup.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:796)
                                                                              at com.squareup.okhttp.Call.getResponse(Call.java:274)
                                                                              at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:230)
                                                                              at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:201)
                                                                              at com.squareup.okhttp.Call.execute(Call.java:81)
                                                                              at me.shreyasr.chatse.chat.service.IncomingEventService.loadRoom$app_debug(IncomingEventService.kt:65)
                                                                              at me.shreyasr.chatse.chat.service.IncomingEventServiceBinder.loadRoom(IncomingEventServiceBinder.kt:27)
                                                                              at me.shreyasr.chatse.chat.ChatActivity$rejoinFavoriteRooms$1.invoke(ChatActivity.kt:481)
                                                                              at me.shreyasr.chatse.chat.ChatActivity$rejoinFavoriteRooms$1.invoke(ChatActivity.kt:56)
                                                                              at org.jetbrains.anko.AsyncKt$doAsync$1.invoke(Async.kt:140)
                                                                              at org.jetbrains.anko.AsyncKt$doAsync$1.invoke(Async.kt)
                                                                              at org.jetbrains.anko.AsyncKt$sam$Callable$761a5578.call(Async.kt)
                                                                              at java.util.concurrent.FutureTask.run(FutureTask.java:234)
                                                                              at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:153)
                                                                              at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:267)
                                                                              at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
                                                                              at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
                                                                              at java.lang.Thread.run(Thread.java:841)

Been trying to fix the httpOnly problem on API level 18. Thoughts?

08-11 15:36:11.243 3613-3644/me.shreyasr.chatse W/SerializableHttpCookie: java.lang.NullPointerException
                                                                              at me.shreyasr.chatse.network.cookie.SerializableHttpCookie.getHttpOnly(SerializableHttpCookie.java:100)
                                                                              at me.shreyasr.chatse.network.cookie.SerializableHttpCookie.writeObject(SerializableHttpCookie.java:142)
                                                                              at java.lang.reflect.Method.invokeNative(Native Method)
                                                                              at java.lang.reflect.Method.invoke(Method.java:525)
                                                                              at java.io.ObjectOutputStream.writeHierarchy(ObjectOutputStream.java:1055)
                                                                              at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1406)
                                                                              at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1673)
                                                                              at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1519)
                                                                              at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1483)
                                                                              at me.shreyasr.chatse.network.cookie.SerializableHttpCookie.encode(SerializableHttpCookie.java:69)
                                                                              at me.shreyasr.chatse.network.cookie.PersistentCookieStore.saveToPersistence(PersistentCookieStore.java:139)
                                                                              at me.shreyasr.chatse.network.cookie.PersistentCookieStore.add(PersistentCookieStore.java:132)
                                                                              at java.net.CookieManager.put(CookieManager.java:188)
                                                                              at com.squareup.okhttp.internal.http.HttpEngine.receiveHeaders(HttpEngine.java:1054)
                                                                              at com.squareup.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:796)
                                                                              at com.squareup.okhttp.Call.getResponse(Call.java:274)
                                                                              at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:230)
                                                                              at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:201)
                                                                              at com.squareup.okhttp.Call.execute(Call.java:81)
                                                                              at me.shreyasr.chatse.chat.service.IncomingEventService.loadRoom$app_debug(IncomingEventService.kt:65)
                                                                              at me.shreyasr.chatse.chat.service.IncomingEventServiceBinder.loadRoom(IncomingEventServiceBinder.kt:27)
                                                                              at me.shreyasr.chatse.chat.ChatActivity$rejoinFavoriteRooms$1.invoke(ChatActivity.kt:481)
                                                                              at me.shreyasr.chatse.chat.ChatActivity$rejoinFavoriteRooms$1.invoke(ChatActivity.kt:56)
                                                                              at org.jetbrains.anko.AsyncKt$doAsync$1.invoke(Async.kt:140)
                                                                              at org.jetbrains.anko.AsyncKt$doAsync$1.invoke(Async.kt)
                                                                              at org.jetbrains.anko.AsyncKt$sam$Callable$761a5578.call(Async.kt)
                                                                              at java.util.concurrent.FutureTask.run(FutureTask.java:234)
                                                                              at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:153)
                                                                              at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:267)
                                                                              at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
                                                                              at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
                                                                              at java.lang.Thread.run(Thread.java:841)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment