Skip to content

Commit 1157657

Browse files
committed
Fix path characters if they're not properly encoded, close AsyncHttpClient#884
1 parent 771789c commit 1157657

File tree

7 files changed

+106
-58
lines changed

7 files changed

+106
-58
lines changed

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

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

1818
import com.ning.http.client.cookie.Cookie;
1919
import com.ning.http.client.multipart.Part;
20-
import com.ning.http.util.QueryComputer;
20+
import com.ning.http.util.UriEncoder;
2121

2222
import java.io.InputStream;
2323
import java.util.Collection;
@@ -43,16 +43,16 @@ public RequestBuilder(String method, boolean disableUrlEncoding) {
4343
super(RequestBuilder.class, method, disableUrlEncoding);
4444
}
4545

46-
public RequestBuilder(String method, QueryComputer queryComputer) {
47-
super(RequestBuilder.class, method, queryComputer);
46+
public RequestBuilder(String method, UriEncoder uriEncoder) {
47+
super(RequestBuilder.class, method, uriEncoder);
4848
}
4949

5050
public RequestBuilder(Request prototype) {
5151
super(RequestBuilder.class, prototype);
5252
}
5353

54-
public RequestBuilder(Request prototype, QueryComputer queryComputer) {
55-
super(RequestBuilder.class, prototype, queryComputer);
54+
public RequestBuilder(Request prototype, UriEncoder uriEncoder) {
55+
super(RequestBuilder.class, prototype, uriEncoder);
5656
}
5757

5858
// Note: For now we keep the delegates in place even though they are not needed

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import com.ning.http.client.multipart.Part;
2525
import com.ning.http.client.uri.Uri;
2626
import com.ning.http.util.AsyncHttpProviderUtils;
27-
import com.ning.http.util.QueryComputer;
27+
import com.ning.http.util.UriEncoder;
2828

2929
import java.io.File;
3030
import java.io.InputStream;
@@ -278,29 +278,29 @@ public String toString() {
278278

279279
private final Class<T> derived;
280280
protected final RequestImpl request;
281-
protected QueryComputer queryComputer;
281+
protected UriEncoder uriEncoder;
282282
protected List<Param> queryParams;
283283
protected SignatureCalculator signatureCalculator;
284284

285285
protected RequestBuilderBase(Class<T> derived, String method, boolean disableUrlEncoding) {
286-
this(derived, method, QueryComputer.queryComputer(disableUrlEncoding));
286+
this(derived, method, UriEncoder.uriEncoder(disableUrlEncoding));
287287
}
288288

289-
protected RequestBuilderBase(Class<T> derived, String method, QueryComputer queryComputer) {
289+
protected RequestBuilderBase(Class<T> derived, String method, UriEncoder uriEncoder) {
290290
this.derived = derived;
291291
request = new RequestImpl();
292292
request.method = method;
293-
this.queryComputer = queryComputer;
293+
this.uriEncoder = uriEncoder;
294294
}
295295

296296
protected RequestBuilderBase(Class<T> derived, Request prototype) {
297-
this(derived, prototype, QueryComputer.URL_ENCODING_ENABLED_QUERY_COMPUTER);
297+
this(derived, prototype, UriEncoder.FIXING);
298298
}
299299

300-
protected RequestBuilderBase(Class<T> derived, Request prototype, QueryComputer queryComputer) {
300+
protected RequestBuilderBase(Class<T> derived, Request prototype, UriEncoder uriEncoder) {
301301
this.derived = derived;
302302
request = new RequestImpl(prototype);
303-
this.queryComputer = queryComputer;
303+
this.uriEncoder = uriEncoder;
304304
}
305305

306306
public T setUrl(String url) {
@@ -627,15 +627,15 @@ private void computeRequestLength() {
627627

628628
private void computeFinalUri() {
629629

630-
if (request.uri == null) {
630+
Uri originalUri = request.uri;
631+
if (originalUri == null) {
631632
logger.debug("setUrl hasn't been invoked. Using {}", DEFAULT_REQUEST_URL);
632633
request.uri = DEFAULT_REQUEST_URL;
633634
}
634635

635-
AsyncHttpProviderUtils.validateSupportedScheme(request.uri);
636+
AsyncHttpProviderUtils.validateSupportedScheme(originalUri);
636637

637-
String newQuery = queryComputer.computeFullQueryString(request.uri.getQuery(), queryParams);
638-
request.uri = request.uri.withNewQuery(newQuery);
638+
request.uri = uriEncoder.encode(originalUri, queryParams);
639639
}
640640

641641
public Request build() {

src/main/java/com/ning/http/client/oauth/OAuthSignatureCalculator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ public OAuthParameterSet(int size) {
230230
}
231231

232232
public OAuthParameterSet add(String key, String value) {
233-
Parameter p = new Parameter(UTF8UrlEncoder.encode(key), UTF8UrlEncoder.encode(value));
233+
Parameter p = new Parameter(UTF8UrlEncoder.encodeQueryElement(key), UTF8UrlEncoder.encodeQueryElement(value));
234234
allParameters.add(p);
235235
return this;
236236
}

src/main/java/com/ning/http/util/AsyncHttpProviderUtils.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ public final static byte[] contentToByte(List<HttpResponseBodyPart> bodyParts) t
8686
}
8787
}
8888

89-
@SuppressWarnings("resource")
9089
public final static InputStream contentToInputStream(List<HttpResponseBodyPart> bodyParts) throws UnsupportedEncodingException {
9190
return bodyParts.isEmpty() ? new ByteArrayInputStream(EMPTY_BYTE_ARRAY) : new HttpResponseBodyPartsInputStream(bodyParts);
9291
}
@@ -192,13 +191,4 @@ private static void encodeAndAppendFormParam(final StringBuilder sb, final CharS
192191
}
193192
sb.append('&');
194193
}
195-
196-
public static void encodeAndAppendQueryParam(final StringBuilder sb, final CharSequence name, final CharSequence value) {
197-
UTF8UrlEncoder.encodeAndAppendQueryElement(sb, name);
198-
if (value != null) {
199-
sb.append('=');
200-
UTF8UrlEncoder.encodeAndAppendQueryElement(sb, value);
201-
}
202-
sb.append('&');
203-
}
204194
}

src/main/java/com/ning/http/util/UTF8UrlEncoder.java

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ public final class UTF8UrlEncoder {
2929
*/
3030
public final static BitSet RFC3986_UNRESERVED_CHARS = new BitSet(256);
3131
public final static BitSet RFC3986_RESERVED_CHARS = new BitSet(256);
32+
public final static BitSet RFC3986_SUBDELIM_CHARS = new BitSet(256);
33+
public final static BitSet BUILT_PATH_UNTOUCHED_CHARS = new BitSet(256);
3234
public final static BitSet BUILT_QUERY_UNTOUCHED_CHARS = new BitSet(256);
3335
// http://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
3436
public final static BitSet FORM_URL_ENCODED_SAFE_CHARS = new BitSet(256);
@@ -51,6 +53,18 @@ public final class UTF8UrlEncoder {
5153
RFC3986_UNRESERVED_CHARS.set('_');
5254
RFC3986_UNRESERVED_CHARS.set('~');
5355

56+
RFC3986_SUBDELIM_CHARS.set('!');
57+
RFC3986_SUBDELIM_CHARS.set('$');
58+
RFC3986_SUBDELIM_CHARS.set('&');
59+
RFC3986_SUBDELIM_CHARS.set('\'');
60+
RFC3986_SUBDELIM_CHARS.set('(');
61+
RFC3986_SUBDELIM_CHARS.set(')');
62+
RFC3986_SUBDELIM_CHARS.set('*');
63+
RFC3986_SUBDELIM_CHARS.set('+');
64+
RFC3986_SUBDELIM_CHARS.set(',');
65+
RFC3986_SUBDELIM_CHARS.set(';');
66+
RFC3986_SUBDELIM_CHARS.set('=');
67+
5468
FORM_URL_ENCODED_SAFE_CHARS.set('-');
5569
FORM_URL_ENCODED_SAFE_CHARS.set('.');
5670
FORM_URL_ENCODED_SAFE_CHARS.set('_');
@@ -74,7 +88,14 @@ public final class UTF8UrlEncoder {
7488
RFC3986_RESERVED_CHARS.set('#');
7589
RFC3986_RESERVED_CHARS.set('[');
7690
RFC3986_RESERVED_CHARS.set(']');
77-
91+
92+
BUILT_PATH_UNTOUCHED_CHARS.or(RFC3986_UNRESERVED_CHARS);
93+
BUILT_PATH_UNTOUCHED_CHARS.set('%');
94+
BUILT_PATH_UNTOUCHED_CHARS.or(RFC3986_SUBDELIM_CHARS);
95+
BUILT_PATH_UNTOUCHED_CHARS.set(':');
96+
BUILT_PATH_UNTOUCHED_CHARS.set('@');
97+
BUILT_PATH_UNTOUCHED_CHARS.set('/');
98+
7899
BUILT_QUERY_UNTOUCHED_CHARS.or(RFC3986_UNRESERVED_CHARS);
79100
BUILT_QUERY_UNTOUCHED_CHARS.or(RFC3986_RESERVED_CHARS);
80101
BUILT_QUERY_UNTOUCHED_CHARS.set('%');
@@ -85,16 +106,22 @@ public final class UTF8UrlEncoder {
85106
private UTF8UrlEncoder() {
86107
}
87108

88-
public static String encode(String input) {
89-
StringBuilder sb = new StringBuilder(input.length() + 16);
90-
encodeAndAppendQueryElement(sb, input);
109+
public static String encodePath(String input) {
110+
StringBuilder sb = new StringBuilder(input.length() + 6);
111+
appendEncoded(sb, input, BUILT_PATH_UNTOUCHED_CHARS, false);
91112
return sb.toString();
92113
}
93-
114+
94115
public static StringBuilder encodeAndAppendQuery(StringBuilder sb, String query) {
95116
return appendEncoded(sb, query, BUILT_QUERY_UNTOUCHED_CHARS, false);
96117
}
97118

119+
public static String encodeQueryElement(String input) {
120+
StringBuilder sb = new StringBuilder(input.length() + 6);
121+
encodeAndAppendQueryElement(sb, input);
122+
return sb.toString();
123+
}
124+
98125
public static StringBuilder encodeAndAppendQueryElement(StringBuilder sb, CharSequence input) {
99126
return appendEncoded(sb, input, RFC3986_UNRESERVED_CHARS, false);
100127
}

src/main/java/com/ning/http/util/QueryComputer.java renamed to src/main/java/com/ning/http/util/UriEncoder.java

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,36 @@
1313
package com.ning.http.util;
1414

1515
import static com.ning.http.util.MiscUtils.isNonEmpty;
16-
import static com.ning.http.util.AsyncHttpProviderUtils.encodeAndAppendQueryParam;
1716
import static com.ning.http.util.UTF8UrlEncoder.encodeAndAppendQuery;
17+
1818
import com.ning.http.client.Param;
19+
import com.ning.http.client.uri.Uri;
1920

2021
import java.util.List;
2122

22-
public enum QueryComputer {
23+
public enum UriEncoder {
24+
25+
FIXING {
2326

24-
URL_ENCODING_ENABLED_QUERY_COMPUTER {
27+
public String encodePath(String path) {
28+
return UTF8UrlEncoder.encodePath(path);
29+
}
2530

26-
private final void encodeAndAppendQueryParams(final StringBuilder sb, final List<Param> queryParams) {
31+
private void encodeAndAppendQueryParam(final StringBuilder sb, final CharSequence name, final CharSequence value) {
32+
UTF8UrlEncoder.encodeAndAppendQueryElement(sb, name);
33+
if (value != null) {
34+
sb.append('=');
35+
UTF8UrlEncoder.encodeAndAppendQueryElement(sb, value);
36+
}
37+
sb.append('&');
38+
}
39+
40+
private void encodeAndAppendQueryParams(final StringBuilder sb, final List<Param> queryParams) {
2741
for (Param param : queryParams)
2842
encodeAndAppendQueryParam(sb, param.getName(), param.getValue());
2943
}
30-
31-
protected final String withQueryWithParams(final String query, final List<Param> queryParams) {
44+
45+
protected String withQueryWithParams(final String query, final List<Param> queryParams) {
3246
// concatenate encoded query + encoded query params
3347
StringBuilder sb = StringUtils.stringBuilder();
3448
encodeAndAppendQuery(sb, query);
@@ -38,14 +52,14 @@ protected final String withQueryWithParams(final String query, final List<Param>
3852
return sb.toString();
3953
}
4054

41-
protected final String withQueryWithoutParams(final String query) {
55+
protected String withQueryWithoutParams(final String query) {
4256
// encode query
4357
StringBuilder sb = StringUtils.stringBuilder();
4458
encodeAndAppendQuery(sb, query);
4559
return sb.toString();
4660
}
4761

48-
protected final String withoutQueryWithParams(final List<Param> queryParams) {
62+
protected String withoutQueryWithParams(final List<Param> queryParams) {
4963
// concatenate encoded query params
5064
StringBuilder sb = StringUtils.stringBuilder();
5165
encodeAndAppendQueryParams(sb, queryParams);
@@ -54,21 +68,25 @@ protected final String withoutQueryWithParams(final List<Param> queryParams) {
5468
}
5569
}, //
5670

57-
URL_ENCODING_DISABLED_QUERY_COMPUTER {
71+
RAW {
5872

59-
private final void appendRawQueryParam(StringBuilder sb, String name, String value) {
73+
public String encodePath(String path) {
74+
return path;
75+
}
76+
77+
private void appendRawQueryParam(StringBuilder sb, String name, String value) {
6078
sb.append(name);
6179
if (value != null)
6280
sb.append('=').append(value);
6381
sb.append('&');
6482
}
65-
66-
private final void appendRawQueryParams(final StringBuilder sb, final List<Param> queryParams) {
83+
84+
private void appendRawQueryParams(final StringBuilder sb, final List<Param> queryParams) {
6785
for (Param param : queryParams)
6886
appendRawQueryParam(sb, param.getName(), param.getValue());
6987
}
70-
71-
protected final String withQueryWithParams(final String query, final List<Param> queryParams) {
88+
89+
protected String withQueryWithParams(final String query, final List<Param> queryParams) {
7290
// concatenate raw query + raw query params
7391
StringBuilder sb = StringUtils.stringBuilder();
7492
sb.append(query);
@@ -77,12 +95,12 @@ protected final String withQueryWithParams(final String query, final List<Param>
7795
return sb.toString();
7896
}
7997

80-
protected final String withQueryWithoutParams(final String query) {
98+
protected String withQueryWithoutParams(final String query) {
8199
// return raw query as is
82100
return query;
83101
}
84102

85-
protected final String withoutQueryWithParams(final List<Param> queryParams) {
103+
protected String withoutQueryWithParams(final List<Param> queryParams) {
86104
// concatenate raw queryParams
87105
StringBuilder sb = StringUtils.stringBuilder();
88106
appendRawQueryParams(sb, queryParams);
@@ -91,8 +109,8 @@ protected final String withoutQueryWithParams(final List<Param> queryParams) {
91109
}
92110
};
93111

94-
public static QueryComputer queryComputer(boolean disableUrlEncoding) {
95-
return disableUrlEncoding ? URL_ENCODING_DISABLED_QUERY_COMPUTER : URL_ENCODING_ENABLED_QUERY_COMPUTER;
112+
public static UriEncoder uriEncoder(boolean disableUrlEncoding) {
113+
return disableUrlEncoding ? RAW : FIXING;
96114
}
97115

98116
protected abstract String withQueryWithParams(final String query, final List<Param> queryParams);
@@ -109,7 +127,20 @@ private final String withoutQuery(final List<Param> queryParams) {
109127
return isNonEmpty(queryParams) ? withoutQueryWithParams(queryParams) : null;
110128
}
111129

112-
public final String computeFullQueryString(final String query, final List<Param> queryParams) {
130+
public Uri encode(Uri uri, List<Param> queryParams) {
131+
String newPath = encodePath(uri.getPath());
132+
String newQuery = encodeQuery(uri.getQuery(), queryParams);
133+
return new Uri(uri.getScheme(),//
134+
uri.getUserInfo(),//
135+
uri.getHost(),//
136+
uri.getPort(),//
137+
newPath,//
138+
newQuery);
139+
}
140+
141+
protected abstract String encodePath(String path);
142+
143+
private final String encodeQuery(final String query, final List<Param> queryParams) {
113144
return isNonEmpty(query) ? withQuery(query, queryParams) : withoutQuery(queryParams);
114145
}
115146
}

src/test/java/com/ning/http/util/UTF8UrlCodecTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,19 @@ public class UTF8UrlCodecTest {
2222

2323
@Test(groups = "fast")
2424
public void testBasics() {
25-
Assert.assertEquals(UTF8UrlEncoder.encode("foobar"), "foobar");
26-
Assert.assertEquals(UTF8UrlEncoder.encode("a&b"), "a%26b");
27-
Assert.assertEquals(UTF8UrlEncoder.encode("a+b"), "a%2Bb");
25+
Assert.assertEquals(UTF8UrlEncoder.encodeQueryElement("foobar"), "foobar");
26+
Assert.assertEquals(UTF8UrlEncoder.encodeQueryElement("a&b"), "a%26b");
27+
Assert.assertEquals(UTF8UrlEncoder.encodeQueryElement("a+b"), "a%2Bb");
2828
}
2929

3030
@Test(groups = "fast")
3131
public void testNonBmp() {
3232
// Plane 1
33-
Assert.assertEquals(UTF8UrlEncoder.encode("\uD83D\uDCA9"), "%F0%9F%92%A9");
33+
Assert.assertEquals(UTF8UrlEncoder.encodeQueryElement("\uD83D\uDCA9"), "%F0%9F%92%A9");
3434
// Plane 2
35-
Assert.assertEquals(UTF8UrlEncoder.encode("\ud84c\uddc8 \ud84f\udfef"), "%F0%A3%87%88%20%F0%A3%BF%AF");
35+
Assert.assertEquals(UTF8UrlEncoder.encodeQueryElement("\ud84c\uddc8 \ud84f\udfef"), "%F0%A3%87%88%20%F0%A3%BF%AF");
3636
// Plane 15
37-
Assert.assertEquals(UTF8UrlEncoder.encode("\udb80\udc01"), "%F3%B0%80%81");
37+
Assert.assertEquals(UTF8UrlEncoder.encodeQueryElement("\udb80\udc01"), "%F3%B0%80%81");
3838
}
3939

4040
@Test(groups = "fast")

0 commit comments

Comments
 (0)