Skip to content

Commit 89d5eda

Browse files
author
Stephane Landelle
committed
Merge pull request AsyncHttpClient#285 from AsyncHttpClient/netty-cookie-decoder
Fork Netty's Cookie Decoder, close AsyncHttpClient#283
2 parents 354fad3 + 4d7fa21 commit 89d5eda

File tree

12 files changed

+686
-103
lines changed

12 files changed

+686
-103
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@
465465
<exclude>**/NettyAsyncHttpProvider$*</exclude>
466466
<exclude>**/NettyResponse</exclude>
467467
<exclude>**/AsyncHttpProviderUtils</exclude>
468+
<exclude>**/Cookie</exclude>
468469
</excludes>
469470
</configuration>
470471
<executions>

src/main/java/com/ning/http/client/Cookie.java

Lines changed: 169 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,35 +20,93 @@
2020
import java.util.Set;
2121
import java.util.TreeSet;
2222

23-
public class Cookie {
23+
public class Cookie implements Comparable<Cookie>{
2424
private final String domain;
2525
private final String name;
2626
private final String value;
2727
private final String path;
2828
private final int maxAge;
2929
private final boolean secure;
3030
private final int version;
31+
private final boolean httpOnly;
32+
private final boolean discard;
33+
private final String comment;
34+
private final String commentUrl;
35+
3136
private Set<Integer> ports = Collections.emptySet();
3237
private Set<Integer> unmodifiablePorts = ports;
3338

39+
@Deprecated
3440
public Cookie(String domain, String name, String value, String path, int maxAge, boolean secure) {
35-
this.domain = domain;
36-
this.name = name;
37-
this.value = value;
38-
this.path = path;
39-
this.maxAge = maxAge;
40-
this.secure = secure;
41-
this.version = 1;
41+
this(domain, name, value, path, maxAge, secure, 1);
4242
}
4343

44+
@Deprecated
4445
public Cookie(String domain, String name, String value, String path, int maxAge, boolean secure, int version) {
45-
this.domain = domain;
46+
this(domain, name, value, path, maxAge, secure, version, false, false, null, null, Collections.<Integer> emptySet());
47+
}
48+
49+
public Cookie(String domain, String name, String value, String path, int maxAge, boolean secure, int version, boolean httpOnly, boolean discard, String comment, String commentUrl, Iterable<Integer> ports) {
50+
51+
if (name == null) {
52+
throw new NullPointerException("name");
53+
}
54+
name = name.trim();
55+
if (name.length() == 0) {
56+
throw new IllegalArgumentException("empty name");
57+
}
58+
59+
for (int i = 0; i < name.length(); i++) {
60+
char c = name.charAt(i);
61+
if (c > 127) {
62+
throw new IllegalArgumentException("name contains non-ascii character: " + name);
63+
}
64+
65+
// Check prohibited characters.
66+
switch (c) {
67+
case '\t':
68+
case '\n':
69+
case 0x0b:
70+
case '\f':
71+
case '\r':
72+
case ' ':
73+
case ',':
74+
case ';':
75+
case '=':
76+
throw new IllegalArgumentException("name contains one of the following prohibited characters: " + "=,; \\t\\r\\n\\v\\f: " + name);
77+
}
78+
}
79+
80+
if (name.charAt(0) == '$') {
81+
throw new IllegalArgumentException("name starting with '$' not allowed: " + name);
82+
}
83+
84+
if (value == null) {
85+
throw new NullPointerException("value");
86+
}
87+
4688
this.name = name;
4789
this.value = value;
48-
this.path = path;
90+
this.domain = validateValue("domain", domain);
91+
this.path = validateValue("path", path);
4992
this.maxAge = maxAge;
5093
this.secure = secure;
5194
this.version = version;
95+
this.httpOnly = httpOnly;
96+
97+
if (version > 0) {
98+
this.comment = validateValue("comment", comment);
99+
} else {
100+
this.comment = null;
101+
}
102+
if (version > 1) {
103+
this.discard = discard;
104+
this.commentUrl = validateValue("commentUrl", commentUrl);
105+
setPorts(ports);
106+
} else {
107+
this.discard = false;
108+
this.commentUrl = null;
109+
}
52110
}
53111

54112
public String getDomain() {
@@ -79,35 +137,30 @@ public int getVersion() {
79137
return version;
80138
}
81139

140+
public String getComment() {
141+
return this.comment;
142+
}
143+
144+
public String getCommentUrl() {
145+
return this.commentUrl;
146+
}
147+
148+
public boolean isHttpOnly() {
149+
return httpOnly;
150+
}
151+
152+
public boolean isDiscard() {
153+
return discard;
154+
}
155+
82156
public Set<Integer> getPorts() {
83157
if (unmodifiablePorts == null) {
84158
unmodifiablePorts = Collections.unmodifiableSet(ports);
85159
}
86160
return unmodifiablePorts;
87161
}
88162

89-
public void setPorts(int... ports) {
90-
if (ports == null) {
91-
throw new NullPointerException("ports");
92-
}
93-
94-
int[] portsCopy = ports.clone();
95-
if (portsCopy.length == 0) {
96-
unmodifiablePorts = this.ports = Collections.emptySet();
97-
} else {
98-
Set<Integer> newPorts = new TreeSet<Integer>();
99-
for (int p : portsCopy) {
100-
if (p <= 0 || p > 65535) {
101-
throw new IllegalArgumentException("port out of range: " + p);
102-
}
103-
newPorts.add(Integer.valueOf(p));
104-
}
105-
this.ports = newPorts;
106-
unmodifiablePorts = null;
107-
}
108-
}
109-
110-
public void setPorts(Iterable<Integer> ports) {
163+
private void setPorts(Iterable<Integer> ports) {
111164
Set<Integer> newPorts = new TreeSet<Integer>();
112165
for (int p : ports) {
113166
if (p <= 0 || p > 65535) {
@@ -125,7 +178,89 @@ public void setPorts(Iterable<Integer> ports) {
125178

126179
@Override
127180
public String toString() {
128-
return String.format("Cookie: domain=%s, name=%s, value=%s, path=%s, maxAge=%d, secure=%s",
129-
domain, name, value, path, maxAge, secure);
181+
StringBuilder buf = new StringBuilder();
182+
buf.append(getName());
183+
buf.append('=');
184+
buf.append(getValue());
185+
if (getDomain() != null) {
186+
buf.append("; domain=");
187+
buf.append(getDomain());
188+
}
189+
if (getPath() != null) {
190+
buf.append("; path=");
191+
buf.append(getPath());
192+
}
193+
if (getComment() != null) {
194+
buf.append("; comment=");
195+
buf.append(getComment());
196+
}
197+
if (getMaxAge() >= 0) {
198+
buf.append("; maxAge=");
199+
buf.append(getMaxAge());
200+
buf.append('s');
201+
}
202+
if (isSecure()) {
203+
buf.append("; secure");
204+
}
205+
if (isHttpOnly()) {
206+
buf.append("; HTTPOnly");
207+
}
208+
return buf.toString();
209+
}
210+
211+
private String validateValue(String name, String value) {
212+
if (value == null) {
213+
return null;
214+
}
215+
value = value.trim();
216+
if (value.length() == 0) {
217+
return null;
218+
}
219+
for (int i = 0; i < value.length(); i++) {
220+
char c = value.charAt(i);
221+
switch (c) {
222+
case '\r':
223+
case '\n':
224+
case '\f':
225+
case 0x0b:
226+
case ';':
227+
throw new IllegalArgumentException(name + " contains one of the following prohibited characters: " + ";\\r\\n\\f\\v (" + value + ')');
228+
}
229+
}
230+
return value;
231+
}
232+
233+
public int compareTo(Cookie c) {
234+
int v;
235+
v = getName().compareToIgnoreCase(c.getName());
236+
if (v != 0) {
237+
return v;
238+
}
239+
240+
if (getPath() == null) {
241+
if (c.getPath() != null) {
242+
return -1;
243+
}
244+
} else if (c.getPath() == null) {
245+
return 1;
246+
} else {
247+
v = getPath().compareTo(c.getPath());
248+
if (v != 0) {
249+
return v;
250+
}
251+
}
252+
253+
if (getDomain() == null) {
254+
if (c.getDomain() != null) {
255+
return -1;
256+
}
257+
} else if (c.getDomain() == null) {
258+
return 1;
259+
} else {
260+
v = getDomain().compareToIgnoreCase(c.getDomain());
261+
return v;
262+
}
263+
264+
return 0;
130265
}
131266
}

src/main/java/com/ning/http/client/providers/apache/ApacheResponse.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import static com.ning.http.util.MiscUtil.isNonEmpty;
1616

17+
import com.ning.org.jboss.netty.handler.codec.http.CookieDecoder;
1718
import com.ning.http.client.Cookie;
1819
import com.ning.http.client.FluentCaseInsensitiveStringsMap;
1920
import com.ning.http.client.HttpResponseBodyPart;
@@ -31,7 +32,7 @@
3132
import java.util.Collections;
3233
import java.util.List;
3334
import java.util.Map;
34-
35+
import java.util.Set;
3536

3637
public class ApacheResponse implements Response {
3738
private final static String DEFAULT_CHARSET = "ISO-8859-1";
@@ -161,8 +162,8 @@ public List<Cookie> getCookies() {
161162
// TODO: ask for parsed header
162163
List<String> v = header.getValue();
163164
for (String value : v) {
164-
Cookie cookie = AsyncHttpProviderUtils.parseCookie(value);
165-
localCookies.add(cookie);
165+
Set<Cookie> cookies = CookieDecoder.decode(value);
166+
localCookies.addAll(cookies);
166167
}
167168
}
168169
}

src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProvider.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515

1616
import static com.ning.http.util.MiscUtil.isNonEmpty;
1717

18+
import com.ning.org.jboss.netty.handler.codec.http.CookieDecoder;
1819
import com.ning.http.client.AsyncHandler;
1920
import com.ning.http.client.AsyncHttpClientConfig;
2021
import com.ning.http.client.AsyncHttpProvider;
2122
import com.ning.http.client.AsyncHttpProviderConfig;
2223
import com.ning.http.client.Body;
2324
import com.ning.http.client.BodyGenerator;
24-
import com.ning.http.client.ConnectionPoolKeyStrategy;
2525
import com.ning.http.client.ConnectionsPool;
2626
import com.ning.http.client.Cookie;
2727
import com.ning.http.client.FluentCaseInsensitiveStringsMap;
@@ -1666,8 +1666,9 @@ private static Request newRequest(final URI uri,
16661666
builder.setQueryParameters(null);
16671667
}
16681668
for (String cookieStr : response.getHeaders().values(Header.Cookie)) {
1669-
Cookie c = AsyncHttpProviderUtils.parseCookie(cookieStr);
1670-
builder.addOrReplaceCookie(c);
1669+
for (Cookie c : CookieDecoder.decode(cookieStr)) {
1670+
builder.addOrReplaceCookie(c);
1671+
}
16711672
}
16721673
return builder.build();
16731674

src/main/java/com/ning/http/client/providers/jdk/JDKResponse.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import static com.ning.http.util.MiscUtil.isNonEmpty;
1616

17+
import com.ning.org.jboss.netty.handler.codec.http.CookieDecoder;
1718
import com.ning.http.client.Cookie;
1819
import com.ning.http.client.FluentCaseInsensitiveStringsMap;
1920
import com.ning.http.client.HttpResponseBodyPart;
@@ -32,6 +33,7 @@
3233
import java.util.Collections;
3334
import java.util.List;
3435
import java.util.Map;
36+
import java.util.Set;
3537
import java.util.concurrent.atomic.AtomicBoolean;
3638

3739

@@ -175,8 +177,8 @@ public List<Cookie> getCookies() {
175177
// TODO: ask for parsed header
176178
List<String> v = header.getValue();
177179
for (String value : v) {
178-
Cookie cookie = AsyncHttpProviderUtils.parseCookie(value);
179-
localCookies.add(cookie);
180+
Set<Cookie> cookies = CookieDecoder.decode(value);
181+
localCookies.addAll(cookies);
180182
}
181183
}
182184
}

src/main/java/com/ning/http/client/providers/netty/NettyAsyncHttpProvider.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import static com.ning.http.util.MiscUtil.isNonEmpty;
1919

20+
import com.ning.org.jboss.netty.handler.codec.http.CookieDecoder;
2021
import com.ning.http.client.AsyncHandler;
2122
import com.ning.http.client.AsyncHandler.STATE;
2223
import com.ning.http.client.AsyncHttpClientConfig;
@@ -585,7 +586,7 @@ public void operationComplete(ChannelFuture cf) {
585586
int delay = Math.min(config.getIdleConnectionTimeoutInMs(), requestTimeout(config, future.getRequest().getPerRequestConfig()));
586587
if (delay != -1 && !future.isDone() && !future.isCancelled()) {
587588
ReaperFuture reaperFuture = new ReaperFuture(future);
588-
Future scheduledFuture = config.reaper().scheduleAtFixedRate(reaperFuture, 0, delay, TimeUnit.MILLISECONDS);
589+
Future<?> scheduledFuture = config.reaper().scheduleAtFixedRate(reaperFuture, 0, delay, TimeUnit.MILLISECONDS);
589590
reaperFuture.setScheduledFuture(scheduledFuture);
590591
future.setReaperFuture(reaperFuture);
591592
}
@@ -2083,13 +2084,15 @@ private boolean redirect(Request request,
20832084

20842085
log.debug("Redirecting to {}", newUrl);
20852086
for (String cookieStr : future.getHttpResponse().getHeaders(HttpHeaders.Names.SET_COOKIE)) {
2086-
Cookie c = AsyncHttpProviderUtils.parseCookie(cookieStr);
2087-
nBuilder.addOrReplaceCookie(c);
2087+
for (Cookie c : CookieDecoder.decode(cookieStr)) {
2088+
nBuilder.addOrReplaceCookie(c);
2089+
}
20882090
}
20892091

20902092
for (String cookieStr : future.getHttpResponse().getHeaders(HttpHeaders.Names.SET_COOKIE2)) {
2091-
Cookie c = AsyncHttpProviderUtils.parseCookie(cookieStr);
2092-
nBuilder.addOrReplaceCookie(c);
2093+
for (Cookie c : CookieDecoder.decode(cookieStr)) {
2094+
nBuilder.addOrReplaceCookie(c);
2095+
}
20932096
}
20942097

20952098
AsyncCallable ac = new AsyncCallable(future) {

0 commit comments

Comments
 (0)