Skip to content

Implemented thread-safe CookieStore #1494

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
Closed

Implemented thread-safe CookieStore #1494

wants to merge 9 commits into from

Conversation

unoexperto
Copy link
Contributor

@unoexperto unoexperto commented Jan 11, 2018

@slandelle I copied your unitests from gattling too. I kept extra methods in CookieStore besides add and get you and I discussed. I actively use remove, getAll and clear because I heavily use AHC for web scraping and serialize and manipulate cookies between sessions while maintaining connection to HTTP server.

If everything looks good to you could you please push new build to Maven ? I need it in production :)

Thanks!

@slandelle
Copy link
Contributor

What have you done with the poms??? Please revert.

This reverts commit 094faaa
@unoexperto
Copy link
Contributor Author

@slandelle Done :)

import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

public class ThreadSafeCookieStore implements CookieStore {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please make final

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

public boolean remove(Cookie cookie) {
try {
lock.lock();
return cookieJar.entrySet().removeIf(v -> v.getValue().cookie == cookie);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean equals, not ==.
Then, I suspect the predicate is wrong and one want to remove by (domain, path, name, value) and don't take other fields into account. Letting user pass a predicate instead would probably be better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I intentionally wanted to remove by reference because intention is to modify cookie jar using Cookie objects returned by .getAll() or get() methods. This is similar to what java.net.InMemoryCookieStore.remove() does.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is specific to your use case, Other people might want a different strategy, eg drop all cookies for a given domain. So it might be best to change the parameter to a Predicate<Cookie>.

if (!requestUri.getPath().isEmpty())
return requestUri.getPath();
else
return "/";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use ternary operator ?.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Forgot it exists in Java after years in Scala :)

final boolean[] removeExpired = {false};
List<Cookie> result = cookieJar
.entrySet()
.parallelStream()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need a parallelStream here? Looks overkill, all the more as most users would only have a few cookies at the same time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really. Let me downgrade then.

public List<Cookie> getAll() {
try {
lock.lock();
final boolean[] removeExpired = {false};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need an array instead of a mutable boolean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variables used in lambda have to be final or effectively final so you can't modify them. Sad limitation of Java8.

* @param requestFilters {@link List<RequestFilter>}
* @return {@link FilterContext}
*/
public FilterContext<T> preProcessRequest(List<RequestFilter> requestFilters) throws FilterException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused, please revert

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

import org.asynchttpclient.AsyncHandler;
import org.asynchttpclient.AsyncHttpClientConfig;
import org.asynchttpclient.HttpResponseStatus;
import org.asynchttpclient.cookie.CookieStore;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert changes here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


final boolean[] removeExpired = {false};

List<Cookie> result = cookieJar.entrySet().parallelStream().filter(pair -> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why parallelStream?


private List<Cookie> get(String domain, String path, boolean secure) {

final boolean[] removeExpired = {false};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why an array?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Limitation of Java8 lambdas.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Damn. OK. I guess a array of booleans of size 1 is smaller than an AtomicBoolean.

public class ThreadSafeCookieStore implements CookieStore {

private Map<CookieKey, StoredCookie> cookieJar = new HashMap<>();
private ReentrantLock lock = new ReentrantLock();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the pros of this HashMap + ReentrantLock? Why not a ConcurrentHashMap?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually used ReadWriteLock first but then thought it's overkill so I replaced it with simpler class. I can use ConcurrentHashMap if you prefer that.

@unoexperto
Copy link
Contributor Author

Made minor modifications that you've requested.

public final class ThreadSafeCookieStore implements CookieStore {

private Map<CookieKey, StoredCookie> cookieJar = new HashMap<>();
private ReentrantLock lock = new ReentrantLock();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments were lost when you pushed. Could you please change to a CHM?

} else {
// rfc6265#section-5.1.4
if (!requestPath.isEmpty() && requestPath.charAt(0) == '/' && requestPath.lastIndexOf('/') > 0)
return requestPath.substring(0, requestPath.lastIndexOf('/'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not exactly my commit: you're computing lastIndexOf('/') twice :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Crap, my bad. I pushed the fix.

@unoexperto
Copy link
Contributor Author

Updated definition of remove()

@@ -151,4 +151,4 @@ public Request getRequest() {
}
}

}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please restore last empty line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done :)

@slandelle
Copy link
Contributor

Almost there :)

@slandelle
Copy link
Contributor

@unoexperto Great! One last thing: could you please squash your commit into a single one and add a commit message such as described here: https://netty.io/wiki/writing-a-commit-message.html?

@unoexperto
Copy link
Contributor Author

Took me an hour to create and apply single patch :( Not sure how to amend this PR so I create new clean one #1495

@unoexperto unoexperto closed this Jan 11, 2018
@unoexperto unoexperto deleted the cookie_store2 branch January 11, 2018 21:28
@slandelle
Copy link
Contributor

@unoexperto

  • git rebase -i HEAD~2
  • r for renaming commit
  • f for fixing it up
  • then git push -f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants