Create a gist now

Instantly share code, notes, and snippets.

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;
}
}

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.

Owner

franmontiel commented Dec 4, 2014

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

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

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

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 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.

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.

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 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?

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!

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);
}

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

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...)

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) == '/');

Owner

franmontiel commented Aug 21, 2015

@tassa69 you're correct. Thanks

StErMi commented Sep 1, 2015

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

Excellent work, really helped me, thanks..

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 commented Sep 21, 2015

Thanks, it worked great in my case

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?

Owner

franmontiel commented Sep 29, 2015

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

linakis commented Oct 3, 2015

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

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?

Owner

franmontiel commented Oct 10, 2015

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

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 !

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 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 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!

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

1f7 commented Feb 4, 2016

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

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 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!

@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

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

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

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

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

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

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 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 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 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 commented Dec 19, 2016

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

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..

@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 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 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>;

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