You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2020/09/12 12:39:03 UTC
[httpcomponents-core] 10/18: RFC 3986 conformance: revised URI
parsing and formatting; URLEncodedUtils deprecated in favor of WWWFormCodec
This is an automated email from the ASF dual-hosted git repository.
olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git
commit 9885d2c74a651016d9f28dd8b6e93409f2820f14
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Mon Jul 20 17:30:22 2020 +0200
RFC 3986 conformance: revised URI parsing and formatting; URLEncodedUtils deprecated in favor of WWWFormCodec
---
.../hc/core5/http2/examples/H2GreetingServer.java | 4 +-
.../hc/core5/testing/framework/FrameworkTest.java | 5 +-
.../framework/TestingFrameworkRequestHandler.java | 7 +-
.../hc/core5/http/io/entity/EntityUtils.java | 4 +-
.../hc/core5/http/io/entity/HttpEntities.java | 4 +-
.../http/nio/entity/AsyncEntityProducers.java | 4 +-
.../http/nio/support/AsyncRequestBuilder.java | 4 +-
.../java/org/apache/hc/core5/net/PercentCodec.java | 163 +++++++++
.../java/org/apache/hc/core5/net/URIBuilder.java | 233 +++++++++---
.../org/apache/hc/core5/net/URLEncodedUtils.java | 303 ++--------------
.../java/org/apache/hc/core5/net/WWWFormCodec.java | 83 +++++
.../hc/core5/http/NameValuePairListMatcher.java | 85 +++++
.../hc/core5/http/io/entity/TestEntityUtils.java | 4 +-
.../org/apache/hc/core5/net/TestPercentCodec.java | 70 ++++
.../org/apache/hc/core5/net/TestURIBuilder.java | 206 ++++++++---
.../apache/hc/core5/net/TestURLEncodedUtils.java | 394 ---------------------
.../org/apache/hc/core5/net/TestWWWFormCodec.java | 123 +++++++
17 files changed, 913 insertions(+), 783 deletions(-)
diff --git a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2GreetingServer.java b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2GreetingServer.java
index b1dc3e1..551be6f 100644
--- a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2GreetingServer.java
+++ b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2GreetingServer.java
@@ -63,7 +63,7 @@ import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.http2.config.H2Config;
import org.apache.hc.core5.http2.impl.nio.bootstrap.H2ServerBootstrap;
import org.apache.hc.core5.io.CloseMode;
-import org.apache.hc.core5.net.URLEncodedUtils;
+import org.apache.hc.core5.net.WWWFormCodec;
import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.hc.core5.reactor.ListenerEndpoint;
import org.apache.hc.core5.util.TimeValue;
@@ -174,7 +174,7 @@ public class H2GreetingServer {
if (contentType != null && contentType.isSameMimeType(ContentType.APPLICATION_FORM_URLENCODED)) {
// decoding the form entity into key/value pairs:
- final List<NameValuePair> args = URLEncodedUtils.parse(httpEntity, contentType.getCharset());
+ final List<NameValuePair> args = WWWFormCodec.parse(httpEntity, contentType.getCharset());
if (!args.isEmpty()) {
name = args.get(0).getValue();
}
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/FrameworkTest.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/FrameworkTest.java
index 326a8f1..9685487 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/FrameworkTest.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/FrameworkTest.java
@@ -46,7 +46,7 @@ import java.util.List;
import java.util.Map;
import org.apache.hc.core5.http.NameValuePair;
-import org.apache.hc.core5.net.URLEncodedUtils;
+import org.apache.hc.core5.net.URIBuilder;
public class FrameworkTest {
@@ -112,7 +112,8 @@ public class FrameworkTest {
if (path != null) {
final URI uri = path.startsWith("/") ? new URI("http://localhost:8080" + path) :
new URI("http://localhost:8080/");
- final List<NameValuePair> params = URLEncodedUtils.parse(uri, StandardCharsets.UTF_8);
+ final URIBuilder uriBuilder = new URIBuilder(uri, StandardCharsets.UTF_8);
+ final List<NameValuePair> params = uriBuilder.getQueryParams();
@SuppressWarnings("unchecked")
final Map<String, Object> queryMap = (Map<String, Object>) request.get(QUERY);
for (final NameValuePair param : params) {
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkRequestHandler.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkRequestHandler.java
index 07bd7d5..e757b62 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkRequestHandler.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFrameworkRequestHandler.java
@@ -45,17 +45,17 @@ import java.util.Map.Entry;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.ProtocolVersion;
import org.apache.hc.core5.http.io.HttpRequestHandler;
-import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.protocol.HttpContext;
-import org.apache.hc.core5.net.URLEncodedUtils;
+import org.apache.hc.core5.net.URIBuilder;
public class TestingFrameworkRequestHandler implements HttpRequestHandler {
protected Throwable thrown;
@@ -137,7 +137,8 @@ public class TestingFrameworkRequestHandler implements HttpRequestHandler {
final Map<String, String> expectedQuery = (Map<String, String>) requestExpectations.get(QUERY);
if (expectedQuery != null) {
final URI uri = request.getUri();
- final List<NameValuePair> actualParams = URLEncodedUtils.parse(uri, StandardCharsets.UTF_8);
+ final URIBuilder uriBuilder = new URIBuilder(uri, StandardCharsets.UTF_8);
+ final List<NameValuePair> actualParams = uriBuilder.getQueryParams();
final Map<String, String> actualParamsMap = new HashMap<>();
for (final NameValuePair actualParam : actualParams) {
actualParamsMap.put(actualParam.getName(), actualParam.getValue());
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/EntityUtils.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/EntityUtils.java
index 0e6d70a..f4b44a3 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/EntityUtils.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/EntityUtils.java
@@ -45,7 +45,7 @@ import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.io.Closer;
-import org.apache.hc.core5.net.URLEncodedUtils;
+import org.apache.hc.core5.net.WWWFormCodec;
import org.apache.hc.core5.util.Args;
import org.apache.hc.core5.util.ByteArrayBuffer;
import org.apache.hc.core5.util.CharArrayBuffer;
@@ -412,7 +412,7 @@ public final class EntityUtils {
if (buf.isEmpty()) {
return Collections.emptyList();
}
- return URLEncodedUtils.parse(buf, charset, '&');
+ return WWWFormCodec.parse(buf, charset);
}
}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntities.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntities.java
index 597e1ea..6331733 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntities.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntities.java
@@ -46,7 +46,7 @@ import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.io.IOCallback;
-import org.apache.hc.core5.net.URLEncodedUtils;
+import org.apache.hc.core5.net.WWWFormCodec;
import org.apache.hc.core5.util.Args;
/**
@@ -88,7 +88,7 @@ public final class HttpEntities {
final ContentType contentType = charset != null ?
ContentType.APPLICATION_FORM_URLENCODED.withCharset(charset) :
ContentType.APPLICATION_FORM_URLENCODED;
- return create(URLEncodedUtils.format(parameters, contentType.getCharset()), contentType);
+ return create(WWWFormCodec.format(parameters, contentType.getCharset()), contentType);
}
public static HttpEntity create(final IOCallback<OutputStream> callback, final ContentType contentType) {
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/AsyncEntityProducers.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/AsyncEntityProducers.java
index 3fd20d1..b34438d 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/AsyncEntityProducers.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/entity/AsyncEntityProducers.java
@@ -45,7 +45,7 @@ import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.nio.AsyncEntityProducer;
import org.apache.hc.core5.http.nio.DataStreamChannel;
import org.apache.hc.core5.http.nio.StreamChannel;
-import org.apache.hc.core5.net.URLEncodedUtils;
+import org.apache.hc.core5.net.WWWFormCodec;
/**
* {AsyncEntityProducer} factory methods.
@@ -82,7 +82,7 @@ public final class AsyncEntityProducers {
final ContentType contentType = charset != null ?
ContentType.APPLICATION_FORM_URLENCODED.withCharset(charset) :
ContentType.APPLICATION_FORM_URLENCODED;
- return create(URLEncodedUtils.format(parameters, contentType.getCharset()), contentType);
+ return create(WWWFormCodec.format(parameters, contentType.getCharset()), contentType);
}
public static AsyncEntityProducer createBinary(
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncRequestBuilder.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncRequestBuilder.java
index 81149b2..720980e 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncRequestBuilder.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncRequestBuilder.java
@@ -50,7 +50,7 @@ import org.apache.hc.core5.http.nio.AsyncRequestProducer;
import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer;
import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer;
import org.apache.hc.core5.net.URIBuilder;
-import org.apache.hc.core5.net.URLEncodedUtils;
+import org.apache.hc.core5.net.WWWFormCodec;
import org.apache.hc.core5.util.Args;
/**
@@ -368,7 +368,7 @@ public class AsyncRequestBuilder {
AsyncEntityProducer entityProducerCopy = entityProducer;
if (parameters != null && !parameters.isEmpty()) {
if (entityProducerCopy == null && (Method.POST.isSame(method) || Method.PUT.isSame(method))) {
- final String content = URLEncodedUtils.format(
+ final String content = WWWFormCodec.format(
parameters,
charset != null ? charset : ContentType.APPLICATION_FORM_URLENCODED.getCharset());
entityProducerCopy = new StringAsyncEntityProducer(
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/net/PercentCodec.java b/httpcore5/src/main/java/org/apache/hc/core5/net/PercentCodec.java
new file mode 100644
index 0000000..2782282
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/net/PercentCodec.java
@@ -0,0 +1,163 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.net;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.BitSet;
+
+/**
+ * Percent-encoding mechanism defined in RFC 3986
+ *
+ * @since 5.1
+ */
+public class PercentCodec {
+
+ static final BitSet GEN_DELIMS = new BitSet(256);
+ static final BitSet SUB_DELIMS = new BitSet(256);
+ static final BitSet UNRESERVED = new BitSet(256);
+ static final BitSet URIC = new BitSet(256);
+
+ static {
+ GEN_DELIMS.set(':');
+ GEN_DELIMS.set('/');
+ GEN_DELIMS.set('?');
+ GEN_DELIMS.set('#');
+ GEN_DELIMS.set('[');
+ GEN_DELIMS.set(']');
+ GEN_DELIMS.set('@');
+
+ SUB_DELIMS.set('!');
+ SUB_DELIMS.set('$');
+ SUB_DELIMS.set('&');
+ SUB_DELIMS.set('\'');
+ SUB_DELIMS.set('(');
+ SUB_DELIMS.set(')');
+ SUB_DELIMS.set('*');
+ SUB_DELIMS.set('+');
+ SUB_DELIMS.set(',');
+ SUB_DELIMS.set(';');
+ SUB_DELIMS.set('=');
+
+ for (int i = 'a'; i <= 'z'; i++) {
+ UNRESERVED.set(i);
+ }
+ for (int i = 'A'; i <= 'Z'; i++) {
+ UNRESERVED.set(i);
+ }
+ // numeric characters
+ for (int i = '0'; i <= '9'; i++) {
+ UNRESERVED.set(i);
+ }
+ UNRESERVED.set('-');
+ UNRESERVED.set('.');
+ UNRESERVED.set('_');
+ UNRESERVED.set('~');
+ URIC.or(SUB_DELIMS);
+ URIC.or(UNRESERVED);
+ }
+
+ private static final int RADIX = 16;
+
+ static void encode(final StringBuilder buf, final CharSequence content, final Charset charset,
+ final BitSet safechars, final boolean blankAsPlus) {
+ if (content == null) {
+ return;
+ }
+ final CharBuffer cb = CharBuffer.wrap(content);
+ final ByteBuffer bb = (charset != null ? charset : StandardCharsets.UTF_8).encode(cb);
+ while (bb.hasRemaining()) {
+ final int b = bb.get() & 0xff;
+ if (safechars.get(b)) {
+ buf.append((char) b);
+ } else if (blankAsPlus && b == ' ') {
+ buf.append("+");
+ } else {
+ buf.append("%");
+ final char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, RADIX));
+ final char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, RADIX));
+ buf.append(hex1);
+ buf.append(hex2);
+ }
+ }
+ }
+
+ static void encode(final StringBuilder buf, final CharSequence content, final Charset charset, final boolean blankAsPlus) {
+ encode(buf, content, charset, UNRESERVED, blankAsPlus);
+ }
+
+ public static void encode(final StringBuilder buf, final CharSequence content, final Charset charset) {
+ encode(buf, content, charset, UNRESERVED, false);
+ }
+
+ public static String encode(final CharSequence content, final Charset charset) {
+ if (content == null) {
+ return null;
+ }
+ final StringBuilder buf = new StringBuilder();
+ encode(buf, content, charset, UNRESERVED, false);
+ return buf.toString();
+ }
+
+ static String decode(final CharSequence content, final Charset charset, final boolean plusAsBlank) {
+ if (content == null) {
+ return null;
+ }
+ final ByteBuffer bb = ByteBuffer.allocate(content.length());
+ final CharBuffer cb = CharBuffer.wrap(content);
+ while (cb.hasRemaining()) {
+ final char c = cb.get();
+ if (c == '%' && cb.remaining() >= 2) {
+ final char uc = cb.get();
+ final char lc = cb.get();
+ final int u = Character.digit(uc, RADIX);
+ final int l = Character.digit(lc, RADIX);
+ if (u != -1 && l != -1) {
+ bb.put((byte) ((u << 4) + l));
+ } else {
+ bb.put((byte) '%');
+ bb.put((byte) uc);
+ bb.put((byte) lc);
+ }
+ } else if (plusAsBlank && c == '+') {
+ bb.put((byte) ' ');
+ } else {
+ bb.put((byte) c);
+ }
+ }
+ bb.flip();
+ return (charset != null ? charset : StandardCharsets.UTF_8).decode(bb).toString();
+ }
+
+ public static String decode(final CharSequence content, final Charset charset) {
+ return decode(content, charset, false);
+ }
+
+}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/net/URIBuilder.java b/httpcore5/src/main/java/org/apache/hc/core5/net/URIBuilder.java
index 3fbe2d7..70358e2 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/net/URIBuilder.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/net/URIBuilder.java
@@ -34,6 +34,7 @@ import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -41,7 +42,9 @@ import java.util.List;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.apache.hc.core5.http.message.ParserCursor;
import org.apache.hc.core5.util.TextUtils;
+import org.apache.hc.core5.util.Tokenizer;
/**
* Builder for {@link URI} instances.
@@ -75,6 +78,7 @@ public class URIBuilder {
private String host;
private int port;
private String encodedPath;
+ private boolean pathRootless;
private List<String> pathSegments;
private String encodedQuery;
private List<NameValuePair> queryParams;
@@ -98,7 +102,7 @@ public class URIBuilder {
* @throws URISyntaxException if the input is not a valid URI
*/
public URIBuilder(final String string) throws URISyntaxException {
- this(new URI(string), null);
+ this(new URI(string), StandardCharsets.UTF_8);
}
/**
@@ -106,7 +110,7 @@ public class URIBuilder {
* @param uri
*/
public URIBuilder(final URI uri) {
- this(uri, null);
+ this(uri, StandardCharsets.UTF_8);
}
/**
@@ -125,8 +129,7 @@ public class URIBuilder {
*/
public URIBuilder(final URI uri, final Charset charset) {
super();
- setCharset(charset);
- digestURI(uri);
+ digestURI(uri, charset);
}
public URIBuilder setCharset(final Charset charset) {
@@ -138,18 +141,118 @@ public class URIBuilder {
return charset;
}
- private List <NameValuePair> parseQuery(final String query, final Charset charset) {
- if (query != null && !query.isEmpty()) {
- return URLEncodedUtils.parse(query, charset);
+ private static final char QUERY_PARAM_SEPARATOR = '&';
+ private static final char PARAM_VALUE_SEPARATOR = '=';
+ private static final char PATH_SEPARATOR = '/';
+
+ private static final BitSet QUERY_PARAM_SEPARATORS = new BitSet(256);
+ private static final BitSet QUERY_VALUE_SEPARATORS = new BitSet(256);
+ private static final BitSet PATH_SEPARATORS = new BitSet(256);
+
+ static {
+ QUERY_PARAM_SEPARATORS.set(QUERY_PARAM_SEPARATOR);
+ QUERY_PARAM_SEPARATORS.set(PARAM_VALUE_SEPARATOR);
+ QUERY_VALUE_SEPARATORS.set(QUERY_PARAM_SEPARATOR);
+ PATH_SEPARATORS.set(PATH_SEPARATOR);
+ }
+
+ static List<NameValuePair> parseQuery(final CharSequence s, final Charset charset, final boolean plusAsBlank) {
+ if (s == null) {
+ return null;
+ }
+ final Tokenizer tokenParser = Tokenizer.INSTANCE;
+ final ParserCursor cursor = new ParserCursor(0, s.length());
+ final List<NameValuePair> list = new ArrayList<>();
+ while (!cursor.atEnd()) {
+ final String name = tokenParser.parseToken(s, cursor, QUERY_PARAM_SEPARATORS);
+ String value = null;
+ if (!cursor.atEnd()) {
+ final int delim = s.charAt(cursor.getPos());
+ cursor.updatePos(cursor.getPos() + 1);
+ if (delim == PARAM_VALUE_SEPARATOR) {
+ value = tokenParser.parseToken(s, cursor, QUERY_VALUE_SEPARATORS);
+ if (!cursor.atEnd()) {
+ cursor.updatePos(cursor.getPos() + 1);
+ }
+ }
+ }
+ if (!name.isEmpty()) {
+ list.add(new BasicNameValuePair(
+ PercentCodec.decode(name, charset, plusAsBlank),
+ PercentCodec.decode(value, charset, plusAsBlank)));
+ }
}
- return null;
+ return list;
}
- private List <String> parsePath(final String path, final Charset charset) {
- if (path != null && !path.isEmpty()) {
- return URLEncodedUtils.parsePathSegments(path, charset);
+ static List<String> splitPath(final CharSequence s) {
+ if (s == null) {
+ return null;
+ }
+ final ParserCursor cursor = new ParserCursor(0, s.length());
+ // Skip leading separator
+ if (cursor.atEnd()) {
+ return new ArrayList<>(0);
+ }
+ if (PATH_SEPARATORS.get(s.charAt(cursor.getPos()))) {
+ cursor.updatePos(cursor.getPos() + 1);
+ }
+ final List<String> list = new ArrayList<>();
+ final StringBuilder buf = new StringBuilder();
+ for (;;) {
+ if (cursor.atEnd()) {
+ list.add(buf.toString());
+ break;
+ }
+ final char current = s.charAt(cursor.getPos());
+ if (PATH_SEPARATORS.get(current)) {
+ list.add(buf.toString());
+ buf.setLength(0);
+ } else {
+ buf.append(current);
+ }
+ cursor.updatePos(cursor.getPos() + 1);
+ }
+ return list;
+ }
+
+ static List<String> parsePath(final CharSequence s, final Charset charset) {
+ if (s == null) {
+ return null;
+ }
+ final List<String> segments = splitPath(s);
+ final List<String> list = new ArrayList<>(segments.size());
+ for (final String segment: segments) {
+ list.add(PercentCodec.decode(segment, charset));
+ }
+ return list;
+ }
+
+ static void formatPath(final StringBuilder buf, final Iterable<String> segments, final boolean rootless, final Charset charset) {
+ int i = 0;
+ for (final String segment : segments) {
+ if (i > 0 || !rootless) {
+ buf.append(PATH_SEPARATOR);
+ }
+ PercentCodec.encode(buf, segment, charset);
+ i++;
+ }
+ }
+
+ static void formatQuery(final StringBuilder buf, final Iterable<? extends NameValuePair> params, final Charset charset,
+ final boolean blankAsPlus) {
+ int i = 0;
+ for (final NameValuePair parameter : params) {
+ if (i > 0) {
+ buf.append(QUERY_PARAM_SEPARATOR);
+ }
+ PercentCodec.encode(buf, parameter.getName(), charset, blankAsPlus);
+ if (parameter.getValue() != null) {
+ buf.append(PARAM_VALUE_SEPARATOR);
+ PercentCodec.encode(buf, parameter.getValue(), charset, blankAsPlus);
+ }
+ i++;
}
- return null;
}
/**
@@ -167,14 +270,23 @@ public class URIBuilder {
if (this.encodedSchemeSpecificPart != null) {
sb.append(this.encodedSchemeSpecificPart);
} else {
+ final boolean authoritySpecified;
if (this.encodedAuthority != null) {
sb.append("//").append(this.encodedAuthority);
+ authoritySpecified = true;
} else if (this.host != null) {
sb.append("//");
if (this.encodedUserInfo != null) {
sb.append(this.encodedUserInfo).append("@");
} else if (this.userInfo != null) {
- encodeUserInfo(sb, this.userInfo);
+ final int idx = this.userInfo.indexOf(':');
+ if (idx != -1) {
+ PercentCodec.encode(sb, this.userInfo.substring(0, idx), this.charset);
+ sb.append(':');
+ PercentCodec.encode(sb, this.userInfo.substring(idx + 1), this.charset);
+ } else {
+ PercentCodec.encode(sb, this.userInfo, this.charset);
+ }
sb.append("@");
}
if (InetAddressUtils.isIPv6Address(this.host)) {
@@ -185,43 +297,38 @@ public class URIBuilder {
if (this.port >= 0) {
sb.append(":").append(this.port);
}
+ authoritySpecified = true;
+ } else {
+ authoritySpecified = false;
}
if (this.encodedPath != null) {
- sb.append(normalizePath(this.encodedPath, sb.length() == 0));
+ if (authoritySpecified && !TextUtils.isEmpty(this.encodedPath) && !this.encodedPath.startsWith("/")) {
+ sb.append('/');
+ }
+ sb.append(this.encodedPath);
} else if (this.pathSegments != null) {
- encodePath(sb, this.pathSegments);
+ formatPath(sb, this.pathSegments, !authoritySpecified && this.pathRootless, this.charset);
}
if (this.encodedQuery != null) {
sb.append("?").append(this.encodedQuery);
} else if (this.queryParams != null && !this.queryParams.isEmpty()) {
sb.append("?");
- encodeUrlForm(sb, this.queryParams);
+ formatQuery(sb, this.queryParams, this.charset, false);
} else if (this.query != null) {
sb.append("?");
- encodeUric(sb, this.query);
+ PercentCodec.encode(sb, this.query, this.charset, PercentCodec.URIC, false);
}
}
if (this.encodedFragment != null) {
sb.append("#").append(this.encodedFragment);
} else if (this.fragment != null) {
sb.append("#");
- encodeUric(sb, this.fragment);
+ PercentCodec.encode(sb, this.fragment, this.charset);
}
return sb.toString();
}
- private static String normalizePath(final String path, final boolean relative) {
- String s = path;
- if (TextUtils.isBlank(s)) {
- return "";
- }
- if (!relative && !s.startsWith("/")) {
- s = "/" + s;
- }
- return s;
- }
-
- private void digestURI(final URI uri) {
+ private void digestURI(final URI uri, final Charset charset) {
this.scheme = uri.getScheme();
this.encodedSchemeSpecificPart = uri.getRawSchemeSpecificPart();
this.encodedAuthority = uri.getRawAuthority();
@@ -230,27 +337,13 @@ public class URIBuilder {
this.encodedUserInfo = uri.getRawUserInfo();
this.userInfo = uri.getUserInfo();
this.encodedPath = uri.getRawPath();
- this.pathSegments = parsePath(uri.getRawPath(), this.charset != null ? this.charset : StandardCharsets.UTF_8);
+ this.pathSegments = parsePath(uri.getRawPath(), charset);
+ this.pathRootless = uri.getRawPath() != null && !uri.getRawPath().startsWith("/");
this.encodedQuery = uri.getRawQuery();
- this.queryParams = parseQuery(uri.getRawQuery(), this.charset != null ? this.charset : StandardCharsets.UTF_8);
+ this.queryParams = parseQuery(uri.getRawQuery(), charset, false);
this.encodedFragment = uri.getRawFragment();
this.fragment = uri.getFragment();
- }
-
- private void encodeUserInfo(final StringBuilder buf, final String userInfo) {
- URLEncodedUtils.encUserInfo(buf, userInfo, this.charset != null ? this.charset : StandardCharsets.UTF_8);
- }
-
- private void encodePath(final StringBuilder buf, final List<String> pathSegments) {
- URLEncodedUtils.formatSegments(buf, pathSegments, this.charset != null ? this.charset : StandardCharsets.UTF_8);
- }
-
- private void encodeUrlForm(final StringBuilder buf, final List<NameValuePair> params) {
- URLEncodedUtils.formatParameters(buf, params, this.charset != null ? this.charset : StandardCharsets.UTF_8);
- }
-
- private void encodeUric(final StringBuilder buf, final String fragment) {
- URLEncodedUtils.encUric(buf, fragment, this.charset != null ? this.charset : StandardCharsets.UTF_8);
+ this.charset = charset;
}
/**
@@ -301,7 +394,7 @@ public class URIBuilder {
final StringBuilder sb = new StringBuilder(schemeSpecificPart);
if (nvps != null && !nvps.isEmpty()) {
sb.append("?");
- encodeUrlForm(sb, nvps);
+ formatQuery(sb, nvps, this.charset, false);
}
this.encodedSchemeSpecificPart = sb.toString();
}
@@ -327,7 +420,11 @@ public class URIBuilder {
* be unescaped and may contain non ASCII characters.
*
* @return this.
+ *
+ * @deprecated The use of clear-text passwords in {@link URI}s has been deprecated and is strongly
+ * discouraged.
*/
+ @Deprecated
public URIBuilder setUserInfo(final String username, final String password) {
return setUserInfo(username + ':' + password);
}
@@ -387,7 +484,9 @@ public class URIBuilder {
* @return this.
*/
public URIBuilder setPath(final String path) {
- return setPathSegments(path != null ? URLEncodedUtils.splitPathSegments(path) : null);
+ setPathSegments(path != null ? splitPath(path) : null);
+ this.pathRootless = path != null && !path.startsWith("/");
+ return this;
}
/**
@@ -399,6 +498,23 @@ public class URIBuilder {
this.pathSegments = pathSegments.length > 0 ? Arrays.asList(pathSegments) : null;
this.encodedSchemeSpecificPart = null;
this.encodedPath = null;
+ this.pathRootless = false;
+ return this;
+ }
+
+ /**
+ * Sets rootless URI path (the first segment does not start with a /).
+ * The value is expected to be unescaped and may contain non ASCII characters.
+ *
+ * @return this.
+ *
+ * @since 5.1
+ */
+ public URIBuilder setPathSegmentsRootless(final String... pathSegments) {
+ this.pathSegments = pathSegments.length > 0 ? Arrays.asList(pathSegments) : null;
+ this.encodedSchemeSpecificPart = null;
+ this.encodedPath = null;
+ this.pathRootless = true;
return this;
}
@@ -411,6 +527,23 @@ public class URIBuilder {
this.pathSegments = pathSegments != null && pathSegments.size() > 0 ? new ArrayList<>(pathSegments) : null;
this.encodedSchemeSpecificPart = null;
this.encodedPath = null;
+ this.pathRootless = false;
+ return this;
+ }
+
+ /**
+ * Sets rootless URI path (the first segment does not start with a /).
+ * The value is expected to be unescaped and may contain non ASCII characters.
+ *
+ * @return this.
+ *
+ * @since 5.1
+ */
+ public URIBuilder setPathSegmentsRootless(final List<String> pathSegments) {
+ this.pathSegments = pathSegments != null && pathSegments.size() > 0 ? new ArrayList<>(pathSegments) : null;
+ this.encodedSchemeSpecificPart = null;
+ this.encodedPath = null;
+ this.pathRootless = true;
return this;
}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/net/URLEncodedUtils.java b/httpcore5/src/main/java/org/apache/hc/core5/net/URLEncodedUtils.java
index c2131ea..a3e3676 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/net/URLEncodedUtils.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/net/URLEncodedUtils.java
@@ -28,14 +28,11 @@
package org.apache.hc.core5.net;
import java.net.URI;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
-import java.util.Collections;
import java.util.List;
import org.apache.hc.core5.http.NameValuePair;
@@ -47,18 +44,15 @@ import org.apache.hc.core5.util.Tokenizer;
* A collection of utilities for encoding URLs.
*
* @since 4.0
+ *
+ * @deprecated Use {@link URIBuilder} to parse and format {@link URI}s and
+ * {@link WWWFormCodec} to parse and format {@code application/x-www-form-urlencoded} forms.
*/
+@Deprecated
public class URLEncodedUtils {
private static final char QP_SEP_A = '&';
private static final char QP_SEP_S = ';';
- private static final String NAME_VALUE_SEPARATOR = "=";
- private static final char PATH_SEPARATOR = '/';
-
- private static final BitSet PATH_SEPARATORS = new BitSet(256);
- static {
- PATH_SEPARATORS.set(PATH_SEPARATOR);
- }
/**
* Returns a list of {@link NameValuePair}s URI query parameters.
@@ -76,7 +70,7 @@ public class URLEncodedUtils {
if (query != null && !query.isEmpty()) {
return parse(query, charset);
}
- return createEmptyList();
+ return new ArrayList<>(0);
}
/**
@@ -91,7 +85,7 @@ public class URLEncodedUtils {
*/
public static List<NameValuePair> parse(final CharSequence s, final Charset charset) {
if (s == null) {
- return createEmptyList();
+ return new ArrayList<>(0);
}
return parse(s, charset, QP_SEP_A, QP_SEP_S);
}
@@ -133,45 +127,13 @@ public class URLEncodedUtils {
}
if (!name.isEmpty()) {
list.add(new BasicNameValuePair(
- decodeFormFields(name, charset),
- decodeFormFields(value, charset)));
- }
- }
- return list;
- }
-
- static List<String> splitSegments(final CharSequence s, final BitSet separators) {
- final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length());
- // Skip leading separator
- if (cursor.atEnd()) {
- return Collections.emptyList();
- }
- if (separators.get(s.charAt(cursor.getPos()))) {
- cursor.updatePos(cursor.getPos() + 1);
- }
- final List<String> list = new ArrayList<>();
- final StringBuilder buf = new StringBuilder();
- for (;;) {
- if (cursor.atEnd()) {
- list.add(buf.toString());
- break;
- }
- final char current = s.charAt(cursor.getPos());
- if (separators.get(current)) {
- list.add(buf.toString());
- buf.setLength(0);
- } else {
- buf.append(current);
+ PercentCodec.decode(name, charset, true),
+ PercentCodec.decode(value, charset, true)));
}
- cursor.updatePos(cursor.getPos() + 1);
}
return list;
}
- static List<String> splitPathSegments(final CharSequence s) {
- return splitSegments(s, PATH_SEPARATORS);
- }
-
/**
* Returns a list of URI path segments.
*
@@ -182,12 +144,7 @@ public class URLEncodedUtils {
* @since 4.5
*/
public static List<String> parsePathSegments(final CharSequence s, final Charset charset) {
- Args.notNull(s, "Char sequence");
- final List<String> list = splitPathSegments(s);
- for (int i = 0; i < list.size(); i++) {
- list.set(i, urlDecode(list.get(i), charset != null ? charset : StandardCharsets.UTF_8, false));
- }
- return list;
+ return URIBuilder.parsePath(s, charset);
}
/**
@@ -202,13 +159,6 @@ public class URLEncodedUtils {
return parsePathSegments(s, StandardCharsets.UTF_8);
}
- static void formatSegments(final StringBuilder buf, final Iterable<String> segments, final Charset charset) {
- for (final String segment : segments) {
- buf.append(PATH_SEPARATOR);
- urlEncode(buf, segment, charset, PATHSAFE);
- }
- }
-
/**
* Returns a string consisting of joint encoded path segments.
*
@@ -221,7 +171,7 @@ public class URLEncodedUtils {
public static String formatSegments(final Iterable<String> segments, final Charset charset) {
Args.notNull(segments, "Segments");
final StringBuilder buf = new StringBuilder();
- formatSegments(buf, segments, charset);
+ URIBuilder.formatPath(buf, segments, false, charset);
return buf.toString();
}
@@ -237,32 +187,6 @@ public class URLEncodedUtils {
return formatSegments(Arrays.asList(segments), StandardCharsets.UTF_8);
}
- static void formatNameValuePairs(
- final StringBuilder buf,
- final Iterable<? extends NameValuePair> parameters,
- final char parameterSeparator,
- final Charset charset) {
- int i = 0;
- for (final NameValuePair parameter : parameters) {
- if (i > 0) {
- buf.append(parameterSeparator);
- }
- encodeFormFields(buf, parameter.getName(), charset);
- if (parameter.getValue() != null) {
- buf.append(NAME_VALUE_SEPARATOR);
- encodeFormFields(buf, parameter.getValue(), charset);
- }
- i++;
- }
- }
-
- static void formatParameters(
- final StringBuilder buf,
- final Iterable<? extends NameValuePair> parameters,
- final Charset charset) {
- formatNameValuePairs(buf, parameters, QP_SEP_A, charset);
- }
-
/**
* Returns a String that is suitable for use as an {@code application/x-www-form-urlencoded}
* list of parameters in an HTTP PUT or HTTP POST.
@@ -280,7 +204,18 @@ public class URLEncodedUtils {
final Charset charset) {
Args.notNull(parameters, "Parameters");
final StringBuilder buf = new StringBuilder();
- formatNameValuePairs(buf, parameters, parameterSeparator, charset);
+ int i = 0;
+ for (final NameValuePair parameter : parameters) {
+ if (i > 0) {
+ buf.append(parameterSeparator);
+ }
+ PercentCodec.encode(buf, parameter.getName(), charset, URLENCODER, true);
+ if (parameter.getValue() != null) {
+ buf.append('=');
+ PercentCodec.encode(buf, parameter.getValue(), charset, URLENCODER, true);
+ }
+ i++;
+ }
return buf.toString();
}
@@ -300,205 +235,25 @@ public class URLEncodedUtils {
return format(parameters, QP_SEP_A, charset);
}
- /**
- * Unreserved characters, i.e. alphanumeric, plus: {@code _ - ! . ~ ' ( ) *}
- * <p>
- * This list is the same as the {@code unreserved} list in
- * <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>
- */
- private static final BitSet UNRESERVED = new BitSet(256);
- /**
- * Punctuation characters: , ; : $ & + =
- * <p>
- * These are the additional characters allowed by userinfo.
- */
- private static final BitSet PUNCT = new BitSet(256);
- /** Characters which are safe to use in userinfo,
- * i.e. {@link #UNRESERVED} plus {@link #PUNCT}uation */
- private static final BitSet USERINFO = new BitSet(256);
- /** Characters which are safe to use in a path,
- * i.e. {@link #UNRESERVED} plus {@link #PUNCT}uation plus / @ */
- private static final BitSet PATHSAFE = new BitSet(256);
- /** Characters which are safe to use in a query or a fragment,
- * i.e. {@link #RESERVED} plus {@link #UNRESERVED} */
- private static final BitSet URIC = new BitSet(256);
-
- /**
- * Reserved characters, i.e. {@code ;/?:@&=+$,[]}
- * <p>
- * This list is the same as the {@code reserved} list in
- * <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>
- * as augmented by
- * <a href="http://www.ietf.org/rfc/rfc2732.txt">RFC 2732</a>
- */
- private static final BitSet RESERVED = new BitSet(256);
-
-
- /**
- * Safe characters for x-www-form-urlencoded data, as per java.net.URLEncoder and browser behaviour,
- * i.e. alphanumeric plus {@code "-", "_", ".", "*"}
- */
private static final BitSet URLENCODER = new BitSet(256);
- private static final BitSet PATH_SPECIAL = new BitSet(256);
-
static {
// unreserved chars
// alpha characters
for (int i = 'a'; i <= 'z'; i++) {
- UNRESERVED.set(i);
+ URLENCODER.set(i);
}
for (int i = 'A'; i <= 'Z'; i++) {
- UNRESERVED.set(i);
+ URLENCODER.set(i);
}
// numeric characters
for (int i = '0'; i <= '9'; i++) {
- UNRESERVED.set(i);
- }
- UNRESERVED.set('_'); // these are the charactes of the "mark" list
- UNRESERVED.set('-');
- UNRESERVED.set('.');
- UNRESERVED.set('*');
- URLENCODER.or(UNRESERVED); // skip remaining unreserved characters
- UNRESERVED.set('!');
- UNRESERVED.set('~');
- UNRESERVED.set('\'');
- UNRESERVED.set('(');
- UNRESERVED.set(')');
- // punct chars
- PUNCT.set(',');
- PUNCT.set(';');
- PUNCT.set(':');
- PUNCT.set('$');
- PUNCT.set('&');
- PUNCT.set('+');
- PUNCT.set('=');
- // Safe for userinfo
- USERINFO.or(UNRESERVED);
- USERINFO.or(PUNCT);
-
- // URL path safe
- PATHSAFE.or(UNRESERVED);
- PATHSAFE.set(';'); // param separator
- PATHSAFE.set(':'); // RFC 2396
- PATHSAFE.set('@');
- PATHSAFE.set('&');
- PATHSAFE.set('=');
- PATHSAFE.set('+');
- PATHSAFE.set('$');
- PATHSAFE.set(',');
-
- PATH_SPECIAL.or(PATHSAFE);
- PATH_SPECIAL.set('/');
-
- RESERVED.set(';');
- RESERVED.set('/');
- RESERVED.set('?');
- RESERVED.set(':');
- RESERVED.set('@');
- RESERVED.set('&');
- RESERVED.set('=');
- RESERVED.set('+');
- RESERVED.set('$');
- RESERVED.set(',');
- RESERVED.set('['); // added by RFC 2732
- RESERVED.set(']'); // added by RFC 2732
-
- URIC.or(RESERVED);
- URIC.or(UNRESERVED);
- }
-
- private static final int RADIX = 16;
-
- private static List<NameValuePair> createEmptyList() {
- return new ArrayList<>(0);
- }
-
- private static void urlEncode(
- final StringBuilder buf,
- final String content,
- final Charset charset,
- final BitSet safechars) {
- if (content == null) {
- return;
- }
- final ByteBuffer bb = charset.encode(content);
- while (bb.hasRemaining()) {
- final int b = bb.get() & 0xff;
- if (safechars.get(b)) {
- buf.append((char) b);
- } else {
- buf.append("%");
- final char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, RADIX));
- final char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, RADIX));
- buf.append(hex1);
- buf.append(hex2);
- }
- }
- }
-
- private static String urlDecode(
- final String content,
- final Charset charset,
- final boolean plusAsBlank) {
- if (content == null) {
- return null;
- }
- final ByteBuffer bb = ByteBuffer.allocate(content.length());
- final CharBuffer cb = CharBuffer.wrap(content);
- while (cb.hasRemaining()) {
- final char c = cb.get();
- if (c == '%' && cb.remaining() >= 2) {
- final char uc = cb.get();
- final char lc = cb.get();
- final int u = Character.digit(uc, 16);
- final int l = Character.digit(lc, 16);
- if (u != -1 && l != -1) {
- bb.put((byte) ((u << 4) + l));
- } else {
- bb.put((byte) '%');
- bb.put((byte) uc);
- bb.put((byte) lc);
- }
- } else if (plusAsBlank && c == '+') {
- bb.put((byte) ' ');
- } else {
- bb.put((byte) c);
- }
- }
- bb.flip();
- return charset.decode(bb).toString();
- }
-
- static String decodeFormFields(final String content, final Charset charset) {
- if (content == null) {
- return null;
+ URLENCODER.set(i);
}
- return urlDecode(content, charset != null ? charset : StandardCharsets.UTF_8, true);
- }
-
- static void encodeFormFields(final StringBuilder buf, final String content, final Charset charset) {
- if (content == null) {
- return;
- }
- urlEncode(buf, content, charset != null ? charset : StandardCharsets.UTF_8, URLENCODER);
- }
-
- static String encodeFormFields(final String content, final Charset charset) {
- if (content == null) {
- return null;
- }
- final StringBuilder buf = new StringBuilder();
- urlEncode(buf, content, charset != null ? charset : StandardCharsets.UTF_8, URLENCODER);
- return buf.toString();
- }
-
- static void encUserInfo(final StringBuilder buf, final String content, final Charset charset) {
- urlEncode(buf, content, charset != null ? charset : StandardCharsets.UTF_8, USERINFO);
- }
-
- static void encUric(final StringBuilder buf, final String content, final Charset charset) {
- urlEncode(buf, content, charset != null ? charset : StandardCharsets.UTF_8, URIC);
+ URLENCODER.set('_'); // these are the characters of the "mark" list
+ URLENCODER.set('-');
+ URLENCODER.set('.');
+ URLENCODER.set('*');
}
}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/net/WWWFormCodec.java b/httpcore5/src/main/java/org/apache/hc/core5/net/WWWFormCodec.java
new file mode 100644
index 0000000..900918e
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/net/WWWFormCodec.java
@@ -0,0 +1,83 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.net;
+
+import java.nio.charset.Charset;
+import java.util.List;
+
+import org.apache.hc.core5.http.NameValuePair;
+
+/**
+ * {@code application/x-www-form-urlencoded} codec.
+ *
+ * @since 5.1
+ */
+public class WWWFormCodec {
+
+ private static final char QP_SEP_A = '&';
+
+ /**
+ * Returns a list of {@link NameValuePair} parameters parsed
+ * from the {@code application/x-www-form-urlencoded} content.
+ *
+ * @param s input text.
+ * @param charset parameter charset.
+ * @return list of form parameters.
+ */
+ public static List<NameValuePair> parse(final CharSequence s, final Charset charset) {
+ return URIBuilder.parseQuery(s, charset, true);
+ }
+
+ /**
+ * Formats the list of {@link NameValuePair} parameters into a {@code application/x-www-form-urlencoded}
+ * content.
+ *
+ * @param buf the content buffer
+ * @param params The from parameters.
+ * @param charset The encoding to use.
+ */
+ public static void format(
+ final StringBuilder buf, final Iterable<? extends NameValuePair> params, final Charset charset) {
+ URIBuilder.formatQuery(buf, params, charset, true);
+ }
+
+ /**
+ * Formats the list of {@link NameValuePair} parameters into a {@code application/x-www-form-urlencoded}
+ * content string.
+ *
+ * @param params The from parameters.
+ * @param charset The encoding to use.
+ * @return content string
+ */
+ public static String format(final Iterable<? extends NameValuePair> params, final Charset charset) {
+ final StringBuilder buf = new StringBuilder();
+ URIBuilder.formatQuery(buf, params, charset, true);
+ return buf.toString();
+ }
+
+}
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/NameValuePairListMatcher.java b/httpcore5/src/test/java/org/apache/hc/core5/http/NameValuePairListMatcher.java
new file mode 100644
index 0000000..7ecf449
--- /dev/null
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/NameValuePairListMatcher.java
@@ -0,0 +1,85 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.http;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.hc.core5.util.LangUtils;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+
+public class NameValuePairListMatcher extends BaseMatcher<List<NameValuePair>> {
+
+ private List<? extends NameValuePair> nvps;
+
+ public NameValuePairListMatcher(final List<? extends NameValuePair> nvps) {
+ this.nvps = nvps;
+ }
+
+ @Override
+ public boolean matches(final Object item) {
+ if (item instanceof List<?>) {
+ final List<?> objects = (List<?>) item;
+ if (objects.size() != nvps.size()) {
+ return false;
+ }
+ for (int i = 1; i < objects.size(); i++) {
+ final Object obj = objects.get(i);
+ if (obj instanceof NameValuePair) {
+ final NameValuePair nvp = (NameValuePair) obj;
+ final NameValuePair expected = nvps.get(i);
+ if (!LangUtils.equals(nvp.getName(), expected.getName())
+ || !LangUtils.equals(nvp.getValue(), expected.getValue())) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(final Description description) {
+ description.appendText("equals ").appendValueList("[", ";", "]", nvps);
+ }
+
+ @Factory
+ public static Matcher<List<NameValuePair>> equalsTo(final NameValuePair... nvps) {
+ return new NameValuePairListMatcher(Arrays.asList(nvps));
+ }
+
+ @Factory
+ public static Matcher<List<NameValuePair>> isEmpty() {
+ return new NameValuePairListMatcher(Collections.<NameValuePair>emptyList());
+ }
+
+}
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestEntityUtils.java b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestEntityUtils.java
index d50131c..f83ac7f 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestEntityUtils.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestEntityUtils.java
@@ -42,7 +42,7 @@ import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.message.BasicNameValuePair;
-import org.apache.hc.core5.net.URLEncodedUtils;
+import org.apache.hc.core5.net.WWWFormCodec;
import org.junit.Assert;
import org.junit.Test;
@@ -228,7 +228,7 @@ public class TestEntityUtils {
parameters.add(new BasicNameValuePair("russian", ru_hello));
parameters.add(new BasicNameValuePair("swiss", ch_hello));
- final String s = URLEncodedUtils.format(parameters, StandardCharsets.UTF_8);
+ final String s = WWWFormCodec.format(parameters, StandardCharsets.UTF_8);
Assert.assertEquals("russian=%D0%92%D1%81%D0%B5%D0%BC_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82" +
"&swiss=Gr%C3%BCezi_z%C3%A4m%C3%A4", s);
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/net/TestPercentCodec.java b/httpcore5/src/test/java/org/apache/hc/core5/net/TestPercentCodec.java
new file mode 100644
index 0000000..a19b617
--- /dev/null
+++ b/httpcore5/src/test/java/org/apache/hc/core5/net/TestPercentCodec.java
@@ -0,0 +1,70 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.net;
+
+import java.nio.charset.StandardCharsets;
+
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.MatcherAssert;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link PercentCodec}.
+ */
+public class TestPercentCodec {
+
+ @Test
+ public void testCoding() {
+ final StringBuilder buf = new StringBuilder();
+ PercentCodec.encode(buf, "blah!", StandardCharsets.UTF_8);
+ PercentCodec.encode(buf, " ~ ", StandardCharsets.UTF_8);
+ PercentCodec.encode(buf, "huh?", StandardCharsets.UTF_8);
+ MatcherAssert.assertThat(buf.toString(), CoreMatchers.equalTo("blah%21%20~%20huh%3F"));
+ }
+
+ @Test
+ public void testDecoding() {
+ MatcherAssert.assertThat(PercentCodec.decode("blah%21%20~%20huh%3F", StandardCharsets.UTF_8),
+ CoreMatchers.equalTo("blah! ~ huh?"));
+ MatcherAssert.assertThat(PercentCodec.decode("blah%21+~%20huh%3F", StandardCharsets.UTF_8),
+ CoreMatchers.equalTo("blah!+~ huh?"));
+ MatcherAssert.assertThat(PercentCodec.decode("blah%21+~%20huh%3F", StandardCharsets.UTF_8, true),
+ CoreMatchers.equalTo("blah! ~ huh?"));
+ }
+
+ @Test
+ public void testDecodingPartialContent() {
+ MatcherAssert.assertThat(PercentCodec.decode("blah%21%20%", StandardCharsets.UTF_8),
+ CoreMatchers.equalTo("blah! %"));
+ MatcherAssert.assertThat(PercentCodec.decode("blah%21%20%a", StandardCharsets.UTF_8),
+ CoreMatchers.equalTo("blah! %a"));
+ MatcherAssert.assertThat(PercentCodec.decode("blah%21%20%wa", StandardCharsets.UTF_8),
+ CoreMatchers.equalTo("blah! %wa"));
+ }
+
+}
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/net/TestURIBuilder.java b/httpcore5/src/test/java/org/apache/hc/core5/net/TestURIBuilder.java
index f86d345..ea153cf 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/net/TestURIBuilder.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/net/TestURIBuilder.java
@@ -32,17 +32,138 @@ import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.NameValuePairListMatcher;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.hamcrest.CoreMatchers;
+import org.hamcrest.MatcherAssert;
import org.junit.Assert;
import org.junit.Test;
public class TestURIBuilder {
+ private static final String CH_HELLO = "\u0047\u0072\u00FC\u0065\u007A\u0069\u005F\u007A\u00E4\u006D\u00E4";
+ private static final String RU_HELLO = "\u0412\u0441\u0435\u043C\u005F\u043F\u0440\u0438\u0432\u0435\u0442";
+
+ static List<String> parsePath(final CharSequence s) {
+ return URIBuilder.parsePath(s, null);
+ }
+
+ @Test
+ public void testParseSegments() throws Exception {
+ MatcherAssert.assertThat(parsePath("/this/that"), CoreMatchers.equalTo(Arrays.asList("this", "that")));
+ MatcherAssert.assertThat(parsePath("this/that"), CoreMatchers.equalTo(Arrays.asList("this", "that")));
+ MatcherAssert.assertThat(parsePath("this//that"), CoreMatchers.equalTo(Arrays.asList("this", "", "that")));
+ MatcherAssert.assertThat(parsePath("this//that/"), CoreMatchers.equalTo(Arrays.asList("this", "", "that", "")));
+ MatcherAssert.assertThat(parsePath("this//that/%2fthis%20and%20that"),
+ CoreMatchers.equalTo(Arrays.asList("this", "", "that", "/this and that")));
+ MatcherAssert.assertThat(parsePath("this///that//"),
+ CoreMatchers.equalTo(Arrays.asList("this", "", "", "that", "", "")));
+ MatcherAssert.assertThat(parsePath("/"), CoreMatchers.equalTo(Collections.singletonList("")));
+ MatcherAssert.assertThat(parsePath(""), CoreMatchers.equalTo(Collections.<String>emptyList()));
+ }
+
+ static String formatPath(final String... pathSegments) {
+ final StringBuilder buf = new StringBuilder();
+ URIBuilder.formatPath(buf, Arrays.asList(pathSegments), false, null);
+ return buf.toString();
+ }
+
+ @Test
+ public void testFormatSegments() throws Exception {
+ MatcherAssert.assertThat(formatPath("this", "that"), CoreMatchers.equalTo("/this/that"));
+ MatcherAssert.assertThat(formatPath("this", "", "that"), CoreMatchers.equalTo("/this//that"));
+ MatcherAssert.assertThat(formatPath("this", "", "that", "/this and that"),
+ CoreMatchers.equalTo("/this//that/%2Fthis%20and%20that"));
+ MatcherAssert.assertThat(formatPath("this", "", "", "that", "", ""),
+ CoreMatchers.equalTo("/this///that//"));
+ MatcherAssert.assertThat(formatPath(""), CoreMatchers.equalTo("/"));
+ MatcherAssert.assertThat(formatPath(), CoreMatchers.equalTo(""));
+ }
+
+ static List<NameValuePair> parseQuery(final CharSequence s) {
+ return URIBuilder.parseQuery(s, null, false);
+ }
+
+ @Test
+ public void testParseQuery() throws Exception {
+ MatcherAssert.assertThat(parseQuery(""), NameValuePairListMatcher.isEmpty());
+ MatcherAssert.assertThat(parseQuery("Name0"),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name0", null)));
+ MatcherAssert.assertThat(parseQuery("Name1=Value1"),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name1", "Value1")));
+ MatcherAssert.assertThat(parseQuery("Name2="),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name2", "")));
+ MatcherAssert.assertThat(parseQuery(" Name3 "),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name3", null)));
+ MatcherAssert.assertThat(parseQuery("Name4=Value%204%21"),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name4", "Value 4!")));
+ MatcherAssert.assertThat(parseQuery("Name4=Value%2B4%21"),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name4", "Value+4!")));
+ MatcherAssert.assertThat(parseQuery("Name4=Value%204%21%20%214"),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name4", "Value 4! !4")));
+ MatcherAssert.assertThat(parseQuery("Name5=aaa&Name6=bbb"),
+ NameValuePairListMatcher.equalsTo(
+ new BasicNameValuePair("Name5", "aaa"),
+ new BasicNameValuePair("Name6", "bbb")));
+ MatcherAssert.assertThat(parseQuery("Name7=aaa&Name7=b%2Cb&Name7=ccc"),
+ NameValuePairListMatcher.equalsTo(
+ new BasicNameValuePair("Name7", "aaa"),
+ new BasicNameValuePair("Name7", "b,b"),
+ new BasicNameValuePair("Name7", "ccc")));
+ MatcherAssert.assertThat(parseQuery("Name8=xx%2C%20%20yy%20%20%2Czz"),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name8", "xx, yy ,zz")));
+ MatcherAssert.assertThat(parseQuery("price=10%20%E2%82%AC"),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("price", "10 \u20AC")));
+ MatcherAssert.assertThat(parseQuery("a=b\"c&d=e"),
+ NameValuePairListMatcher.equalsTo(
+ new BasicNameValuePair("a", "b\"c"),
+ new BasicNameValuePair("d", "e")));
+ MatcherAssert.assertThat(parseQuery("russian=" + PercentCodec.encode(RU_HELLO, StandardCharsets.UTF_8) +
+ "&swiss=" + PercentCodec.encode(CH_HELLO, StandardCharsets.UTF_8)),
+ NameValuePairListMatcher.equalsTo(
+ new BasicNameValuePair("russian", RU_HELLO),
+ new BasicNameValuePair("swiss", CH_HELLO)));
+ }
+
+ static String formatQuery(final NameValuePair... params) {
+ final StringBuilder buf = new StringBuilder();
+ URIBuilder.formatQuery(buf, Arrays.asList(params), null, false);
+ return buf.toString();
+ }
+
+ @Test
+ public void testFormatQuery() throws Exception {
+ MatcherAssert.assertThat(formatQuery(new BasicNameValuePair("Name0", null)), CoreMatchers.equalTo("Name0"));
+ MatcherAssert.assertThat(formatQuery(new BasicNameValuePair("Name1", "Value1")), CoreMatchers.equalTo("Name1=Value1"));
+ MatcherAssert.assertThat(formatQuery(new BasicNameValuePair("Name2", "")), CoreMatchers.equalTo("Name2="));
+ MatcherAssert.assertThat(formatQuery(new BasicNameValuePair("Name4", "Value 4&")),
+ CoreMatchers.equalTo("Name4=Value%204%26"));
+ MatcherAssert.assertThat(formatQuery(new BasicNameValuePair("Name4", "Value+4&")),
+ CoreMatchers.equalTo("Name4=Value%2B4%26"));
+ MatcherAssert.assertThat(formatQuery(new BasicNameValuePair("Name4", "Value 4& =4")),
+ CoreMatchers.equalTo("Name4=Value%204%26%20%3D4"));
+ MatcherAssert.assertThat(formatQuery(
+ new BasicNameValuePair("Name5", "aaa"),
+ new BasicNameValuePair("Name6", "bbb")), CoreMatchers.equalTo("Name5=aaa&Name6=bbb"));
+ MatcherAssert.assertThat(formatQuery(
+ new BasicNameValuePair("Name7", "aaa"),
+ new BasicNameValuePair("Name7", "b,b"),
+ new BasicNameValuePair("Name7", "ccc")
+ ), CoreMatchers.equalTo("Name7=aaa&Name7=b%2Cb&Name7=ccc"));
+ MatcherAssert.assertThat(formatQuery(new BasicNameValuePair("Name8", "xx, yy ,zz")),
+ CoreMatchers.equalTo("Name8=xx%2C%20%20yy%20%20%2Czz"));
+ MatcherAssert.assertThat(formatQuery(
+ new BasicNameValuePair("russian", RU_HELLO),
+ new BasicNameValuePair("swiss", CH_HELLO)),
+ CoreMatchers.equalTo("russian=" + PercentCodec.encode(RU_HELLO, StandardCharsets.UTF_8) +
+ "&swiss=" + PercentCodec.encode(CH_HELLO, StandardCharsets.UTF_8)));
+ }
+
@Test
public void testHierarchicalUri() throws Exception {
final URI uri = new URI("http", "stuff", "localhost", 80, "/some stuff", "param=stuff", "fragment");
@@ -169,14 +290,6 @@ public class TestURIBuilder {
}
@Test
- public void testSetUserInfo() throws Exception {
- final URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff", null);
- final URIBuilder uribuilder = new URIBuilder(uri).setUserInfo("user", "password");
- final URI result = uribuilder.build();
- Assert.assertEquals(new URI("http://user:password@localhost:80/?param=stuff"), result);
- }
-
- @Test
public void testRemoveParameters() throws Exception {
final URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff", null);
final URIBuilder uribuilder = new URIBuilder(uri).removeQuery();
@@ -290,35 +403,6 @@ public class TestURIBuilder {
}
@Test
- public void testAgainstURIEncoded() throws Exception {
- // Check that the encoded URI generated by URI builder agrees with that generated by using URI directly
- final String scheme="https";
- final String host="localhost";
- final String specials="/ abcd!$&*()_-+.,=:;'~<>/@[]|#^%\"{}\\`xyz"; // N.B. excludes \u00a3\u00ac\u00a6
- final URI uri = new URI(scheme, specials, host, 80, specials, specials, specials);
-
- final URI bld = new URIBuilder()
- .setScheme(scheme)
- .setHost(host)
- .setUserInfo(specials)
- .setPath(specials)
- .setCustomQuery(specials)
- .setFragment(specials)
- .build();
-
- Assert.assertEquals(uri.getHost(), bld.getHost());
-
- Assert.assertEquals(uri.getRawUserInfo(), bld.getRawUserInfo());
-
- Assert.assertEquals(uri.getRawPath(), bld.getRawPath());
-
- Assert.assertEquals(uri.getRawQuery(), bld.getRawQuery());
-
- Assert.assertEquals(uri.getRawFragment(), bld.getRawFragment());
-
- }
-
- @Test
public void testBuildAddParametersUTF8() throws Exception {
assertAddParameters(StandardCharsets.UTF_8);
}
@@ -355,8 +439,8 @@ public class TestURIBuilder {
}
public void assertBuild(final Charset charset, final URI uri) throws Exception {
- final String encodedData1 = URLEncodedUtils.encodeFormFields("\"1\u00aa position\"", charset);
- final String encodedData2 = URLEncodedUtils.encodeFormFields("Jos\u00e9 Abra\u00e3o", charset);
+ final String encodedData1 = PercentCodec.encode("\"1\u00aa position\"", charset);
+ final String encodedData2 = PercentCodec.encode("Jos\u00e9 Abra\u00e3o", charset);
final String uriExpected = String.format("https://somehost.com/stuff?parameter1=value1¶meter2=%s¶meter3=%s", encodedData1, encodedData2);
@@ -392,7 +476,7 @@ public class TestURIBuilder {
@Test
public void testTolerateNullInput() throws Exception {
- Assert.assertThat(new URIBuilder()
+ MatcherAssert.assertThat(new URIBuilder()
.setScheme(null)
.setHost("localhost")
.setUserInfo(null)
@@ -406,7 +490,7 @@ public class TestURIBuilder {
@Test
public void testTolerateBlankInput() throws Exception {
- Assert.assertThat(new URIBuilder()
+ MatcherAssert.assertThat(new URIBuilder()
.setScheme("")
.setHost("localhost")
.setUserInfo("")
@@ -424,8 +508,7 @@ public class TestURIBuilder {
final HttpHost httpHost = new HttpHost("http", "example.com", 1234);
final URIBuilder uribuilder = new URIBuilder();
uribuilder.setHttpHost(httpHost);
- final URI result = uribuilder.build();
- Assert.assertEquals(URI.create(httpHost.toURI()), result);
+ Assert.assertEquals(URI.create("http://example.com:1234"), uribuilder.build());
}
@Test
@@ -435,23 +518,50 @@ public class TestURIBuilder {
.setHost("somehost")
.setPath("//blah//blah")
.build();
- Assert.assertThat(uri, CoreMatchers.equalTo(URI.create("ftp://somehost//blah//blah")));
+ MatcherAssert.assertThat(uri, CoreMatchers.equalTo(URI.create("ftp://somehost//blah//blah")));
}
@Test
- public void testPathNoLeadingSlash() throws Exception {
+ public void testNoAuthorityAndPath() throws Exception {
final URI uri = new URIBuilder()
- .setScheme("ftp")
+ .setScheme("file")
+ .setPath("/blah")
+ .build();
+ MatcherAssert.assertThat(uri, CoreMatchers.equalTo(URI.create("file:/blah")));
+ }
+
+ @Test
+ public void testNoAuthorityAndPathSegments() throws Exception {
+ final URI uri = new URIBuilder()
+ .setScheme("file")
+ .setPathSegments("this", "that")
+ .build();
+ MatcherAssert.assertThat(uri, CoreMatchers.equalTo(URI.create("file:/this/that")));
+ }
+
+ @Test
+ public void testNoAuthorityAndRootlessPath() throws Exception {
+ final URI uri = new URIBuilder()
+ .setScheme("file")
.setPath("blah")
.build();
- Assert.assertThat(uri, CoreMatchers.equalTo(URI.create("ftp:/blah")));
+ MatcherAssert.assertThat(uri, CoreMatchers.equalTo(URI.create("file:blah")));
+ }
+
+ @Test
+ public void testNoAuthorityAndRootlessPathSegments() throws Exception {
+ final URI uri = new URIBuilder()
+ .setScheme("file")
+ .setPathSegmentsRootless("this", "that")
+ .build();
+ MatcherAssert.assertThat(uri, CoreMatchers.equalTo(URI.create("file:this/that")));
}
@Test
public void testOpaque() throws Exception {
final URIBuilder uriBuilder = new URIBuilder("http://host.com");
final URI uri = uriBuilder.build();
- Assert.assertThat(uriBuilder.isOpaque(), CoreMatchers.equalTo(uri.isOpaque()));
+ MatcherAssert.assertThat(uriBuilder.isOpaque(), CoreMatchers.equalTo(uri.isOpaque()));
}
@Test
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/net/TestURLEncodedUtils.java b/httpcore5/src/test/java/org/apache/hc/core5/net/TestURLEncodedUtils.java
deleted file mode 100644
index e7687a4..0000000
--- a/httpcore5/src/test/java/org/apache/hc/core5/net/TestURLEncodedUtils.java
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.hc.core5.net;
-
-import java.net.URI;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.hc.core5.http.NameValuePair;
-import org.apache.hc.core5.http.message.BasicNameValuePair;
-import org.hamcrest.CoreMatchers;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class TestURLEncodedUtils {
-
- @Test
- public void testParseURLCodedContent() throws Exception {
- List <NameValuePair> result;
-
- result = parse("");
- Assert.assertTrue(result.isEmpty());
-
- result = parse("Name0");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name0", null);
-
- result = parse("Name1=Value1");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name1", "Value1");
-
- result = parse("Name2=");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name2", "");
-
- result = parse("Name3");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name3", null);
-
- result = parse("Name4=Value%204%21");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name4", "Value 4!");
-
- result = parse("Name4=Value%2B4%21");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name4", "Value+4!");
-
- result = parse("Name4=Value%204%21%20%214");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name4", "Value 4! !4");
-
- result = parse("Name5=aaa&Name6=bbb");
- Assert.assertEquals(2, result.size());
- assertNameValuePair(result.get(0), "Name5", "aaa");
- assertNameValuePair(result.get(1), "Name6", "bbb");
-
- result = parse("Name7=aaa&Name7=b%2Cb&Name7=ccc");
- Assert.assertEquals(3, result.size());
- assertNameValuePair(result.get(0), "Name7", "aaa");
- assertNameValuePair(result.get(1), "Name7", "b,b");
- assertNameValuePair(result.get(2), "Name7", "ccc");
-
- result = parse("Name8=xx%2C%20%20yy%20%20%2Czz");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name8", "xx, yy ,zz");
-
- result = parse("price=10%20%E2%82%AC");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "price", "10 \u20AC");
- }
-
- @Test
- public void testParseSegments() throws Exception {
- Assert.assertThat(URLEncodedUtils.parsePathSegments("/this/that"),
- CoreMatchers.equalTo(Arrays.asList("this", "that")));
- Assert.assertThat(URLEncodedUtils.parsePathSegments("this/that"),
- CoreMatchers.equalTo(Arrays.asList("this", "that")));
- Assert.assertThat(URLEncodedUtils.parsePathSegments("this//that"),
- CoreMatchers.equalTo(Arrays.asList("this", "", "that")));
- Assert.assertThat(URLEncodedUtils.parsePathSegments("this//that/"),
- CoreMatchers.equalTo(Arrays.asList("this", "", "that", "")));
- Assert.assertThat(URLEncodedUtils.parsePathSegments("this//that/%2fthis%20and%20that"),
- CoreMatchers.equalTo(Arrays.asList("this", "", "that", "/this and that")));
- Assert.assertThat(URLEncodedUtils.parsePathSegments("this///that//"),
- CoreMatchers.equalTo(Arrays.asList("this", "", "", "that", "", "")));
- Assert.assertThat(URLEncodedUtils.parsePathSegments("/"),
- CoreMatchers.equalTo(Collections.singletonList("")));
- Assert.assertThat(URLEncodedUtils.parsePathSegments(""),
- CoreMatchers.equalTo(Collections.<String>emptyList()));
- }
-
- @Test
- public void testFormatSegments() throws Exception {
- Assert.assertThat(URLEncodedUtils.formatSegments("this", "that"),
- CoreMatchers.equalTo("/this/that"));
- Assert.assertThat(URLEncodedUtils.formatSegments("this", "", "that"),
- CoreMatchers.equalTo("/this//that"));
- Assert.assertThat(URLEncodedUtils.formatSegments("this", "", "that", "/this and that"),
- CoreMatchers.equalTo("/this//that/%2Fthis%20and%20that"));
- Assert.assertThat(URLEncodedUtils.formatSegments("this", "", "", "that", "", ""),
- CoreMatchers.equalTo("/this///that//"));
- Assert.assertThat(URLEncodedUtils.formatSegments(""),
- CoreMatchers.equalTo("/"));
- Assert.assertThat(URLEncodedUtils.formatSegments(),
- CoreMatchers.equalTo(""));
- }
-
- @Test
- public void testParseURLCodedContentString() throws Exception {
- List <NameValuePair> result;
-
- result = parseString("");
- Assert.assertTrue(result.isEmpty());
-
- result = parseString("Name0");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name0", null);
-
- result = parseString("Name1=Value1");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name1", "Value1");
-
- result = parseString("Name2=");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name2", "");
-
- result = parseString("Name3");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name3", null);
-
- result = parseString("Name4=Value%204%21");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name4", "Value 4!");
-
- result = parseString("Name4=Value%2B4%21");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name4", "Value+4!");
-
- result = parseString("Name4=Value%204%21%20%214");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name4", "Value 4! !4");
-
- result = parseString("Name5=aaa&Name6=bbb");
- Assert.assertEquals(2, result.size());
- assertNameValuePair(result.get(0), "Name5", "aaa");
- assertNameValuePair(result.get(1), "Name6", "bbb");
-
- result = parseString("Name7=aaa&Name7=b%2Cb&Name7=ccc");
- Assert.assertEquals(3, result.size());
- assertNameValuePair(result.get(0), "Name7", "aaa");
- assertNameValuePair(result.get(1), "Name7", "b,b");
- assertNameValuePair(result.get(2), "Name7", "ccc");
-
- result = parseString("Name8=xx%2C%20%20yy%20%20%2Czz");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "Name8", "xx, yy ,zz");
-
- result = parseString("price=10%20%E2%82%AC");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "price", "10 \u20AC");
-
- result = parse("a=b\"c&d=e");
- Assert.assertEquals(2, result.size());
- assertNameValuePair(result.get(0), "a", "b\"c");
- assertNameValuePair(result.get(1), "d", "e");
- }
-
- @Test
- public void testParseInvalidURLCodedContent() throws Exception {
- List <NameValuePair> result;
-
- result = parse("name=%");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "name", "%");
-
- result = parse("name=%a");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "name", "%a");
-
- result = parse("name=%wa%20");
- Assert.assertEquals(1, result.size());
- assertNameValuePair(result.get(0), "name", "%wa ");
- }
-
- private static final int SWISS_GERMAN_HELLO [] = {
- 0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4
- };
-
- private static final int RUSSIAN_HELLO [] = {
- 0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438,
- 0x432, 0x435, 0x442
- };
-
- private static String constructString(final int [] unicodeChars) {
- final StringBuilder buffer = new StringBuilder();
- if (unicodeChars != null) {
- for (final int unicodeChar : unicodeChars) {
- buffer.append((char)unicodeChar);
- }
- }
- return buffer.toString();
- }
-
- @Test
- public void testParseUTF8Ampersand1String() throws Exception {
- final String ru_hello = constructString(RUSSIAN_HELLO);
- final String ch_hello = constructString(SWISS_GERMAN_HELLO);
- final List <NameValuePair> parameters = new ArrayList<>();
- parameters.add(new BasicNameValuePair("russian", ru_hello));
- parameters.add(new BasicNameValuePair("swiss", ch_hello));
-
- final String s = URLEncodedUtils.format(parameters, StandardCharsets.UTF_8);
-
- final List <NameValuePair> result = URLEncodedUtils.parse(s, StandardCharsets.UTF_8);
- Assert.assertEquals(2, result.size());
- assertNameValuePair(result.get(0), "russian", ru_hello);
- assertNameValuePair(result.get(1), "swiss", ch_hello);
- }
-
- @Test
- public void testParseUTF8Ampersand2String() throws Exception {
- testParseUTF8String('&');
- }
-
- @Test
- public void testParseUTF8SemicolonString() throws Exception {
- testParseUTF8String(';');
- }
-
- private void testParseUTF8String(final char parameterSeparator) throws Exception {
- final String ru_hello = constructString(RUSSIAN_HELLO);
- final String ch_hello = constructString(SWISS_GERMAN_HELLO);
- final List <NameValuePair> parameters = new ArrayList<>();
- parameters.add(new BasicNameValuePair("russian", ru_hello));
- parameters.add(new BasicNameValuePair("swiss", ch_hello));
-
- final String s = URLEncodedUtils.format(parameters, parameterSeparator, StandardCharsets.UTF_8);
-
- final List<NameValuePair> result1 = URLEncodedUtils.parse(s, StandardCharsets.UTF_8);
- Assert.assertEquals(2, result1.size());
- assertNameValuePair(result1.get(0), "russian", ru_hello);
- assertNameValuePair(result1.get(1), "swiss", ch_hello);
-
- final List<NameValuePair> result2 = URLEncodedUtils.parse(s, StandardCharsets.UTF_8, parameterSeparator);
- Assert.assertEquals(2, result2.size());
- assertNameValuePair(result2.get(0), "russian", ru_hello);
- assertNameValuePair(result2.get(1), "swiss", ch_hello);
- }
-
- @Test
- public void testEmptyQuery() throws Exception {
- final List<NameValuePair> result = URLEncodedUtils.parse("", StandardCharsets.UTF_8);
- Assert.assertEquals(0, result.size());
- // [HTTPCLIENT-1889]:
- result.add(new BasicNameValuePair("key", "value"));
- }
-
- @Test
- public void testFormat() throws Exception {
- final List <NameValuePair> params = new ArrayList<>();
- Assert.assertEquals(0, URLEncodedUtils.format(params, StandardCharsets.US_ASCII).length());
-
- params.clear();
- params.add(new BasicNameValuePair("Name0", null));
- Assert.assertEquals("Name0", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name1", "Value1"));
- Assert.assertEquals("Name1=Value1", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name2", ""));
- Assert.assertEquals("Name2=", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name4", "Value 4&"));
- Assert.assertEquals("Name4=Value%204%26", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name4", "Value+4&"));
- Assert.assertEquals("Name4=Value%2B4%26", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name4", "Value 4& =4"));
- Assert.assertEquals("Name4=Value%204%26%20%3D4", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name5", "aaa"));
- params.add(new BasicNameValuePair("Name6", "bbb"));
- Assert.assertEquals("Name5=aaa&Name6=bbb", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name7", "aaa"));
- params.add(new BasicNameValuePair("Name7", "b,b"));
- params.add(new BasicNameValuePair("Name7", "ccc"));
- Assert.assertEquals("Name7=aaa&Name7=b%2Cb&Name7=ccc", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
- Assert.assertEquals("Name7=aaa&Name7=b%2Cb&Name7=ccc", URLEncodedUtils.format(params, '&', StandardCharsets.US_ASCII));
- Assert.assertEquals("Name7=aaa;Name7=b%2Cb;Name7=ccc", URLEncodedUtils.format(params, ';', StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name8", "xx, yy ,zz"));
- Assert.assertEquals("Name8=xx%2C%20%20yy%20%20%2Czz", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
- }
-
- @Test
- public void testFormatString() throws Exception { // as above, using String
- final List <NameValuePair> params = new ArrayList<>();
- Assert.assertEquals(0, URLEncodedUtils.format(params, StandardCharsets.US_ASCII).length());
-
- params.clear();
- params.add(new BasicNameValuePair("Name0", null));
- Assert.assertEquals("Name0", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name1", "Value1"));
- Assert.assertEquals("Name1=Value1", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name2", ""));
- Assert.assertEquals("Name2=", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name4", "Value 4&"));
- Assert.assertEquals("Name4=Value%204%26", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name4", "Value+4&"));
- Assert.assertEquals("Name4=Value%2B4%26", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name4", "Value 4& =4"));
- Assert.assertEquals("Name4=Value%204%26%20%3D4", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name5", "aaa"));
- params.add(new BasicNameValuePair("Name6", "bbb"));
- Assert.assertEquals("Name5=aaa&Name6=bbb", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name7", "aaa"));
- params.add(new BasicNameValuePair("Name7", "b,b"));
- params.add(new BasicNameValuePair("Name7", "ccc"));
- Assert.assertEquals("Name7=aaa&Name7=b%2Cb&Name7=ccc", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
-
- params.clear();
- params.add(new BasicNameValuePair("Name8", "xx, yy ,zz"));
- Assert.assertEquals("Name8=xx%2C%20%20yy%20%20%2Czz", URLEncodedUtils.format(params, StandardCharsets.US_ASCII));
- }
-
- private List <NameValuePair> parse (final String params) {
- return URLEncodedUtils.parse(params, StandardCharsets.UTF_8);
- }
-
- private List <NameValuePair> parseString (final String uri) throws Exception {
- return URLEncodedUtils.parse(new URI("?"+uri), StandardCharsets.UTF_8);
- }
-
- private static void assertNameValuePair (
- final NameValuePair parameter,
- final String expectedName,
- final String expectedValue) {
- Assert.assertEquals(parameter.getName(), expectedName);
- Assert.assertEquals(parameter.getValue(), expectedValue);
- }
-
-}
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/net/TestWWWFormCodec.java b/httpcore5/src/test/java/org/apache/hc/core5/net/TestWWWFormCodec.java
new file mode 100644
index 0000000..88d4362
--- /dev/null
+++ b/httpcore5/src/test/java/org/apache/hc/core5/net/TestWWWFormCodec.java
@@ -0,0 +1,123 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.net;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.NameValuePairListMatcher;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.MatcherAssert;
+import org.junit.Test;
+
+public class TestWWWFormCodec {
+
+ private static final String CH_HELLO = "\u0047\u0072\u00FC\u0065\u007A\u0069\u005F\u007A\u00E4\u006D\u00E4";
+ private static final String RU_HELLO = "\u0412\u0441\u0435\u043C\u005F\u043F\u0440\u0438\u0432\u0435\u0442";
+
+ private static List<NameValuePair> parse(final String params) {
+ return WWWFormCodec.parse(params, StandardCharsets.UTF_8);
+ }
+
+ @Test
+ public void testParse() throws Exception {
+ MatcherAssert.assertThat(parse(""), NameValuePairListMatcher.isEmpty());
+ MatcherAssert.assertThat(parse("Name0"),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name0", null)));
+ MatcherAssert.assertThat(parse("Name1=Value1"),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name1", "Value1")));
+ MatcherAssert.assertThat(parse("Name2="),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name2", "")));
+ MatcherAssert.assertThat(parse(" Name3 "),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name3", null)));
+ MatcherAssert.assertThat(parse("Name4=Value%204%21"),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name4", "Value 4!")));
+ MatcherAssert.assertThat(parse("Name4=Value%2B4%21"),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name4", "Value+4!")));
+ MatcherAssert.assertThat(parse("Name4=Value%204%21%20%214"),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name4", "Value 4! !4")));
+ MatcherAssert.assertThat(parse("Name5=aaa&Name6=bbb"),
+ NameValuePairListMatcher.equalsTo(
+ new BasicNameValuePair("Name5", "aaa"),
+ new BasicNameValuePair("Name6", "bbb")));
+ MatcherAssert.assertThat(parse("Name7=aaa&Name7=b%2Cb&Name7=ccc"),
+ NameValuePairListMatcher.equalsTo(
+ new BasicNameValuePair("Name7", "aaa"),
+ new BasicNameValuePair("Name7", "b,b"),
+ new BasicNameValuePair("Name7", "ccc")));
+ MatcherAssert.assertThat(parse("Name8=xx%2C%20%20yy%20%20%2Czz"),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("Name8", "xx, yy ,zz")));
+ MatcherAssert.assertThat(parse("price=10%20%E2%82%AC"),
+ NameValuePairListMatcher.equalsTo(new BasicNameValuePair("price", "10 \u20AC")));
+ MatcherAssert.assertThat(parse("a=b\"c&d=e"),
+ NameValuePairListMatcher.equalsTo(
+ new BasicNameValuePair("a", "b\"c"),
+ new BasicNameValuePair("d", "e")));
+ MatcherAssert.assertThat(parse("russian=" + PercentCodec.encode(RU_HELLO, StandardCharsets.UTF_8) +
+ "&swiss=" + PercentCodec.encode(CH_HELLO, StandardCharsets.UTF_8)),
+ NameValuePairListMatcher.equalsTo(
+ new BasicNameValuePair("russian", RU_HELLO),
+ new BasicNameValuePair("swiss", CH_HELLO)));
+ }
+
+ private static String format(final NameValuePair... nvps) {
+ return WWWFormCodec.format(Arrays.asList(nvps), StandardCharsets.UTF_8);
+ }
+
+ @Test
+ public void testFormat() throws Exception {
+ MatcherAssert.assertThat(format(new BasicNameValuePair("Name0", null)), CoreMatchers.equalTo("Name0"));
+ MatcherAssert.assertThat(format(new BasicNameValuePair("Name1", "Value1")), CoreMatchers.equalTo("Name1=Value1"));
+ MatcherAssert.assertThat(format(new BasicNameValuePair("Name2", "")), CoreMatchers.equalTo("Name2="));
+ MatcherAssert.assertThat(format(new BasicNameValuePair("Name4", "Value 4&")),
+ CoreMatchers.equalTo("Name4=Value+4%26"));
+ MatcherAssert.assertThat(format(new BasicNameValuePair("Name4", "Value+4&")),
+ CoreMatchers.equalTo("Name4=Value%2B4%26"));
+ MatcherAssert.assertThat(format(new BasicNameValuePair("Name4", "Value 4& =4")),
+ CoreMatchers.equalTo("Name4=Value+4%26+%3D4"));
+ MatcherAssert.assertThat(format(
+ new BasicNameValuePair("Name5", "aaa"),
+ new BasicNameValuePair("Name6", "bbb")), CoreMatchers.equalTo("Name5=aaa&Name6=bbb"));
+ MatcherAssert.assertThat(format(
+ new BasicNameValuePair("Name7", "aaa"),
+ new BasicNameValuePair("Name7", "b,b"),
+ new BasicNameValuePair("Name7", "ccc")
+ ), CoreMatchers.equalTo("Name7=aaa&Name7=b%2Cb&Name7=ccc"));
+ MatcherAssert.assertThat(format(new BasicNameValuePair("Name8", "xx, yy ,zz")),
+ CoreMatchers.equalTo("Name8=xx%2C++yy++%2Czz"));
+ MatcherAssert.assertThat(format(
+ new BasicNameValuePair("russian", RU_HELLO),
+ new BasicNameValuePair("swiss", CH_HELLO)),
+ CoreMatchers.equalTo("russian=" + PercentCodec.encode(RU_HELLO, StandardCharsets.UTF_8) +
+ "&swiss=" + PercentCodec.encode(CH_HELLO, StandardCharsets.UTF_8)));
+ }
+
+}