diff --git a/client/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java b/client/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java index e0f8413662..70779f34ee 100644 --- a/client/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java +++ b/client/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java @@ -22,7 +22,6 @@ import io.netty.util.Timer; import org.asynchttpclient.channel.ChannelPool; import org.asynchttpclient.channel.KeepAliveStrategy; -import org.asynchttpclient.cookie.CookieStore; import org.asynchttpclient.filter.IOExceptionFilter; import org.asynchttpclient.filter.RequestFilter; import org.asynchttpclient.filter.ResponseFilter; @@ -183,13 +182,6 @@ public interface AsyncHttpClientConfig { */ List getIoExceptionFilters(); - /** - * Return cookie store that is used to store and retrieve cookies - * - * @return {@link CookieStore} object - */ - CookieStore getCookieStore(); - /** * Return the number of time the library will retry when an {@link java.io.IOException} is throw by the remote server * diff --git a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java index 8d2c3f7ab1..cb972f7c65 100644 --- a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java +++ b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java @@ -17,7 +17,6 @@ package org.asynchttpclient; import io.netty.channel.EventLoopGroup; -import io.netty.handler.codec.http.cookie.Cookie; import io.netty.util.HashedWheelTimer; import io.netty.util.Timer; import io.netty.util.concurrent.DefaultThreadFactory; @@ -31,7 +30,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.List; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; @@ -190,22 +188,6 @@ public BoundRequestBuilder prepareRequest(RequestBuilder requestBuilder) { @Override public ListenableFuture executeRequest(Request request, AsyncHandler handler) { - if (config.getCookieStore() != null) { - try { - List cookies = config.getCookieStore().get(request.getUri()); - if (!cookies.isEmpty()) { - RequestBuilder requestBuilder = new RequestBuilder(request); - for (Cookie cookie : cookies) { - requestBuilder.addOrReplaceCookie(cookie); - } - request = requestBuilder.build(); - } - } catch (Exception e) { - handler.onThrowable(e); - return new ListenableFuture.CompletedFailure<>("Failed to set cookies of request", e); - } - } - if (noRequestFilters) { return execute(request, handler); } else { diff --git a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java index 1827728e4c..0ecf1d537c 100644 --- a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java +++ b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java @@ -25,8 +25,6 @@ import org.asynchttpclient.channel.DefaultKeepAliveStrategy; import org.asynchttpclient.channel.KeepAliveStrategy; import org.asynchttpclient.config.AsyncHttpClientConfigDefaults; -import org.asynchttpclient.cookie.CookieStore; -import org.asynchttpclient.cookie.ThreadSafeCookieStore; import org.asynchttpclient.filter.IOExceptionFilter; import org.asynchttpclient.filter.RequestFilter; import org.asynchttpclient.filter.ResponseFilter; @@ -106,9 +104,6 @@ public class DefaultAsyncHttpClientConfig implements AsyncHttpClientConfig { private final List responseFilters; private final List ioExceptionFilters; - // cookie store - private final CookieStore cookieStore; - // internals private final String threadPoolName; private final int httpClientCodecMaxInitialLineLength; @@ -185,9 +180,6 @@ private DefaultAsyncHttpClientConfig(// http List responseFilters, List ioExceptionFilters, - // cookie store - CookieStore cookieStore, - // tuning boolean tcpNoDelay, boolean soReuseAddress, @@ -272,9 +264,6 @@ private DefaultAsyncHttpClientConfig(// http this.responseFilters = responseFilters; this.ioExceptionFilters = ioExceptionFilters; - // cookie store - this.cookieStore = cookieStore; - // tuning this.tcpNoDelay = tcpNoDelay; this.soReuseAddress = soReuseAddress; @@ -537,12 +526,6 @@ public List getIoExceptionFilters() { return ioExceptionFilters; } - // cookie store - @Override - public CookieStore getCookieStore() { - return cookieStore; - } - // tuning @Override public boolean isTcpNoDelay() { @@ -713,9 +696,6 @@ public static class Builder { private SslContext sslContext; private SslEngineFactory sslEngineFactory; - // cookie store - private CookieStore cookieStore = new ThreadSafeCookieStore(); - // tuning private boolean tcpNoDelay = defaultTcpNoDelay(); private boolean soReuseAddress = defaultSoReuseAddress(); @@ -1092,12 +1072,6 @@ public Builder removeIOExceptionFilter(IOExceptionFilter ioExceptionFilter) { return this; } - // cookie store - public Builder setCookieStore(CookieStore cookieStore) { - this.cookieStore = cookieStore; - return this; - } - // tuning public Builder setTcpNoDelay(boolean tcpNoDelay) { this.tcpNoDelay = tcpNoDelay; @@ -1265,7 +1239,6 @@ public DefaultAsyncHttpClientConfig build() { requestFilters.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(requestFilters), responseFilters.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(responseFilters), ioExceptionFilters.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(ioExceptionFilters), - cookieStore, tcpNoDelay, soReuseAddress, soLinger, diff --git a/client/src/main/java/org/asynchttpclient/cookie/CookieStore.java b/client/src/main/java/org/asynchttpclient/cookie/CookieStore.java deleted file mode 100644 index 0c5ad544ed..0000000000 --- a/client/src/main/java/org/asynchttpclient/cookie/CookieStore.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2017 AsyncHttpClient Project. All rights reserved. - * - * This program is licensed to you under the Apache License Version 2.0, - * and you may not use this file except in compliance with the Apache License Version 2.0. - * You may obtain a copy of the Apache License Version 2.0 at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the Apache License Version 2.0 is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. - */ - -package org.asynchttpclient.cookie; - -import io.netty.handler.codec.http.cookie.Cookie; -import org.asynchttpclient.uri.Uri; - -import java.net.CookieManager; -import java.util.List; -import java.util.function.Predicate; - -/** - * This interface represents an abstract store for {@link Cookie} objects. - * - *

{@link CookieManager} will call {@code CookieStore.add} to save cookies - * for every incoming HTTP response, and call {@code CookieStore.get} to - * retrieve cookie for every outgoing HTTP request. A CookieStore - * is responsible for removing HttpCookie instances which have expired. - * - * @since 2.1 - */ -public interface CookieStore { - /** - * Adds one {@link Cookie} to the store. This is called for every incoming HTTP response. - * If the given cookie has already expired it will not be added, but existing values will still be removed. - * - *

A cookie to store may or may not be associated with an URI. If it - * is not associated with an URI, the cookie's domain and path attribute - * will indicate where it comes from. If it is associated with an URI and - * its domain and path attribute are not specified, given URI will indicate - * where this cookie comes from. - * - *

If a cookie corresponding to the given URI already exists, - * then it is replaced with the new one. - * - * @param uri the {@link Uri uri} this cookie associated with. if {@code null}, this cookie will not be associated with an URI - * @param cookie the {@link Cookie cookie} to be added - */ - void add(Uri uri, Cookie cookie); - - /** - * Retrieve cookies associated with given URI, or whose domain matches the given URI. Only cookies that - * have not expired are returned. This is called for every outgoing HTTP request. - * - * @param uri the {@link Uri uri} associated with the cookies to be returned - * @return an immutable list of Cookie, return empty list if no cookies match the given URI - */ - List get(Uri uri); - - /** - * Get all not-expired cookies in cookie store. - * - * @return an immutable list of http cookies; - * return empty list if there's no http cookie in store - */ - List getAll(); - - /** - * Remove a cookie from store. - * - * @param predicate that indicates what cookies to remove - * @return {@code true} if this store contained the specified cookie - * @throws NullPointerException if {@code cookie} is {@code null} - */ - boolean remove(Predicate predicate); - - /** - * Remove all cookies in this cookie store. - * - * @return true if any cookies were purged. - */ - boolean clear(); -} diff --git a/client/src/main/java/org/asynchttpclient/cookie/ThreadSafeCookieStore.java b/client/src/main/java/org/asynchttpclient/cookie/ThreadSafeCookieStore.java deleted file mode 100644 index 277db387ce..0000000000 --- a/client/src/main/java/org/asynchttpclient/cookie/ThreadSafeCookieStore.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (c) 2017 AsyncHttpClient Project. All rights reserved. - * - * This program is licensed to you under the Apache License Version 2.0, - * and you may not use this file except in compliance with the Apache License Version 2.0. - * You may obtain a copy of the Apache License Version 2.0 at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the Apache License Version 2.0 is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. - */ - -package org.asynchttpclient.cookie; - -import io.netty.handler.codec.http.cookie.Cookie; -import org.asynchttpclient.uri.Uri; -import org.asynchttpclient.util.Assertions; -import org.asynchttpclient.util.MiscUtils; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -public final class ThreadSafeCookieStore implements CookieStore { - - private Map cookieJar = new ConcurrentHashMap<>(); - - @Override - public void add(Uri uri, Cookie cookie) { - String thisRequestDomain = requestDomain(uri); - String thisRequestPath = requestPath(uri); - - add(thisRequestDomain, thisRequestPath, cookie); - } - - @Override - public List get(Uri uri) { - return get(requestDomain(uri), requestPath(uri), uri.isSecured()); - } - - @Override - public List getAll() { - final boolean[] removeExpired = {false}; - List result = cookieJar - .entrySet() - .stream() - .filter(pair -> { - boolean hasCookieExpired = hasCookieExpired(pair.getValue().cookie, pair.getValue().createdAt); - if (hasCookieExpired && !removeExpired[0]) - removeExpired[0] = true; - return !hasCookieExpired; - }) - .map(pair -> pair.getValue().cookie) - .collect(Collectors.toList()); - - if (removeExpired[0]) - removeExpired(); - - return result; - } - - @Override - public boolean remove(Predicate predicate) { - return cookieJar.entrySet().removeIf(v -> predicate.test(v.getValue().cookie)); - } - - @Override - public boolean clear() { - boolean result = !cookieJar.isEmpty(); - cookieJar.clear(); - return result; - } - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - private String requestDomain(Uri requestUri) { - return requestUri.getHost().toLowerCase(); - } - - private String requestPath(Uri requestUri) { - return requestUri.getPath().isEmpty() ? "/" : requestUri.getPath(); - } - - // rfc6265#section-5.2.3 - // Let cookie-domain be the attribute-value without the leading %x2E (".") character. - private AbstractMap.SimpleEntry cookieDomain(String cookieDomain, String requestDomain) { - if (cookieDomain != null) { - String normalizedCookieDomain = cookieDomain.toLowerCase(); - return new AbstractMap.SimpleEntry<>( - (!cookieDomain.isEmpty() && cookieDomain.charAt(0) == '.') ? - normalizedCookieDomain.substring(1) : - normalizedCookieDomain, false); - } else - return new AbstractMap.SimpleEntry<>(requestDomain, true); - } - - // rfc6265#section-5.2.4 - private String cookiePath(String rawCookiePath, String requestPath) { - if (MiscUtils.isNonEmpty(rawCookiePath) && rawCookiePath.charAt(0) == '/') { - return rawCookiePath; - } else { - // rfc6265#section-5.1.4 - int indexOfLastSlash = requestPath.lastIndexOf('/'); - if (!requestPath.isEmpty() && requestPath.charAt(0) == '/' && indexOfLastSlash > 0) - return requestPath.substring(0, indexOfLastSlash); - else - return "/"; - } - } - - private boolean hasCookieExpired(Cookie cookie, long whenCreated) { - // if not specify max-age, this cookie should be discarded when user agent is to be closed, but it is not expired. - if (cookie.maxAge() == Cookie.UNDEFINED_MAX_AGE) - return false; - - if (cookie.maxAge() <= 0) - return true; - - if (whenCreated > 0) { - long deltaSecond = (System.currentTimeMillis() - whenCreated) / 1000; - return deltaSecond > cookie.maxAge(); - } else - return false; - } - - // rfc6265#section-5.1.3 - // check "The string is a host name (i.e., not an IP address)" ignored - private boolean domainsMatch(String cookieDomain, String requestDomain, boolean hostOnly) { - return (hostOnly && Objects.equals(requestDomain, cookieDomain)) || - (Objects.equals(requestDomain, cookieDomain) || requestDomain.endsWith("." + cookieDomain)); - } - - // rfc6265#section-5.1.4 - private boolean pathsMatch(String cookiePath, String requestPath) { - return Objects.equals(cookiePath, requestPath) || - (requestPath.startsWith(cookiePath) && (cookiePath.charAt(cookiePath.length() - 1) == '/' || requestPath.charAt(cookiePath.length()) == '/')); - } - - private void add(String requestDomain, String requestPath, Cookie cookie) { - - AbstractMap.SimpleEntry pair = cookieDomain(cookie.domain(), requestDomain); - String keyDomain = pair.getKey(); - boolean hostOnly = pair.getValue(); - String keyPath = cookiePath(cookie.path(), requestPath); - CookieKey key = new CookieKey(cookie.name().toLowerCase(), keyDomain, keyPath); - - if (hasCookieExpired(cookie, 0)) - cookieJar.remove(key); - else - cookieJar.put(key, new StoredCookie(cookie, hostOnly, cookie.maxAge() != Cookie.UNDEFINED_MAX_AGE)); - } - - private List get(String domain, String path, boolean secure) { - - final boolean[] removeExpired = {false}; - - List result = cookieJar.entrySet().stream().filter(pair -> { - CookieKey key = pair.getKey(); - StoredCookie storedCookie = pair.getValue(); - boolean hasCookieExpired = hasCookieExpired(storedCookie.cookie, storedCookie.createdAt); - if (hasCookieExpired && !removeExpired[0]) - removeExpired[0] = true; - return !hasCookieExpired && domainsMatch(key.domain, domain, storedCookie.hostOnly) && pathsMatch(key.path, path) && (secure || !storedCookie.cookie.isSecure()); - }).map(v -> v.getValue().cookie).collect(Collectors.toList()); - - if (removeExpired[0]) - removeExpired(); - - return result; - } - - private void removeExpired() { - cookieJar.entrySet().removeIf(v -> hasCookieExpired(v.getValue().cookie, v.getValue().createdAt)); - } - - private static class CookieKey implements Comparable { - final String name; - final String domain; - final String path; - - CookieKey(String name, String domain, String path) { - this.name = name; - this.domain = domain; - this.path = path; - } - - @Override - public int compareTo(CookieKey o) { - Assertions.assertNotNull(o, "Parameter can't be null"); - int result; - if ((result = this.name.compareTo(o.name)) == 0) - if ((result = this.domain.compareTo(o.domain)) == 0) - result = this.path.compareTo(o.path); - - return result; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof CookieKey && this.compareTo((CookieKey) obj) == 0; - } - - @Override - public int hashCode() { - int result = 17; - result = 31 * result + name.hashCode(); - result = 31 * result + domain.hashCode(); - result = 31 * result + path.hashCode(); - return result; - } - - @Override - public String toString() { - return String.format("%s: %s; %s", name, domain, path); - } - } - - private static class StoredCookie { - final Cookie cookie; - final boolean hostOnly; - final boolean persistent; - final long createdAt = System.currentTimeMillis(); - - StoredCookie(Cookie cookie, boolean hostOnly, boolean persistent) { - this.cookie = cookie; - this.hostOnly = hostOnly; - this.persistent = persistent; - } - - @Override - public String toString() { - return String.format("%s; hostOnly %s; persistent %s", cookie.toString(), hostOnly, persistent); - } - } -} diff --git a/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Interceptors.java b/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Interceptors.java index 134213f60a..095f71cec3 100644 --- a/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Interceptors.java +++ b/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Interceptors.java @@ -18,16 +18,12 @@ import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.cookie.ClientCookieDecoder; -import io.netty.handler.codec.http.cookie.Cookie; import org.asynchttpclient.*; -import org.asynchttpclient.cookie.CookieStore; import org.asynchttpclient.netty.NettyResponseFuture; import org.asynchttpclient.netty.channel.ChannelManager; import org.asynchttpclient.netty.request.NettyRequestSender; import org.asynchttpclient.proxy.ProxyServer; -import static io.netty.handler.codec.http.HttpHeaderNames.SET_COOKIE; import static org.asynchttpclient.util.HttpConstants.ResponseStatusCodes.*; public class Interceptors { @@ -40,7 +36,6 @@ public class Interceptors { private final ConnectSuccessInterceptor connectSuccessInterceptor; private final ResponseFiltersInterceptor responseFiltersInterceptor; private final boolean hasResponseFilters; - private final ClientCookieDecoder cookieDecoder; public Interceptors(AsyncHttpClientConfig config, ChannelManager channelManager, @@ -53,7 +48,6 @@ public Interceptors(AsyncHttpClientConfig config, connectSuccessInterceptor = new ConnectSuccessInterceptor(channelManager, requestSender); responseFiltersInterceptor = new ResponseFiltersInterceptor(config, requestSender); hasResponseFilters = !config.getResponseFilters().isEmpty(); - cookieDecoder = config.isUseLaxCookieEncoder() ? ClientCookieDecoder.LAX : ClientCookieDecoder.STRICT; } public boolean exitAfterIntercept(Channel channel, @@ -69,18 +63,6 @@ public boolean exitAfterIntercept(Channel channel, Request request = future.getCurrentRequest(); Realm realm = request.getRealm() != null ? request.getRealm() : config.getRealm(); - // This MUST BE called before Redirect30xInterceptor because latter assumes cookie store is already updated - CookieStore cookieStore = config.getCookieStore(); - if (cookieStore != null) { - for (String cookieStr : responseHeaders.getAll(SET_COOKIE)) { - Cookie c = cookieDecoder.decode(cookieStr); - if (c != null) { - // Set-Cookie header could be invalid/malformed - cookieStore.add(future.getCurrentRequest().getUri(), c); - } - } - } - if (hasResponseFilters && responseFiltersInterceptor.exitAfterProcessingFilters(channel, future, handler, status, responseHeaders)) { return true; } diff --git a/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Redirect30xInterceptor.java b/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Redirect30xInterceptor.java index 121bb71658..9b2b3ac697 100644 --- a/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Redirect30xInterceptor.java +++ b/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Redirect30xInterceptor.java @@ -17,13 +17,13 @@ import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpUtil; +import io.netty.handler.codec.http.cookie.ClientCookieDecoder; import io.netty.handler.codec.http.cookie.Cookie; import org.asynchttpclient.AsyncHttpClientConfig; import org.asynchttpclient.Realm; import org.asynchttpclient.Realm.AuthScheme; import org.asynchttpclient.Request; import org.asynchttpclient.RequestBuilder; -import org.asynchttpclient.cookie.CookieStore; import org.asynchttpclient.handler.MaxRedirectException; import org.asynchttpclient.netty.NettyResponseFuture; import org.asynchttpclient.netty.channel.ChannelManager; @@ -33,7 +33,6 @@ import org.slf4j.LoggerFactory; import java.util.HashSet; -import java.util.List; import java.util.Set; import static io.netty.handler.codec.http.HttpHeaderNames.*; @@ -60,11 +59,13 @@ public class Redirect30xInterceptor { private final AsyncHttpClientConfig config; private final NettyRequestSender requestSender; private final MaxRedirectException maxRedirectException; + private final ClientCookieDecoder cookieDecoder; Redirect30xInterceptor(ChannelManager channelManager, AsyncHttpClientConfig config, NettyRequestSender requestSender) { this.channelManager = channelManager; this.config = config; this.requestSender = requestSender; + this.cookieDecoder = config.isUseLaxCookieEncoder() ? ClientCookieDecoder.LAX : ClientCookieDecoder.STRICT; maxRedirectException = unknownStackTrace(new MaxRedirectException("Maximum redirect reached: " + config.getMaxRedirects()), Redirect30xInterceptor.class, "exitAfterHandlingRedirect"); } @@ -91,6 +92,7 @@ public boolean exitAfterHandlingRedirect(Channel channel, boolean keepBody = statusCode == TEMPORARY_REDIRECT_307 || statusCode == PERMANENT_REDIRECT_308 || (statusCode == FOUND_302 && config.isStrict302Handling()); final RequestBuilder requestBuilder = new RequestBuilder(switchToGet ? GET : originalMethod) + .setCookies(request.getCookies()) .setChannelPoolPartitioning(request.getChannelPoolPartitioning()) .setFollowRedirect(true) .setLocalAddress(request.getLocalAddress()) @@ -125,14 +127,12 @@ else if (request.getBodyGenerator() != null) Uri newUri = Uri.create(future.getUri(), location); LOGGER.debug("Redirecting to {}", newUri); - - CookieStore cookieStore = config.getCookieStore(); - if (cookieStore != null) { - // Update request's cookies assuming that cookie store is already updated by Interceptors - List cookies = cookieStore.get(newUri); - if (!cookies.isEmpty()) - for (Cookie cookie : cookies) - requestBuilder.addOrReplaceCookie(cookie); + + for (String cookieStr : responseHeaders.getAll(SET_COOKIE)) { + Cookie c = cookieDecoder.decode(cookieStr); + if (c != null) { + requestBuilder.addOrReplaceCookie(c); + } } boolean sameBase = request.getUri().isSameBase(newUri); diff --git a/client/src/test/java/org/asynchttpclient/CookieStoreTest.java b/client/src/test/java/org/asynchttpclient/CookieStoreTest.java deleted file mode 100644 index e16a477c25..0000000000 --- a/client/src/test/java/org/asynchttpclient/CookieStoreTest.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (c) 2017 AsyncHttpClient Project. All rights reserved. - * - * This program is licensed to you under the Apache License Version 2.0, - * and you may not use this file except in compliance with the Apache License Version 2.0. - * You may obtain a copy of the Apache License Version 2.0 at - * http://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the Apache License Version 2.0 is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. - */ - -package org.asynchttpclient; - -import io.netty.handler.codec.http.cookie.ClientCookieDecoder; -import io.netty.handler.codec.http.cookie.ClientCookieEncoder; -import io.netty.handler.codec.http.cookie.Cookie; -import org.asynchttpclient.cookie.CookieStore; -import org.asynchttpclient.cookie.ThreadSafeCookieStore; -import org.asynchttpclient.uri.Uri; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Test; - -import java.util.List; - -import static org.testng.Assert.assertTrue; - -public class CookieStoreTest { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - @BeforeClass(alwaysRun = true) - public void setUpGlobal() { - logger.info("Local HTTP server started successfully"); - System.out.println("--Start"); - } - - @AfterClass(alwaysRun = true) - public void tearDownGlobal() { - System.out.println("--Stop"); - } - - @Test - public void runAllSequentiallyBecauseNotThreadSafe() { - addCookieWithEmptyPath(); - dontReturnCookieForAnotherDomain(); - returnCookieWhenItWasSetOnSamePath(); - returnCookieWhenItWasSetOnParentPath(); - dontReturnCookieWhenDomainMatchesButPathIsDifferent(); - dontReturnCookieWhenDomainMatchesButPathIsParent(); - returnCookieWhenDomainMatchesAndPathIsChild(); - returnCookieWhenItWasSetOnSubdomain(); - replaceCookieWhenSetOnSameDomainAndPath(); - dontReplaceCookiesWhenTheyHaveDifferentName(); - expireCookieWhenSetWithDateInThePast(); - cookieWithSameNameMustCoexistIfSetOnDifferentDomains(); - handleMissingDomainAsRequestHost(); - handleMissingPathAsSlash(); - returnTheCookieWheniTSissuedFromRequestWithSubpath(); - handleMissingPathAsRequestPathWhenFromRootDir(); - handleMissingPathAsRequestPathWhenPathIsNotEmpty(); - handleDomainInCaseInsensitiveManner(); - handleCookieNameInCaseInsensitiveManner(); - handleCookiePathInCaseSensitiveManner(); - ignoreQueryParametersInUri(); - shouldServerOnSubdomainWhenDomainMatches(); - replaceCookieWhenSetOnSamePathBySameUri(); - handleMultipleCookieOfSameNameOnDifferentPaths(); - handleTrailingSlashesInPaths(); - returnMultipleCookiesEvenIfTheyHaveSameName(); - shouldServeCookiesBasedOnTheUriScheme(); - shouldAlsoServeNonSecureCookiesBasedOnTheUriScheme(); - shouldNotServeSecureCookiesForDefaultRetrievedHttpUriScheme(); - shouldServeSecureCookiesForSpecificallyRetrievedHttpUriScheme(); - } - - private void addCookieWithEmptyPath() { - CookieStore store = new ThreadSafeCookieStore(); - Uri uri = Uri.create("http://www.foo.com"); - store.add(uri, ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; path=")); - assertTrue(store.get(uri).size() > 0); - } - - private void dontReturnCookieForAnotherDomain() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("http://www.foo.com"), ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; path=")); - assertTrue(store.get(Uri.create("http://www.bar.com")).isEmpty()); - } - - private void returnCookieWhenItWasSetOnSamePath() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("http://www.foo.com"), ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; path=/bar/")); - assertTrue(store.get(Uri.create("http://www.foo.com/bar/")).size() == 1); - } - - private void returnCookieWhenItWasSetOnParentPath() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("http://www.foo.com"), ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; Domain=www.foo.com; path=/bar")); - assertTrue(store.get(Uri.create("http://www.foo.com/bar/baz")).size() == 1); - } - - private void dontReturnCookieWhenDomainMatchesButPathIsDifferent() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("http://www.foo.com/bar"), ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; Domain=www.foo.com; path=/bar")); - assertTrue(store.get(Uri.create("http://www.foo.com/baz")).isEmpty()); - } - - private void dontReturnCookieWhenDomainMatchesButPathIsParent() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("http://www.foo.com/bar"), ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; Domain=www.foo.com; path=/bar")); - assertTrue(store.get(Uri.create("http://www.foo.com")).isEmpty()); - } - - private void returnCookieWhenDomainMatchesAndPathIsChild() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("http://www.foo.com/bar"), ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; Domain=www.foo.com; path=/bar")); - assertTrue(store.get(Uri.create("http://www.foo.com/bar/baz")).size() == 1); - } - - private void returnCookieWhenItWasSetOnSubdomain() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("http://www.foo.com"), ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; Domain=.foo.com")); - assertTrue(store.get(Uri.create("http://bar.foo.com")).size() == 1); - } - - private void replaceCookieWhenSetOnSameDomainAndPath() { - CookieStore store = new ThreadSafeCookieStore(); - Uri uri = Uri.create("http://www.foo.com/bar/baz"); - store.add(uri, ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; Domain=www.foo.com; path=/bar")); - store.add(uri, ClientCookieDecoder.LAX.decode("ALPHA=VALUE2; Domain=www.foo.com; path=/bar")); - assertTrue(store.getAll().size() == 1); - assertTrue(store.get(uri).get(0).value().equals("VALUE2")); - } - - private void dontReplaceCookiesWhenTheyHaveDifferentName() { - CookieStore store = new ThreadSafeCookieStore(); - Uri uri = Uri.create("http://www.foo.com/bar/baz"); - store.add(uri, ClientCookieDecoder.LAX.decode("BETA=VALUE1; Domain=www.foo.com; path=/bar")); - store.add(uri, ClientCookieDecoder.LAX.decode("ALPHA=VALUE2; Domain=www.foo.com; path=/bar")); - assertTrue(store.get(uri).size() == 2); - } - - private void expireCookieWhenSetWithDateInThePast() { - CookieStore store = new ThreadSafeCookieStore(); - Uri uri = Uri.create("http://www.foo.com/bar"); - store.add(uri, ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; Domain=www.foo.com; path=/bar")); - store.add(uri, ClientCookieDecoder.LAX.decode("ALPHA=EXPIRED; Domain=www.foo.com; Path=/bar; Expires=Sun, 06 Nov 1994 08:49:37 GMT")); - assertTrue(store.getAll().isEmpty()); - } - - private void cookieWithSameNameMustCoexistIfSetOnDifferentDomains() { - CookieStore store = new ThreadSafeCookieStore(); - Uri uri1 = Uri.create("http://www.foo.com"); - store.add(uri1, ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; Domain=www.foo.com")); - Uri uri2 = Uri.create("http://www.bar.com"); - store.add(uri2, ClientCookieDecoder.LAX.decode("ALPHA=VALUE2; Domain=www.bar.com")); - - assertTrue(store.get(uri1).size() == 1); - assertTrue(store.get(uri1).get(0).value().equals("VALUE1")); - - assertTrue(store.get(uri2).size() == 1); - assertTrue(store.get(uri2).get(0).value().equals("VALUE2")); - } - - private void handleMissingDomainAsRequestHost() { - CookieStore store = new ThreadSafeCookieStore(); - Uri uri = Uri.create("http://www.foo.com"); - store.add(uri, ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; Path=/")); - assertTrue(store.get(uri).size() == 1); - } - - private void handleMissingPathAsSlash() { - CookieStore store = new ThreadSafeCookieStore(); - Uri uri = Uri.create("http://www.foo.com"); - store.add(uri, ClientCookieDecoder.LAX.decode("tooe_token=0b1d81dd02d207491a6e9b0a2af9470da9eb1dad")); - assertTrue(store.get(uri).size() == 1); - } - - private void returnTheCookieWheniTSissuedFromRequestWithSubpath() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("http://www.foo.com/bar"), ClientCookieDecoder.LAX.decode("ALPHA=VALUE; path=/")); - assertTrue(store.get(Uri.create("http://www.foo.com")).size() == 1); - } - - private void handleMissingPathAsRequestPathWhenFromRootDir() { - CookieStore store = new ThreadSafeCookieStore(); - Uri uri = Uri.create("http://www.foo.com"); - store.add(uri, ClientCookieDecoder.LAX.decode("ALPHA=VALUE1")); - assertTrue(store.get(uri).size() == 1); - } - - private void handleMissingPathAsRequestPathWhenPathIsNotEmpty() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("http://www.foo.com/bar"), ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; Domain=www.foo.com; path=/bar")); - assertTrue(store.get(Uri.create("http://www.foo.com/baz")).isEmpty()); - } - - // RFC 2965 sec. 3.3.3 - private void handleDomainInCaseInsensitiveManner() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("http://www.foo.com/bar"), ClientCookieDecoder.LAX.decode("ALPHA=VALUE1")); - assertTrue(store.get(Uri.create("http://www.FoO.com/bar")).size() == 1); - } - - // RFC 2965 sec. 3.3.3 - private void handleCookieNameInCaseInsensitiveManner() { - CookieStore store = new ThreadSafeCookieStore(); - Uri uri = Uri.create("http://www.foo.com/bar/baz"); - store.add(uri, ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; Domain=www.foo.com; path=/bar")); - store.add(uri, ClientCookieDecoder.LAX.decode("alpha=VALUE2; Domain=www.foo.com; path=/bar")); - assertTrue(store.getAll().size() == 1); - assertTrue(store.get(uri).get(0).value().equals("VALUE2")); - } - - // RFC 2965 sec. 3.3.3 - private void handleCookiePathInCaseSensitiveManner() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("http://www.foo.com/foo/bar"), ClientCookieDecoder.LAX.decode("ALPHA=VALUE1")); - assertTrue(store.get(Uri.create("http://www.FoO.com/Foo/bAr")).isEmpty()); - } - - private void ignoreQueryParametersInUri() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("http://www.foo.com/bar?query1"), ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; Domain=www.foo.com; path=/")); - assertTrue(store.get(Uri.create("http://www.foo.com/bar?query2")).size() == 1); - } - - // RFC 6265, 5.1.3. Domain Matching - private void shouldServerOnSubdomainWhenDomainMatches() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("https://x.foo.org/"), ClientCookieDecoder.LAX.decode("cookie1=VALUE1; Path=/; Domain=foo.org;")); - assertTrue(store.get(Uri.create("https://y.x.foo.org/")).size() == 1); - } - - // NOTE: Similar to replaceCookieWhenSetOnSameDomainAndPath() - private void replaceCookieWhenSetOnSamePathBySameUri() { - CookieStore store = new ThreadSafeCookieStore(); - Uri uri = Uri.create("https://foo.org/"); - store.add(uri, ClientCookieDecoder.LAX.decode("cookie1=VALUE1; Path=/")); - store.add(uri, ClientCookieDecoder.LAX.decode("cookie1=VALUE2; Path=/")); - store.add(uri, ClientCookieDecoder.LAX.decode("cookie1=VALUE3; Path=/")); - assertTrue(store.getAll().size() == 1); - assertTrue(store.get(uri).get(0).value().equals("VALUE3")); - } - - private void handleMultipleCookieOfSameNameOnDifferentPaths() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("http://www.foo.com"), ClientCookieDecoder.LAX.decode("cookie=VALUE0; path=/")); - store.add(Uri.create("http://www.foo.com/foo/bar"), ClientCookieDecoder.LAX.decode("cookie=VALUE1; path=/foo/bar/")); - store.add(Uri.create("http://www.foo.com/foo/baz"), ClientCookieDecoder.LAX.decode("cookie=VALUE2; path=/foo/baz/")); - - Uri uri1 = Uri.create("http://www.foo.com/foo/bar/"); - List cookies1 = store.get(uri1); - assertTrue(cookies1.size() == 2); - assertTrue(cookies1.stream().filter(c -> c.value().equals("VALUE0") || c.value().equals("VALUE1")).count() == 2); - - Uri uri2 = Uri.create("http://www.foo.com/foo/baz/"); - List cookies2 = store.get(uri2); - assertTrue(cookies2.size() == 2); - assertTrue(cookies2.stream().filter(c -> c.value().equals("VALUE0") || c.value().equals("VALUE2")).count() == 2); - } - - private void handleTrailingSlashesInPaths() { - CookieStore store = new ThreadSafeCookieStore(); - store.add( - Uri.create("https://vagrant.moolb.com/app/consumer/j_spring_cas_security_check?ticket=ST-5-Q7gzqPpvG3N3Bb02bm3q-llinder-vagrantmgr.moolb.com"), - ClientCookieDecoder.LAX.decode("JSESSIONID=211D17F016132BCBD31D9ABB31D90960; Path=/app/consumer/; HttpOnly")); - assertTrue(store.getAll().size() == 1); - assertTrue(store.get(Uri.create("https://vagrant.moolb.com/app/consumer/")).get(0).value().equals("211D17F016132BCBD31D9ABB31D90960")); - } - - private void returnMultipleCookiesEvenIfTheyHaveSameName() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("http://foo.com"), ClientCookieDecoder.LAX.decode("JSESSIONID=FOO; Domain=.foo.com")); - store.add(Uri.create("http://sub.foo.com"), ClientCookieDecoder.LAX.decode("JSESSIONID=BAR; Domain=sub.foo.com")); - - Uri uri1 = Uri.create("http://sub.foo.com"); - List cookies1 = store.get(uri1); - assertTrue(cookies1.size() == 2); - assertTrue(cookies1.stream().filter(c -> c.value().equals("FOO") || c.value().equals("BAR")).count() == 2); - - String result = ClientCookieEncoder.LAX.encode(cookies1.get(0), cookies1.get(1)); - assertTrue(result.equals("JSESSIONID=FOO; JSESSIONID=BAR")); - } - - // rfc6265#section-1 Cookies for a given host are shared across all the ports on that host - private void shouldServeCookiesBasedOnTheUriScheme() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("https://foo.org/moodle/"), ClientCookieDecoder.LAX.decode("cookie1=VALUE1; Path=/")); - store.add(Uri.create("https://foo.org:443/moodle/login"), ClientCookieDecoder.LAX.decode("cookie1=VALUE2; Path=/")); - store.add(Uri.create("https://foo.org:443/moodle/login"), ClientCookieDecoder.LAX.decode("cookie1=VALUE3; Path=/; Secure")); - - Uri uri = Uri.create("https://foo.org/moodle/login"); - assertTrue(store.getAll().size() == 1); - assertTrue(store.get(uri).get(0).value().equals("VALUE3")); - assertTrue(store.get(uri).get(0).isSecure()); - } - - // rfc6265#section-1 Cookies for a given host are shared across all the ports on that host - private void shouldAlsoServeNonSecureCookiesBasedOnTheUriScheme() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("https://foo.org/moodle/"), ClientCookieDecoder.LAX.decode("cookie1=VALUE1; Path=/")); - store.add(Uri.create("https://foo.org:443/moodle/login"), ClientCookieDecoder.LAX.decode("cookie1=VALUE2; Path=/")); - store.add(Uri.create("https://foo.org:443/moodle/login"), ClientCookieDecoder.LAX.decode("cookie1=VALUE3; Path=/; HttpOnly")); - - Uri uri = Uri.create("https://foo.org/moodle/login"); - assertTrue(store.getAll().size() == 1); - assertTrue(store.get(uri).get(0).value().equals("VALUE3")); - assertTrue(!store.get(uri).get(0).isSecure()); - } - - // rfc6265#section-1 Cookies for a given host are shared across all the ports on that host - private void shouldNotServeSecureCookiesForDefaultRetrievedHttpUriScheme() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("https://foo.org/moodle/"), ClientCookieDecoder.LAX.decode("cookie1=VALUE1; Path=/")); - store.add(Uri.create("https://foo.org:443/moodle/login"), ClientCookieDecoder.LAX.decode("cookie1=VALUE2; Path=/")); - store.add(Uri.create("https://foo.org:443/moodle/login"), ClientCookieDecoder.LAX.decode("cookie1=VALUE3; Path=/; Secure")); - - Uri uri = Uri.create("http://foo.org/moodle/login"); - assertTrue(store.get(uri).isEmpty()); - } - - // rfc6265#section-1 Cookies for a given host are shared across all the ports on that host - private void shouldServeSecureCookiesForSpecificallyRetrievedHttpUriScheme() { - CookieStore store = new ThreadSafeCookieStore(); - store.add(Uri.create("https://foo.org/moodle/"), ClientCookieDecoder.LAX.decode("cookie1=VALUE1; Path=/")); - store.add(Uri.create("https://foo.org:443/moodle/login"), ClientCookieDecoder.LAX.decode("cookie1=VALUE2; Path=/")); - store.add(Uri.create("https://foo.org:443/moodle/login"), ClientCookieDecoder.LAX.decode("cookie1=VALUE3; Path=/; Secure")); - - Uri uri = Uri.create("https://foo.org/moodle/login"); - assertTrue(store.get(uri).size() == 1); - assertTrue(store.get(uri).get(0).value().equals("VALUE3")); - assertTrue(store.get(uri).get(0).isSecure()); - } -} diff --git a/extras/typesafeconfig/src/main/java/org/asynchttpclient/extras/typesafeconfig/AsyncHttpClientTypesafeConfig.java b/extras/typesafeconfig/src/main/java/org/asynchttpclient/extras/typesafeconfig/AsyncHttpClientTypesafeConfig.java index 8917052611..bfd741e1bc 100644 --- a/extras/typesafeconfig/src/main/java/org/asynchttpclient/extras/typesafeconfig/AsyncHttpClientTypesafeConfig.java +++ b/extras/typesafeconfig/src/main/java/org/asynchttpclient/extras/typesafeconfig/AsyncHttpClientTypesafeConfig.java @@ -26,8 +26,6 @@ import org.asynchttpclient.channel.DefaultKeepAliveStrategy; import org.asynchttpclient.channel.KeepAliveStrategy; import org.asynchttpclient.config.AsyncHttpClientConfigDefaults; -import org.asynchttpclient.cookie.CookieStore; -import org.asynchttpclient.cookie.ThreadSafeCookieStore; import org.asynchttpclient.filter.IOExceptionFilter; import org.asynchttpclient.filter.RequestFilter; import org.asynchttpclient.filter.ResponseFilter; @@ -154,11 +152,6 @@ public List getIoExceptionFilters() { return new LinkedList<>(); } - @Override - public CookieStore getCookieStore() { - return new ThreadSafeCookieStore(); - } - @Override public int getMaxRequestRetry() { return getIntegerOpt(MAX_REQUEST_RETRY_CONFIG).orElse(defaultMaxRequestRetry());