From a6d659ea0cc11fa5131304d8a04a7ba89c7a66af Mon Sep 17 00:00:00 2001 From: Stephane Landelle Date: Thu, 5 Jan 2017 15:43:30 +0100 Subject: [PATCH] Drop own Cookie in favor of Netty's one, close #1297 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: AHC’s fast Cookie parser has been contributed to Netty. Let’s drop our own implementation so: * I don’t have to maintain both implementations * people using Netty as HTTP server don’t have so many implementations to deal with. Modifications: *Drop AHC’s Cookie, CookieDecoder and CookieEncoder Result: Less code to maintain, one single implementation around for Netty + AHC users --- .../org/asynchttpclient/DefaultRequest.java | 2 +- .../java/org/asynchttpclient/Request.java | 2 +- .../asynchttpclient/RequestBuilderBase.java | 6 +- .../java/org/asynchttpclient/Response.java | 2 +- .../org/asynchttpclient/cookie/Cookie.java | 105 ----- .../asynchttpclient/cookie/CookieDecoder.java | 277 ------------ .../asynchttpclient/cookie/CookieEncoder.java | 94 ---- .../asynchttpclient/cookie/CookieUtil.java | 137 ------ .../cookie/HttpHeaderDateFormatter.java | 416 ------------------ .../asynchttpclient/netty/NettyResponse.java | 6 +- .../intercept/Redirect30xInterceptor.java | 6 +- .../netty/request/NettyRequestFactory.java | 4 +- .../webdav/WebDavResponse.java | 2 +- .../org/asynchttpclient/BasicHttpTest.java | 8 +- .../org/asynchttpclient/RemoteSiteTest.java | 9 +- .../asynchttpclient/RequestBuilderTest.java | 37 +- .../cookie/CookieDecoderTest.java | 80 ---- .../cookie/HttpHeaderDateFormatterTest.java | 62 --- .../netty/NettyAsyncResponseTest.java | 8 +- .../extras/simple/SimpleAsyncHttpClient.java | 2 +- pom.xml | 15 +- 21 files changed, 75 insertions(+), 1205 deletions(-) delete mode 100644 client/src/main/java/org/asynchttpclient/cookie/Cookie.java delete mode 100644 client/src/main/java/org/asynchttpclient/cookie/CookieDecoder.java delete mode 100644 client/src/main/java/org/asynchttpclient/cookie/CookieEncoder.java delete mode 100644 client/src/main/java/org/asynchttpclient/cookie/CookieUtil.java delete mode 100644 client/src/main/java/org/asynchttpclient/cookie/HttpHeaderDateFormatter.java delete mode 100644 client/src/test/java/org/asynchttpclient/cookie/CookieDecoderTest.java delete mode 100644 client/src/test/java/org/asynchttpclient/cookie/HttpHeaderDateFormatterTest.java diff --git a/client/src/main/java/org/asynchttpclient/DefaultRequest.java b/client/src/main/java/org/asynchttpclient/DefaultRequest.java index fc6f21c477..bbc8540902 100644 --- a/client/src/main/java/org/asynchttpclient/DefaultRequest.java +++ b/client/src/main/java/org/asynchttpclient/DefaultRequest.java @@ -15,6 +15,7 @@ import static org.asynchttpclient.util.MiscUtils.isNonEmpty; import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.cookie.Cookie; import io.netty.resolver.NameResolver; import java.io.File; @@ -28,7 +29,6 @@ import java.util.Map; import org.asynchttpclient.channel.ChannelPoolPartitioning; -import org.asynchttpclient.cookie.Cookie; import org.asynchttpclient.proxy.ProxyServer; import org.asynchttpclient.request.body.generator.BodyGenerator; import org.asynchttpclient.request.body.multipart.Part; diff --git a/client/src/main/java/org/asynchttpclient/Request.java b/client/src/main/java/org/asynchttpclient/Request.java index 3afffaaf04..9aab60469e 100644 --- a/client/src/main/java/org/asynchttpclient/Request.java +++ b/client/src/main/java/org/asynchttpclient/Request.java @@ -17,6 +17,7 @@ package org.asynchttpclient; import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.cookie.Cookie; import io.netty.resolver.NameResolver; import java.io.File; @@ -27,7 +28,6 @@ import java.util.List; import org.asynchttpclient.channel.ChannelPoolPartitioning; -import org.asynchttpclient.cookie.Cookie; import org.asynchttpclient.proxy.ProxyServer; import org.asynchttpclient.request.body.generator.BodyGenerator; import org.asynchttpclient.request.body.multipart.Part; diff --git a/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java b/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java index e69f57bb7e..0e809c0016 100644 --- a/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java +++ b/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java @@ -20,6 +20,7 @@ import static org.asynchttpclient.util.MiscUtils.isNonEmpty; import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.cookie.Cookie; import io.netty.resolver.DefaultNameResolver; import io.netty.resolver.NameResolver; import io.netty.util.concurrent.ImmediateEventExecutor; @@ -36,7 +37,6 @@ import java.util.Map; import org.asynchttpclient.channel.ChannelPoolPartitioning; -import org.asynchttpclient.cookie.Cookie; import org.asynchttpclient.proxy.ProxyServer; import org.asynchttpclient.request.body.generator.BodyGenerator; import org.asynchttpclient.request.body.generator.ReactiveStreamsBodyGenerator; @@ -290,12 +290,12 @@ public T addCookie(Cookie cookie) { } public T addOrReplaceCookie(Cookie cookie) { - String cookieKey = cookie.getName(); + String cookieKey = cookie.name(); boolean replace = false; int index = 0; lazyInitCookies(); for (Cookie c : this.cookies) { - if (c.getName().equals(cookieKey)) { + if (c.name().equals(cookieKey)) { replace = true; break; } diff --git a/client/src/main/java/org/asynchttpclient/Response.java b/client/src/main/java/org/asynchttpclient/Response.java index e17961df5d..b92083a89b 100644 --- a/client/src/main/java/org/asynchttpclient/Response.java +++ b/client/src/main/java/org/asynchttpclient/Response.java @@ -17,6 +17,7 @@ package org.asynchttpclient; import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.cookie.Cookie; import java.io.InputStream; import java.net.SocketAddress; @@ -25,7 +26,6 @@ import java.util.ArrayList; import java.util.List; -import org.asynchttpclient.cookie.Cookie; import org.asynchttpclient.netty.NettyResponse; import org.asynchttpclient.uri.Uri; diff --git a/client/src/main/java/org/asynchttpclient/cookie/Cookie.java b/client/src/main/java/org/asynchttpclient/cookie/Cookie.java deleted file mode 100644 index b08e9de2b2..0000000000 --- a/client/src/main/java/org/asynchttpclient/cookie/Cookie.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2014 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 static org.asynchttpclient.cookie.CookieUtil.*; - -public class Cookie { - - public static Cookie newValidCookie(String name, String value, boolean wrap, String domain, String path, long maxAge, boolean secure, boolean httpOnly) { - return new Cookie(validateCookieName(name), validateCookieValue(value), wrap, validateCookieAttribute("domain", domain), validateCookieAttribute("path", path), maxAge, secure, httpOnly); - } - - private final String name; - private final String value; - private final boolean wrap; - private final String domain; - private final String path; - private final long maxAge; - private final boolean secure; - private final boolean httpOnly; - - public Cookie(String name, String value, boolean wrap, String domain, String path, long maxAge, boolean secure, boolean httpOnly) { - this.name = name; - this.value = value; - this.wrap = wrap; - this.domain = domain; - this.path = path; - this.maxAge = maxAge; - this.secure = secure; - this.httpOnly = httpOnly; - } - - public String getDomain() { - return domain; - } - - public String getName() { - return name; - } - - public String getValue() { - return value; - } - - public boolean isWrap() { - return wrap; - } - - public String getPath() { - return path; - } - - public long getMaxAge() { - return maxAge; - } - - public boolean isSecure() { - return secure; - } - - public boolean isHttpOnly() { - return httpOnly; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(); - buf.append(name); - buf.append('='); - if (wrap) - buf.append('"').append(value).append('"'); - else - buf.append(value); - if (domain != null) { - buf.append("; domain="); - buf.append(domain); - } - if (path != null) { - buf.append("; path="); - buf.append(path); - } - if (maxAge >= 0) { - buf.append("; maxAge="); - buf.append(maxAge); - buf.append('s'); - } - if (secure) { - buf.append("; secure"); - } - if (httpOnly) { - buf.append("; HTTPOnly"); - } - return buf.toString(); - } -} diff --git a/client/src/main/java/org/asynchttpclient/cookie/CookieDecoder.java b/client/src/main/java/org/asynchttpclient/cookie/CookieDecoder.java deleted file mode 100644 index b86097037d..0000000000 --- a/client/src/main/java/org/asynchttpclient/cookie/CookieDecoder.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (c) 2014 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 static org.asynchttpclient.util.Assertions.*; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.nio.CharBuffer; -import java.util.Date; - -import static org.asynchttpclient.cookie.CookieUtil.*; - -public class CookieDecoder { - - private static final Logger LOGGER = LoggerFactory.getLogger(CookieDecoder.class); - - /** - * Decodes the specified HTTP header value into {@link Cookie}. - * - * @param header the Set-Cookie header - * @return the decoded {@link Cookie} - */ - public static Cookie decode(String header) { - - assertNotNull(header, "header"); - - final int headerLen = header.length(); - - if (headerLen == 0) { - return null; - } - - CookieBuilder cookieBuilder = null; - - loop: for (int i = 0;;) { - - // Skip spaces and separators. - for (;;) { - if (i == headerLen) { - break loop; - } - char c = header.charAt(i); - if (c == ',') { - // Having multiple cookies in a single Set-Cookie header is - // deprecated, modern browsers only parse the first one - break loop; - - } else if (c == '\t' || c == '\n' || c == 0x0b || c == '\f' || c == '\r' || c == ' ' || c == ';') { - i++; - continue; - } - break; - } - - int nameBegin = i; - int nameEnd = i; - int valueStart = -1; - int valueEnd = -1; - - if (i != headerLen) { - keyValLoop: for (;;) { - - char curChar = header.charAt(i); - if (curChar == ';') { - // NAME; (no value till ';') - nameEnd = i; - valueStart = valueEnd = -1; - break keyValLoop; - - } else if (curChar == '=') { - // NAME=VALUE - nameEnd = i; - i++; - if (i == headerLen) { - // NAME= (empty value, i.e. nothing after '=') - valueStart = valueEnd = 0; - break keyValLoop; - } - - valueStart = i; - // NAME=VALUE; - int semiPos = header.indexOf(';', i); - valueEnd = i = semiPos > 0 ? semiPos : headerLen; - break keyValLoop; - } else { - i++; - } - - if (i == headerLen) { - // NAME (no value till the end of string) - nameEnd = headerLen; - valueStart = valueEnd = -1; - break; - } - } - } - - if (valueEnd > 0 && header.charAt(valueEnd - 1) == ',') { - // old multiple cookies separator, skipping it - valueEnd--; - } - - if (cookieBuilder == null) { - // cookie name-value pair - if (nameBegin == -1 || nameBegin == nameEnd) { - LOGGER.debug("Skipping cookie with null name"); - return null; - } - - if (valueStart == -1) { - LOGGER.debug("Skipping cookie with null value"); - return null; - } - - CharSequence wrappedValue = CharBuffer.wrap(header, valueStart, valueEnd); - CharSequence unwrappedValue = unwrapValue(wrappedValue); - if (unwrappedValue == null) { - LOGGER.debug("Skipping cookie because starting quotes are not properly balanced in '{}'", unwrappedValue); - return null; - } - - final String name = header.substring(nameBegin, nameEnd); - - final boolean wrap = unwrappedValue.length() != valueEnd - valueStart; - - cookieBuilder = new CookieBuilder(name, unwrappedValue.toString(), wrap, header); - - } else { - // cookie attribute - cookieBuilder.appendAttribute(nameBegin, nameEnd, valueStart, valueEnd); - } - } - return cookieBuilder.cookie(); - } - - private static class CookieBuilder { - - private static final String PATH = "Path"; - - private static final String EXPIRES = "Expires"; - - private static final String MAX_AGE = "Max-Age"; - - private static final String DOMAIN = "Domain"; - - private static final String SECURE = "Secure"; - - private static final String HTTPONLY = "HTTPOnly"; - - private final String name; - private final String value; - private final boolean wrap; - private final String header; - private String domain; - private String path; - private long maxAge = Long.MIN_VALUE; - private int expiresStart; - private int expiresEnd; - private boolean secure; - private boolean httpOnly; - - public CookieBuilder(String name, String value, boolean wrap, String header) { - this.name = name; - this.value = value; - this.wrap = wrap; - this.header = header; - } - - public Cookie cookie() { - return new Cookie(name, value, wrap, domain, path, mergeMaxAgeAndExpires(), secure, httpOnly); - } - - private long mergeMaxAgeAndExpires() { - // max age has precedence over expires - if (maxAge != Long.MIN_VALUE) { - return maxAge; - } else if (isValueDefined(expiresStart, expiresEnd)) { - Date expiresDate = HttpHeaderDateFormatter.parse(header, expiresStart, expiresEnd); - if (expiresDate != null) { - long maxAgeMillis = expiresDate.getTime() - System.currentTimeMillis(); - return maxAgeMillis / 1000 + (maxAgeMillis % 1000 != 0 ? 1 : 0); - } - } - return Long.MIN_VALUE; - } - - /** - * Parse and store a key-value pair. First one is considered to be the cookie name/value. Unknown attribute names are silently discarded. - * - * @param keyStart where the key starts in the header - * @param keyEnd where the key ends in the header - * @param valueStart where the value starts in the header - * @param valueEnd where the value ends in the header - */ - public void appendAttribute(int keyStart, int keyEnd, int valueStart, int valueEnd) { - setCookieAttribute(keyStart, keyEnd, valueStart, valueEnd); - } - - private void setCookieAttribute(int keyStart, int keyEnd, int valueStart, int valueEnd) { - - int length = keyEnd - keyStart; - - if (length == 4) { - parse4(keyStart, valueStart, valueEnd); - } else if (length == 6) { - parse6(keyStart, valueStart, valueEnd); - } else if (length == 7) { - parse7(keyStart, valueStart, valueEnd); - } else if (length == 8) { - parse8(keyStart, valueStart, valueEnd); - } - } - - private void parse4(int nameStart, int valueStart, int valueEnd) { - if (header.regionMatches(true, nameStart, PATH, 0, 4)) { - path = computeValue(valueStart, valueEnd); - } - } - - private void parse6(int nameStart, int valueStart, int valueEnd) { - if (header.regionMatches(true, nameStart, DOMAIN, 0, 5)) { - domain = computeValue(valueStart, valueEnd); - } else if (header.regionMatches(true, nameStart, SECURE, 0, 5)) { - secure = true; - } - } - - private void parse7(int nameStart, int valueStart, int valueEnd) { - if (header.regionMatches(true, nameStart, EXPIRES, 0, 7)) { - expiresStart = valueStart; - expiresEnd = valueEnd; - } else if (header.regionMatches(true, nameStart, MAX_AGE, 0, 7)) { - try { - maxAge = Math.max(Integer.valueOf(computeValue(valueStart, valueEnd)), 0); - } catch (NumberFormatException e1) { - // ignore failure to parse -> treat as session cookie - } - } - } - - private void parse8(int nameStart, int valueStart, int valueEnd) { - if (header.regionMatches(true, nameStart, HTTPONLY, 0, 8)) { - httpOnly = true; - } - } - - private static boolean isValueDefined(int valueStart, int valueEnd) { - return valueStart != -1 && valueStart != valueEnd; - } - - private String computeValue(int valueStart, int valueEnd) { - if (isValueDefined(valueStart, valueEnd)) { - while (valueStart < valueEnd && header.charAt(valueStart) <= ' ') { - valueStart++; - } - while (valueStart < valueEnd && (header.charAt(valueEnd - 1) <= ' ')) { - valueEnd--; - } - return valueStart == valueEnd ? null : header.substring(valueStart, valueEnd); - } else { - return null; - } - } - } -} diff --git a/client/src/main/java/org/asynchttpclient/cookie/CookieEncoder.java b/client/src/main/java/org/asynchttpclient/cookie/CookieEncoder.java deleted file mode 100644 index 01bc6caf13..0000000000 --- a/client/src/main/java/org/asynchttpclient/cookie/CookieEncoder.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2014 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 java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; - -import org.asynchttpclient.util.StringUtils; - -public final class CookieEncoder { - - /** - * Sort cookies into decreasing order of path length, breaking ties by sorting into increasing chronological order of creation time, as recommended by RFC 6265. - */ - private static final Comparator COOKIE_COMPARATOR = new Comparator() { - @Override - public int compare(Cookie c1, Cookie c2) { - String path1 = c1.getPath(); - String path2 = c2.getPath(); - // Cookies with unspecified path default to the path of the request. We don't - // know the request path here, but we assume that the length of an unspecified - // path is longer than any specified path (i.e. pathless cookies come first), - // because setting cookies with a path longer than the request path is of - // limited use. - int len1 = path1 == null ? Integer.MAX_VALUE : path1.length(); - int len2 = path2 == null ? Integer.MAX_VALUE : path2.length(); - int diff = len2 - len1; - if (diff != 0) { - return diff; - } - // Rely on Java's sort stability to retain creation order in cases where - // cookies have same path length. - return -1; - } - }; - - private CookieEncoder() { - } - - public static String encode(Collection cookies) { - StringBuilder sb = StringUtils.stringBuilder(); - - if (cookies.isEmpty()) { - return ""; - - } else if (cookies.size() == 1) { - Cookie cookie = cookies.iterator().next(); - if (cookie != null) { - add(sb, cookie.getName(), cookie.getValue(), cookie.isWrap()); - } - - } else { - Cookie[] cookiesSorted = cookies.toArray(new Cookie[cookies.size()]); - Arrays.sort(cookiesSorted, COOKIE_COMPARATOR); - for (Cookie cookie : cookiesSorted) { - if (cookie != null) { - add(sb, cookie.getName(), cookie.getValue(), cookie.isWrap()); - } - } - } - - if (sb.length() > 0) { - sb.setLength(sb.length() - 2); - } - return sb.toString(); - } - - private static void add(StringBuilder sb, String name, String val, boolean wrap) { - - if (val == null) { - val = ""; - } - - sb.append(name); - sb.append('='); - if (wrap) - sb.append('"').append(val).append('"'); - else - sb.append(val); - sb.append(';'); - sb.append(' '); - } -} diff --git a/client/src/main/java/org/asynchttpclient/cookie/CookieUtil.java b/client/src/main/java/org/asynchttpclient/cookie/CookieUtil.java deleted file mode 100644 index 6c89dacddd..0000000000 --- a/client/src/main/java/org/asynchttpclient/cookie/CookieUtil.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2015 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 static org.asynchttpclient.util.Assertions.*; - -import java.util.BitSet; - -public class CookieUtil { - - private static final BitSet VALID_COOKIE_NAME_OCTETS = validCookieNameOctets(); - private static final BitSet VALID_COOKIE_VALUE_OCTETS = validCookieValueOctets(); - private static final BitSet VALID_COOKIE_ATTRIBUTE_VALUE_OCTETS = validCookieAttributeValueOctets(); - - // token = 1* - // separators = "(" | ")" | "<" | ">" | "@" - // | "," | ";" | ":" | "\" | <"> - // | "/" | "[" | "]" | "?" | "=" - // | "{" | "}" | SP | HT - private static BitSet validCookieNameOctets() { - BitSet bits = new BitSet(); - for (int i = 32; i < 127; i++) { - bits.set(i); - } - int[] separators = new int[] { '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t' }; - for (int separator : separators) { - bits.set(separator, false); - } - return bits; - } - - // cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E - // US-ASCII characters excluding CTLs, whitespace, DQUOTE, comma, semicolon, and backslash - private static BitSet validCookieValueOctets() { - BitSet bits = new BitSet(); - bits.set(0x21); - for (int i = 0x23; i <= 0x2B; i++) { - bits.set(i); - } - for (int i = 0x2D; i <= 0x3A; i++) { - bits.set(i); - } - for (int i = 0x3C; i <= 0x5B; i++) { - bits.set(i); - } - for (int i = 0x5D; i <= 0x7E; i++) { - bits.set(i); - } - return bits; - } - - private static BitSet validCookieAttributeValueOctets() { - BitSet bits = new BitSet(); - for (int i = 32; i < 127; i++) { - bits.set(i); - } - bits.set(';', false); - return bits; - } - - static String validateCookieName(String name) { - name = assertNotNull(name, "name").trim(); - assertNotEmpty(name, "name"); - int i = firstInvalidOctet(name, VALID_COOKIE_NAME_OCTETS); - if (i != -1) { - throw new IllegalArgumentException("name contains prohibited character: " + name.charAt(i)); - } - return name; - } - - static String validateCookieValue(String value) { - value = assertNotNull(value, "name").trim(); - CharSequence unwrappedValue = unwrapValue(value); - int i = firstInvalidOctet(unwrappedValue, VALID_COOKIE_VALUE_OCTETS); - if (i != -1) { - throw new IllegalArgumentException("value contains prohibited character: " + unwrappedValue.charAt(i)); - } - return value; - } - - static String validateCookieAttribute(String name, String value) { - if (value == null) { - return null; - } - value = value.trim(); - if (value.length() == 0) { - return null; - } - int i = firstInvalidOctet(value, VALID_COOKIE_ATTRIBUTE_VALUE_OCTETS); - if (i != -1) { - throw new IllegalArgumentException(name + " contains prohibited character: " + value.charAt(i)); - } - return value; - } - - static int firstInvalidCookieValueOctet(CharSequence cs) { - return firstInvalidOctet(cs, VALID_COOKIE_VALUE_OCTETS); - } - - static int firstInvalidOctet(CharSequence cs, BitSet bits) { - - for (int i = 0; i < cs.length(); i++) { - char c = cs.charAt(i); - if (!bits.get(c)) { - return i; - } - } - return -1; - } - - static CharSequence unwrapValue(CharSequence cs) { - final int len = cs.length(); - if (len > 0 && cs.charAt(0) == '"') { - if (len >= 2 && cs.charAt(len - 1) == '"') { - // properly balanced - return len == 2 ? "" : cs.subSequence(1, len - 1); - } else { - return null; - } - } - return cs; - } - - private CookieUtil() { - // Unused - } -} diff --git a/client/src/main/java/org/asynchttpclient/cookie/HttpHeaderDateFormatter.java b/client/src/main/java/org/asynchttpclient/cookie/HttpHeaderDateFormatter.java deleted file mode 100644 index 169f30597f..0000000000 --- a/client/src/main/java/org/asynchttpclient/cookie/HttpHeaderDateFormatter.java +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright (c) 2016 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 static io.netty.util.internal.ObjectUtil.checkNotNull; - -import io.netty.util.concurrent.FastThreadLocal; - -import java.util.BitSet; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.TimeZone; - -/** - * A formatter for HTTP header dates, such as "Expires" and "Date" headers, or "expires" field in "Set-Cookie". - * - * On the parsing side, it honors RFC6265 (so it supports RFC1123). - * Note that: - *
    - *
  • Day of week is ignored and not validated
  • - *
  • Timezone is ignored, as RFC6265 assumes UTC
  • - *
