1
0
mirror of https://github.com/etesync/android synced 2025-01-11 08:10:58 +00:00

Handle cookies correctly using a name/domain/path MultiKeyMap

This commit is contained in:
Ricki Hirner 2016-06-21 20:51:52 +02:00
parent ad8c832819
commit 977409511a
2 changed files with 49 additions and 15 deletions

View File

@ -8,10 +8,13 @@
package at.bitfire.davdroid; package at.bitfire.davdroid;
import java.util.Collections; import org.apache.commons.collections4.MapIterator;
import org.apache.commons.collections4.keyvalue.MultiKey;
import org.apache.commons.collections4.map.HashedMap;
import org.apache.commons.collections4.map.MultiKeyMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import okhttp3.Cookie; import okhttp3.Cookie;
import okhttp3.CookieJar; import okhttp3.CookieJar;
@ -25,20 +28,42 @@ public class MemoryCookieStore implements CookieJar {
public static final MemoryCookieStore INSTANCE = new MemoryCookieStore(); public static final MemoryCookieStore INSTANCE = new MemoryCookieStore();
protected final Map<HttpUrl, List<Cookie>> store = new ConcurrentHashMap<>(); /**
* Stored cookies. The multi-key consists of three parts: name, domain, and path.
* This ensures that cookies can be overwritten. [RFC 6265 5.3 Storage Model]
* Not thread-safe!
*/
protected final MultiKeyMap<String, Cookie> storage = MultiKeyMap.multiKeyMap(new HashedMap<MultiKey<? extends String>, Cookie>());
@Override @Override
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) { public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
store.put(url, cookies); synchronized(storage) {
for (Cookie cookie : cookies)
storage.put(cookie.name(), cookie.domain(), cookie.path(), cookie);
}
} }
@Override @Override
public List<Cookie> loadForRequest(HttpUrl url) { public List<Cookie> loadForRequest(HttpUrl url) {
List<Cookie> cookies = store.get(url); List<Cookie> cookies = new LinkedList<>();
if (cookies == null) synchronized(storage) {
cookies = Collections.emptyList(); MapIterator<MultiKey<? extends String>, Cookie> iter = storage.mapIterator();
while (iter.hasNext()) {
iter.next();
Cookie cookie = iter.getValue();
// remove expired cookies
if (cookie.expiresAt() <= System.currentTimeMillis()) {
iter.remove();
continue;
}
// add applicable cookies
if (cookie.matches(url))
cookies.add(cookie);
}
}
return cookies; return cookies;
} }

View File

@ -38,12 +38,13 @@ public class HttpClientTest extends TestCase {
} }
public void testCookies() throws IOException, InterruptedException, URISyntaxException { public void testCookies() throws IOException, InterruptedException, URISyntaxException {
HttpUrl url = server.url("/"); HttpUrl url = server.url("/test");
// set cookie in first response // set cookie for root path (/) and /test path in first response
server.enqueue(new MockResponse() server.enqueue(new MockResponse()
.setResponseCode(200) .setResponseCode(200)
.setHeader("Set-Cookie", "theme=light; path=/") .addHeader("Set-Cookie", "cookie1=1; path=/")
.addHeader("Set-Cookie", "cookie2=2")
.setBody("Cookie set")); .setBody("Cookie set"));
httpClient.newCall(new Request.Builder() httpClient.newCall(new Request.Builder()
.get().url(url) .get().url(url)
@ -51,14 +52,22 @@ public class HttpClientTest extends TestCase {
assertNull(server.takeRequest().getHeader("Cookie")); assertNull(server.takeRequest().getHeader("Cookie"));
// cookie should be sent with second request // cookie should be sent with second request
// second response lets first cookie expire and overwrites second cookie
server.enqueue(new MockResponse()
.addHeader("Set-Cookie", "cookie1=1a; path=/; Max-Age=0")
.addHeader("Set-Cookie", "cookie2=2a")
.setResponseCode(200));
httpClient.newCall(new Request.Builder()
.get().url(url)
.build()).execute();
assertEquals("cookie2=2; cookie1=1", server.takeRequest().getHeader("Cookie"));
server.enqueue(new MockResponse() server.enqueue(new MockResponse()
.setResponseCode(200)); .setResponseCode(200));
httpClient.newCall(new Request.Builder() httpClient.newCall(new Request.Builder()
.get().url(url) .get().url(url)
.build()).execute(); .build()).execute();
//assertEquals("theme=light", server.takeRequest().getHeader("Cookie")); assertEquals("cookie2=2a", server.takeRequest().getHeader("Cookie"));
// doesn't work for URLs with ports, see https://code.google.com/p/android/issues/detail?id=193475
} }
} }