- * If you're looking for a date format that validates day of week, or supports other timezones, consider using - * java.util.DateTimeFormatter.RFC_1123_DATE_TIME. - * - * On the formatting side, it uses RFC1123 format. - * - * @see RFC6265 for the parsing side - * @see RFC1123 for the encoding side. - */ -public final class HttpHeaderDateFormatter { - - private static final BitSet DELIMITERS = new BitSet(); - static { - DELIMITERS.set(0x09); - for (char c = 0x20; c <= 0x2F; c++) { - DELIMITERS.set(c); - } - for (char c = 0x3B; c <= 0x40; c++) { - DELIMITERS.set(c); - } - for (char c = 0x5B; c <= 0x60; c++) { - DELIMITERS.set(c); - } - for (char c = 0x7B; c <= 0x7E; c++) { - DELIMITERS.set(c); - } - } - - private static final String[] DAY_OF_WEEK_TO_SHORT_NAME = - new String[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; - - private static final String[] CALENDAR_MONTH_TO_SHORT_NAME = - new String[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; - - private static final FastThreadLocal INSTANCES = - new FastThreadLocal() { - @Override - protected HttpHeaderDateFormatter initialValue() { - return new HttpHeaderDateFormatter(); - } - }; - - /** - * Parse some text into a {@link Date}, according to RFC6265 - * @param txt text to parse - * @return a {@link Date}, or null if text couldn't be parsed - */ - public static Date parse(CharSequence txt) { - return parse(checkNotNull(txt, "txt"), 0, txt.length()); - } - - /** - * Parse some text into a {@link Date}, according to RFC6265 - * @param txt text to parse - * @param start the start index inside txt - * @param end the end index inside txt - * @return a {@link Date}, or null if text couldn't be parsed - */ - public static Date parse(CharSequence txt, int start, int end) { - return formatter().parse0(checkNotNull(txt, "txt"), start, end); - } - - /** - * Format a {@link Date} into RFC1123 format - * @param date the date to format - * @return a RFC1123 string - */ - public static String format(Date date) { - return formatter().format0(checkNotNull(date, "date")); - } - - /** - * Append a {@link Date} to a {@link StringBuilder} into RFC1123 format - * @param date the date to format - * @param sb the StringBuilder - * @return the same StringBuilder - */ - public static StringBuilder append(Date date, StringBuilder sb) { - return formatter().append0(checkNotNull(date, "date"), checkNotNull(sb, "sb")); - } - - private static HttpHeaderDateFormatter formatter() { - HttpHeaderDateFormatter formatter = INSTANCES.get(); - formatter.reset(); - return formatter; - } - - // delimiter = %x09 / %x20-2F / %x3B-40 / %x5B-60 / %x7B-7E - private static boolean isDelim(char c) { - return DELIMITERS.get(c); - } - - private static boolean isDigit(char c) { - return c >= 0x30 && c <= 0x39; - } - - private static int getNumericalValue(char c) { - return (int) c - 48; - } - - private final GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); - private final StringBuilder sb = new StringBuilder(29); // Sun, 27 Nov 2016 19:37:15 GMT - private boolean timeFound; - private int hours; - private int minutes; - private int seconds; - private boolean dayOfMonthFound; - private int dayOfMonth; - private boolean monthFound; - private int month; - private boolean yearFound; - private int year; - - private HttpHeaderDateFormatter() { - reset(); - } - - public void reset() { - timeFound = false; - hours = -1; - minutes = -1; - seconds = -1; - dayOfMonthFound = false; - dayOfMonth = -1; - monthFound = false; - month = -1; - yearFound = false; - year = -1; - cal.set(Calendar.MILLISECOND, 0); - sb.setLength(0); - } - - private boolean tryParseTime(CharSequence txt, int tokenStart, int tokenEnd) { - int len = tokenEnd - tokenStart; - - // d:m:yy to dd:mm:yyyy - if (len < 6 || len > 10) { - return false; - } - - int localHours = -1; - int localMinutes = -1; - int localSeconds = -1; - int currentPartNumber = 0; - int currentPartValue = 0; - - for (int i = tokenStart; i < tokenEnd; i++) { - char c = txt.charAt(i); - if (isDigit(c)) { - currentPartValue = currentPartValue * 10 + getNumericalValue(c); - } else if (c == ':') { - if (currentPartValue == 0) { - // invalid :: (nothing in between) - return false; - - } else if (currentPartNumber == 0) { - // flushing hours - localHours = currentPartValue; - currentPartValue = 0; - currentPartNumber++; - - } else if (currentPartNumber == 1) { - // flushing minutes - localMinutes = currentPartValue; - currentPartValue = 0; - currentPartNumber++; - - } else if (currentPartNumber == 2) { - // invalid, too many : - return false; - } - } else { - // invalid char - return false; - } - } - - if (currentPartValue > 0) { - // pending seconds - localSeconds = currentPartValue; - } - - if (localHours >= 0 && localMinutes >= 0 && localSeconds >= 0) { - hours = localHours; - minutes = localMinutes; - seconds = localSeconds; - return true; - } - - return false; - } - - private boolean tryParseDayOfMonth(CharSequence txt, int tokenStart, int tokenEnd) { - int len = tokenEnd - tokenStart; - - if (len != 1 && len != 2) { - return false; - } - - int localDayOfMonth = 0; - for (int i = tokenStart; i < tokenEnd; i++) { - char c = txt.charAt(i); - if (isDigit(c)) { - localDayOfMonth = localDayOfMonth * 10 + getNumericalValue(c); - } else { - // invalid - return false; - } - } - - if (localDayOfMonth > 0) { - dayOfMonth = localDayOfMonth; - return true; - } - - return false; - } - - private boolean tryParseMonth(CharSequence txt, int tokenStart, int tokenEnd) { - int len = tokenEnd - tokenStart; - - if (len == 3) { - String tokenString = txt.subSequence(tokenStart, tokenEnd).toString(); - - if (tokenString.equalsIgnoreCase("Jan")) { - month = Calendar.JANUARY; - } else if (tokenString.equalsIgnoreCase("Feb")) { - month = Calendar.FEBRUARY; - } else if (tokenString.equalsIgnoreCase("Mar")) { - month = Calendar.MARCH; - } else if (tokenString.equalsIgnoreCase("Apr")) { - month = Calendar.APRIL; - } else if (tokenString.equalsIgnoreCase("May")) { - month = Calendar.MAY; - } else if (tokenString.equalsIgnoreCase("Jun")) { - month = Calendar.JUNE; - } else if (tokenString.equalsIgnoreCase("Jul")) { - month = Calendar.JULY; - } else if (tokenString.equalsIgnoreCase("Aug")) { - month = Calendar.AUGUST; - } else if (tokenString.equalsIgnoreCase("Sep")) { - month = Calendar.SEPTEMBER; - } else if (tokenString.equalsIgnoreCase("Oct")) { - month = Calendar.OCTOBER; - } else if (tokenString.equalsIgnoreCase("Nov")) { - month = Calendar.NOVEMBER; - } else if (tokenString.equalsIgnoreCase("Dec")) { - month = Calendar.DECEMBER; - } - } - - return month != -1; - } - - private boolean tryParseYear(CharSequence txt, int tokenStart, int tokenEnd) { - int len = tokenEnd - tokenStart; - - if (len != 2 && len != 4) { - return false; - } - - int localYear = 0; - for (int i = tokenStart; i < tokenEnd; i++) { - char c = txt.charAt(i); - if (isDigit(c)) { - localYear = localYear * 10 + getNumericalValue(c); - } else { - // invalid - return false; - } - } - - if (localYear > 0) { - year = localYear; - return true; - } - - return false; - } - - private void parseToken(CharSequence txt, int tokenStart, int tokenEnd) { - if (!timeFound) { - timeFound = tryParseTime(txt, tokenStart, tokenEnd); - if (timeFound) { - return; - } - } - - if (!dayOfMonthFound) { - dayOfMonthFound = tryParseDayOfMonth(txt, tokenStart, tokenEnd); - if (dayOfMonthFound) { - return; - } - } - - if (!monthFound) { - monthFound = tryParseMonth(txt, tokenStart, tokenEnd); - if (monthFound) { - return; - } - } - - if (!yearFound) { - yearFound = tryParseYear(txt, tokenStart, tokenEnd); - } - } - - private Date parse0(CharSequence txt, int start, int end) { - int tokenStart = -1; - - for (int i = start; i < end; i++) { - char c = txt.charAt(i); - - if (isDelim(c)) { - if (tokenStart != -1) { - // terminate token - parseToken(txt, tokenStart, i); - tokenStart = -1; - } - } else { - if (tokenStart == -1) { - // start new token - tokenStart = i; - } - } - } - - if (tokenStart != -1) { - // terminate trailing token - parseToken(txt, tokenStart, txt.length()); - } - - if (!timeFound || !dayOfMonthFound || !monthFound || !yearFound) { - // missing field - return null; - } - - if (dayOfMonth < 1 || dayOfMonth > 31 || hours > 23 || minutes > 59 || seconds > 59) { - // invalid values - return null; - } - - if (year >= 70 && year <= 99) { - year += 1900; - } else if (year >= 0 && year < 70) { - year += 2000; - } else if (year < 1601) { - // invalid value - return null; - } - - cal.set(Calendar.DAY_OF_MONTH, dayOfMonth); - cal.set(Calendar.MONTH, month); - cal.set(Calendar.YEAR, year); - cal.set(Calendar.HOUR_OF_DAY, hours); - cal.set(Calendar.MINUTE, minutes); - cal.set(Calendar.SECOND, seconds); - return cal.getTime(); - } - - private String format0(Date date) { - append0(date, sb); - return sb.toString(); - } - - private StringBuilder append0(Date date, StringBuilder sb) { - cal.setTime(date); - - sb.append(DAY_OF_WEEK_TO_SHORT_NAME[cal.get(Calendar.DAY_OF_WEEK) - 1]).append(", "); - sb.append(cal.get(Calendar.DAY_OF_MONTH)).append(' '); - sb.append(CALENDAR_MONTH_TO_SHORT_NAME[cal.get(Calendar.MONTH)]).append(' '); - sb.append(cal.get(Calendar.YEAR)).append(' '); - appendZeroLeftPadded(cal.get(Calendar.HOUR_OF_DAY), sb).append(':'); - appendZeroLeftPadded(cal.get(Calendar.MINUTE), sb).append(':'); - return appendZeroLeftPadded(cal.get(Calendar.SECOND), sb).append(" GMT"); - } - - private static StringBuilder appendZeroLeftPadded(int value, StringBuilder sb) { - if (value < 10) { - sb.append('0'); - } - return sb.append(value); - } -} diff --git a/client/src/main/java/org/asynchttpclient/netty/NettyResponse.java b/client/src/main/java/org/asynchttpclient/netty/NettyResponse.java index 979598fb4d..ec3a7fd881 100755 --- a/client/src/main/java/org/asynchttpclient/netty/NettyResponse.java +++ b/client/src/main/java/org/asynchttpclient/netty/NettyResponse.java @@ -18,6 +18,8 @@ import static org.asynchttpclient.util.MiscUtils.isNonEmpty; import io.netty.handler.codec.http.EmptyHttpHeaders; import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.cookie.ClientCookieDecoder; +import io.netty.handler.codec.http.cookie.Cookie; import java.io.ByteArrayInputStream; import java.io.InputStream; @@ -33,8 +35,6 @@ import org.asynchttpclient.HttpResponseHeaders; import org.asynchttpclient.HttpResponseStatus; import org.asynchttpclient.Response; -import org.asynchttpclient.cookie.Cookie; -import org.asynchttpclient.cookie.CookieDecoder; import org.asynchttpclient.uri.Uri; /** @@ -66,7 +66,7 @@ private List buildCookies() { if (isNonEmpty(setCookieHeaders)) { List cookies = new ArrayList<>(1); for (String value : setCookieHeaders) { - Cookie c = CookieDecoder.decode(value); + Cookie c = ClientCookieDecoder.STRICT.decode(value); if (c != null) cookies.add(c); } 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 c4f884addc..ebcaffe53c 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 @@ -22,6 +22,8 @@ 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 java.util.HashSet; import java.util.Set; @@ -31,8 +33,6 @@ import org.asynchttpclient.Realm.AuthScheme; import org.asynchttpclient.Request; import org.asynchttpclient.RequestBuilder; -import org.asynchttpclient.cookie.Cookie; -import org.asynchttpclient.cookie.CookieDecoder; import org.asynchttpclient.handler.MaxRedirectException; import org.asynchttpclient.netty.NettyResponseFuture; import org.asynchttpclient.netty.channel.ChannelManager; @@ -125,7 +125,7 @@ else if (request.getBodyGenerator() != null) LOGGER.debug("Redirecting to {}", newUri); for (String cookieStr : responseHeaders.getAll(SET_COOKIE)) { - Cookie c = CookieDecoder.decode(cookieStr); + Cookie c = ClientCookieDecoder.STRICT.decode(cookieStr); if (c != null) requestBuilder.addOrReplaceCookie(c); } diff --git a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java index 2218e7b4cb..0c6c871835 100755 --- a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java @@ -27,13 +27,13 @@ import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http.cookie.ClientCookieEncoder; import java.nio.charset.Charset; import org.asynchttpclient.AsyncHttpClientConfig; import org.asynchttpclient.Realm; import org.asynchttpclient.Request; -import org.asynchttpclient.cookie.CookieEncoder; import org.asynchttpclient.netty.request.body.NettyBody; import org.asynchttpclient.netty.request.body.NettyBodyBody; import org.asynchttpclient.netty.request.body.NettyByteArrayBody; @@ -168,7 +168,7 @@ public NettyRequest newNettyRequest(Request request, boolean forceConnect, Proxy headers.set(request.getHeaders()); if (isNonEmpty(request.getCookies())) - headers.set(COOKIE, CookieEncoder.encode(request.getCookies())); + headers.set(COOKIE, ClientCookieEncoder.STRICT.encode(request.getCookies())); String userDefinedAcceptEncoding = headers.get(ACCEPT_ENCODING); if (userDefinedAcceptEncoding != null) { diff --git a/client/src/main/java/org/asynchttpclient/webdav/WebDavResponse.java b/client/src/main/java/org/asynchttpclient/webdav/WebDavResponse.java index 27273556cd..78f26ac701 100644 --- a/client/src/main/java/org/asynchttpclient/webdav/WebDavResponse.java +++ b/client/src/main/java/org/asynchttpclient/webdav/WebDavResponse.java @@ -13,6 +13,7 @@ package org.asynchttpclient.webdav; import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.cookie.Cookie; import java.io.InputStream; import java.net.SocketAddress; @@ -21,7 +22,6 @@ import java.util.List; import org.asynchttpclient.Response; -import org.asynchttpclient.cookie.Cookie; import org.asynchttpclient.uri.Uri; import org.w3c.dom.Document; diff --git a/client/src/test/java/org/asynchttpclient/BasicHttpTest.java b/client/src/test/java/org/asynchttpclient/BasicHttpTest.java index 8467629715..b199c18f94 100755 --- a/client/src/test/java/org/asynchttpclient/BasicHttpTest.java +++ b/client/src/test/java/org/asynchttpclient/BasicHttpTest.java @@ -23,6 +23,8 @@ import io.netty.handler.codec.http.DefaultHttpHeaders; import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.cookie.Cookie; +import io.netty.handler.codec.http.cookie.DefaultCookie; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -48,7 +50,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.asynchttpclient.cookie.Cookie; import org.asynchttpclient.handler.MaxRedirectException; import org.asynchttpclient.request.body.generator.InputStreamBodyGenerator; import org.asynchttpclient.request.body.multipart.StringPart; @@ -62,6 +63,7 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; + public class BasicHttpTest extends HttpTest { private static HttpServer server; @@ -265,7 +267,9 @@ public Response onCompleted(Response response) throws Exception { public void getWithCookies() throws Throwable { withClient().run(client -> { withServer(server).run(server -> { - final Cookie coo = Cookie.newValidCookie("foo", "value", false, "/", "/", Long.MIN_VALUE, false, false); + final Cookie coo = new DefaultCookie("foo", "value"); + coo.setDomain("/"); + coo.setPath("/"); server.enqueueEcho(); client.prepareGet(getTargetUrl())// diff --git a/client/src/test/java/org/asynchttpclient/RemoteSiteTest.java b/client/src/test/java/org/asynchttpclient/RemoteSiteTest.java index 93b7be6d63..06d93f51dd 100644 --- a/client/src/test/java/org/asynchttpclient/RemoteSiteTest.java +++ b/client/src/test/java/org/asynchttpclient/RemoteSiteTest.java @@ -19,6 +19,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.asynchttpclient.Dsl.*; import static org.testng.Assert.*; +import io.netty.handler.codec.http.cookie.Cookie; +import io.netty.handler.codec.http.cookie.DefaultCookie; import java.io.InputStream; import java.net.URLEncoder; @@ -26,7 +28,6 @@ import java.util.concurrent.TimeUnit; import org.apache.commons.io.IOUtils; -import org.asynchttpclient.cookie.Cookie; import org.testng.annotations.Test; /** @@ -168,11 +169,15 @@ public void stripQueryStringTest() throws Exception { @Test(groups = "online") public void evilCoookieTest() throws Exception { try (AsyncHttpClient c = asyncHttpClient()) { + Cookie cookie = new DefaultCookie("evilcookie", "test"); + cookie.setDomain(".google.com"); + cookie.setPath("/"); + RequestBuilder builder = get("http://localhost")// .setFollowRedirect(true)// .setUrl("http://www.google.com/")// .addHeader("Content-Type", "text/plain")// - .addCookie(new Cookie("evilcookie", "test", false, ".google.com", "/", Long.MIN_VALUE, false, false)); + .addCookie(cookie); Response response = c.executeRequest(builder.build()).get(); assertNotNull(response); diff --git a/client/src/test/java/org/asynchttpclient/RequestBuilderTest.java b/client/src/test/java/org/asynchttpclient/RequestBuilderTest.java index 38b6a2c3e3..051867bdc2 100644 --- a/client/src/test/java/org/asynchttpclient/RequestBuilderTest.java +++ b/client/src/test/java/org/asynchttpclient/RequestBuilderTest.java @@ -18,19 +18,22 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.singletonList; import static org.asynchttpclient.Dsl.get; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; +import static org.testng.Assert.*; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.cookie.Cookie; +import io.netty.handler.codec.http.cookie.DefaultCookie; import java.io.IOException; import java.io.UnsupportedEncodingException; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; -import org.asynchttpclient.cookie.Cookie; import org.testng.annotations.Test; -import io.netty.handler.codec.http.HttpMethod; - public class RequestBuilderTest { private final static String SAFE_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890-_*."; @@ -134,17 +137,33 @@ public void testSetHeaders() { public void testAddOrReplaceCookies() { RequestBuilder requestBuilder = new RequestBuilder(); - Cookie cookie = new Cookie("name", "value", false, "google.com", "/", 1000, true, true); + Cookie cookie = new DefaultCookie("name", "value"); + cookie.setDomain("google.com"); + cookie.setPath("/"); + cookie.setMaxAge(1000); + cookie.setSecure(true); + cookie.setHttpOnly(true); requestBuilder.addOrReplaceCookie(cookie); assertEquals(requestBuilder.cookies.size(), 1, "cookies size should be 1 after adding one cookie"); assertEquals(requestBuilder.cookies.get(0), cookie, "cookie does not match"); - Cookie cookie2 = new Cookie("name", "value2", true, "google2.com", "/path", 1001, false, false); + Cookie cookie2 = new DefaultCookie("name", "value"); + cookie2.setDomain("google2.com"); + cookie2.setPath("/path"); + cookie2.setMaxAge(1001); + cookie2.setSecure(false); + cookie2.setHttpOnly(false); + requestBuilder.addOrReplaceCookie(cookie2); assertEquals(requestBuilder.cookies.size(), 1, "cookies size should remain 1 as we just replaced a cookie with same name"); assertEquals(requestBuilder.cookies.get(0), cookie2, "cookie does not match"); - Cookie cookie3 = new Cookie("name", "value", false, "google.com", "/", 1000, true, true); + Cookie cookie3 = new DefaultCookie("name", "value"); + cookie3.setDomain("google.com"); + cookie3.setPath("/"); + cookie3.setMaxAge(1000); + cookie3.setSecure(true); + cookie3.setHttpOnly(true); requestBuilder.addOrReplaceCookie(cookie3); assertEquals(requestBuilder.cookies.size(), 2, "cookie size must be 2 after adding 1 more cookie i.e. cookie3"); } diff --git a/client/src/test/java/org/asynchttpclient/cookie/CookieDecoderTest.java b/client/src/test/java/org/asynchttpclient/cookie/CookieDecoderTest.java deleted file mode 100644 index 42670a1336..0000000000 --- a/client/src/test/java/org/asynchttpclient/cookie/CookieDecoderTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2014 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 static org.testng.Assert.*; -import io.netty.handler.codec.http.HttpHeaderDateFormat; - -import java.util.Date; - -import org.testng.annotations.Test; - -public class CookieDecoderTest { - - @Test(groups = "standalone") - public void testDecodeUnquoted() { - Cookie cookie = CookieDecoder.decode("foo=value; domain=/; path=/"); - assertNotNull(cookie); - assertEquals(cookie.getValue(), "value"); - assertEquals(cookie.isWrap(), false); - assertEquals(cookie.getDomain(), "/"); - assertEquals(cookie.getPath(), "/"); - } - - @Test(groups = "standalone") - public void testDecodeQuoted() { - Cookie cookie = CookieDecoder.decode("ALPHA=\"VALUE1\"; Domain=docs.foo.com; Path=/accounts; Expires=Wed, 05 Feb 2014 07:37:38 GMT; Secure; HttpOnly"); - assertNotNull(cookie); - assertEquals(cookie.getValue(), "VALUE1"); - assertEquals(cookie.isWrap(), true); - } - - @Test(groups = "standalone") - public void testDecodeQuotedContainingEscapedQuote() { - Cookie cookie = CookieDecoder.decode("ALPHA=\"VALUE1\\\"\"; Domain=docs.foo.com; Path=/accounts; Expires=Wed, 05 Feb 2014 07:37:38 GMT; Secure; HttpOnly"); - assertNotNull(cookie); - assertEquals(cookie.getValue(), "VALUE1\\\""); - assertEquals(cookie.isWrap(), true); - } - - @Test(groups = "standalone") - public void testIgnoreEmptyDomain() { - Cookie cookie = CookieDecoder.decode("sessionid=OTY4ZDllNTgtYjU3OC00MWRjLTkzMWMtNGUwNzk4MTY0MTUw;Domain=;Path=/"); - assertNull(cookie.getDomain()); - } - - @Test(groups = "standalone") - public void testDecodingSingleCookieV0() { - String cookieString = "myCookie=myValue;expires=XXX;path=/apathsomewhere;domain=.adomainsomewhere;secure;"; - cookieString = cookieString.replace("XXX", HttpHeaderDateFormat.get().format(new Date(System.currentTimeMillis() + 50000))); - - Cookie cookie = CookieDecoder.decode(cookieString); - assertNotNull(cookie); - assertEquals("myValue", cookie.getValue()); - assertEquals(".adomainsomewhere", cookie.getDomain()); - - boolean fail = true; - for (int i = 40; i <= 60; i++) { - if (cookie.getMaxAge() == i) { - fail = false; - break; - } - } - if (fail) { - fail("expected: 50, actual: " + cookie.getMaxAge()); - } - - assertEquals(cookie.getPath(), "/apathsomewhere"); - assertTrue(cookie.isSecure()); - } -} diff --git a/client/src/test/java/org/asynchttpclient/cookie/HttpHeaderDateFormatterTest.java b/client/src/test/java/org/asynchttpclient/cookie/HttpHeaderDateFormatterTest.java deleted file mode 100644 index 24ec9199fb..0000000000 --- a/client/src/test/java/org/asynchttpclient/cookie/HttpHeaderDateFormatterTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2016 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 static org.testng.Assert.assertEquals; - -import java.util.Date; - -import org.testng.annotations.Test; - -public class HttpHeaderDateFormatterTest { - /** - * This date is set at "06 Nov 1994 08:49:37 GMT", from - * examples in RFC documentation - */ - private static final Date DATE = new Date(784111777000L); - - @Test - public void testParseWithSingleDigitDay() { - assertEquals(DATE, HttpHeaderDateFormatter.parse("Sun, 6 Nov 1994 08:49:37 GMT")); - } - - @Test - public void testParseWithDoubleDigitDay() { - assertEquals(DATE, HttpHeaderDateFormatter.parse("Sun, 06 Nov 1994 08:49:37 GMT")); - } - - @Test - public void testParseWithDashSeparatorSingleDigitDay() { - assertEquals(DATE, HttpHeaderDateFormatter.parse("Sunday, 06-Nov-94 08:49:37 GMT")); - } - - @Test - public void testParseWithSingleDoubleDigitDay() { - assertEquals(DATE, HttpHeaderDateFormatter.parse("Sunday, 6-Nov-94 08:49:37 GMT")); - } - - @Test - public void testParseWithoutGMT() { - assertEquals(DATE, HttpHeaderDateFormatter.parse("Sun Nov 6 08:49:37 1994")); - } - - @Test - public void testParseWithFunkyTimezone() { - assertEquals(DATE, HttpHeaderDateFormatter.parse("Sun Nov 6 08:49:37 1994 -0000")); - } - - @Test - public void testFormat() { - assertEquals("Sun, 6 Nov 1994 08:49:37 GMT", HttpHeaderDateFormatter.format(DATE)); - } -} diff --git a/client/src/test/java/org/asynchttpclient/netty/NettyAsyncResponseTest.java b/client/src/test/java/org/asynchttpclient/netty/NettyAsyncResponseTest.java index 80c64c215c..5dd27d9520 100644 --- a/client/src/test/java/org/asynchttpclient/netty/NettyAsyncResponseTest.java +++ b/client/src/test/java/org/asynchttpclient/netty/NettyAsyncResponseTest.java @@ -15,6 +15,7 @@ import static io.netty.handler.codec.http.HttpHeaderNames.SET_COOKIE; import static org.testng.Assert.*; import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.cookie.Cookie; import java.text.SimpleDateFormat; import java.util.Date; @@ -23,7 +24,6 @@ import java.util.TimeZone; import org.asynchttpclient.HttpResponseHeaders; -import org.asynchttpclient.cookie.Cookie; import org.testng.annotations.Test; public class NettyAsyncResponseTest { @@ -44,7 +44,7 @@ public void testCookieParseExpires() { assertEquals(cookies.size(), 1); Cookie cookie = cookies.get(0); - assertTrue(cookie.getMaxAge() >= 58 && cookie.getMaxAge() <= 60); + assertTrue(cookie.maxAge() >= 58 && cookie.maxAge() <= 60); } @Test(groups = "standalone") @@ -57,7 +57,7 @@ public void testCookieParseMaxAge() { assertEquals(cookies.size(), 1); Cookie cookie = cookies.get(0); - assertEquals(cookie.getMaxAge(), 60); + assertEquals(cookie.maxAge(), 60); } @Test(groups = "standalone") @@ -70,6 +70,6 @@ public void testCookieParseWeirdExpiresValue() { assertEquals(cookies.size(), 1); Cookie cookie = cookies.get(0); - assertEquals(cookie.getMaxAge(), Long.MIN_VALUE); + assertEquals(cookie.maxAge(), Long.MIN_VALUE); } } diff --git a/extras/simple/src/main/java/org/asynchttpclient/extras/simple/SimpleAsyncHttpClient.java b/extras/simple/src/main/java/org/asynchttpclient/extras/simple/SimpleAsyncHttpClient.java index 2eb51109e9..e32a54bfc0 100644 --- a/extras/simple/src/main/java/org/asynchttpclient/extras/simple/SimpleAsyncHttpClient.java +++ b/extras/simple/src/main/java/org/asynchttpclient/extras/simple/SimpleAsyncHttpClient.java @@ -16,6 +16,7 @@ import static org.asynchttpclient.Dsl.*; import static org.asynchttpclient.util.MiscUtils.*; import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.cookie.Cookie; import io.netty.handler.ssl.SslContext; import java.io.Closeable; @@ -41,7 +42,6 @@ import org.asynchttpclient.RequestBuilder; import org.asynchttpclient.Response; import org.asynchttpclient.SslEngineFactory; -import org.asynchttpclient.cookie.Cookie; import org.asynchttpclient.handler.ProgressAsyncHandler; import org.asynchttpclient.handler.resumable.ResumableAsyncHandler; import org.asynchttpclient.handler.resumable.ResumableIOExceptionFilter; diff --git a/pom.xml b/pom.xml index a299e12ebf..d03e4d56de 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,19 @@ + + + sonatype-snapshots + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + + 3.0.0 @@ -378,7 +391,7 @@ true 1.8 1.8 - 4.1.6.Final + 4.1.7.Final-SNAPSHOT 1.7.22 1.1.8 6.9.10