HTTPCLIENT-1195: URIBuilder now uses single argument URI constructor to create URI instance

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1349530 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2012-06-12 21:11:47 +00:00
parent c429b34ca9
commit 637f046eb1
4 changed files with 303 additions and 101 deletions

View File

@ -35,24 +35,29 @@ import java.util.List;
import org.apache.http.Consts;
import org.apache.http.NameValuePair;
import org.apache.http.conn.util.InetAddressUtils;
import org.apache.http.message.BasicNameValuePair;
/**
* {@link URI} builder for HTTP requests.
*
*
* @since 4.2
*/
public class URIBuilder {
private String scheme;
private String schemeSpecificPart;
private String authority;
private String encodedSchemeSpecificPart;
private String encodedAuthority;
private String userInfo;
private String encodedUserInfo;
private String host;
private int port;
private String path;
private String encodedPath;
private String encodedQuery;
private List<NameValuePair> queryParams;
private String fragment;
private String encodedFragment;
public URIBuilder() {
super();
@ -76,52 +81,90 @@ public class URIBuilder {
return null;
}
private String formatQuery(final List<NameValuePair> parameters) {
if (parameters == null) {
return null;
}
final StringBuilder result = new StringBuilder();
for (final NameValuePair parameter : parameters) {
if (result.length() > 0) {
result.append("&");
}
result.append(parameter.getName());
if (parameter.getValue() != null) {
result.append("=");
result.append(parameter.getValue());
}
}
return result.toString();
}
/**
* Builds a {@link URI} instance.
*/
public URI build() throws URISyntaxException {
if (this.schemeSpecificPart != null) {
return new URI(this.scheme, this.schemeSpecificPart, this.fragment);
} else if (this.authority != null) {
return new URI(this.scheme, this.authority,
this.path, formatQuery(this.queryParams), this.fragment);
} else {
return new URI(this.scheme, this.userInfo, this.host, this.port,
this.path, formatQuery(this.queryParams), this.fragment);
return new URI(buildString());
}
private String buildString() {
StringBuilder sb = new StringBuilder();
if (this.scheme != null) {
sb.append(this.scheme).append(':');
}
if (this.encodedSchemeSpecificPart != null) {
sb.append(this.encodedSchemeSpecificPart);
} else {
if (this.encodedAuthority != null) {
sb.append("//").append(this.encodedAuthority);
} else if (this.host != null) {
sb.append("//");
if (this.encodedUserInfo != null) {
sb.append(this.encodedUserInfo).append("@");
} else if (this.userInfo != null) {
sb.append(encodeUserInfo(this.userInfo)).append("@");
}
if (InetAddressUtils.isIPv6Address(this.host)) {
sb.append("[").append(this.host).append("]");
} else {
sb.append(this.host);
}
if (this.port >= 0) {
sb.append(":").append(this.port);
}
}
if (this.encodedPath != null) {
sb.append(this.encodedPath);
} else if (this.path != null) {
sb.append(encodePath(this.path));
}
if (this.encodedQuery != null) {
sb.append("?").append(this.encodedQuery);
} else if (this.queryParams != null) {
sb.append("?").append(encodeQuery(this.queryParams));
}
}
if (this.encodedFragment != null) {
sb.append("#").append(this.encodedFragment);
} else if (this.fragment != null) {
sb.append("#").append(encodeFragment(this.fragment));
}
return sb.toString();
}
private void digestURI(final URI uri) {
this.scheme = uri.getScheme();
this.schemeSpecificPart = uri.getSchemeSpecificPart();
this.authority = uri.getAuthority();
this.encodedSchemeSpecificPart = uri.getRawSchemeSpecificPart();
this.encodedAuthority = uri.getRawAuthority();
this.host = uri.getHost();
this.port = uri.getPort();
this.encodedUserInfo = uri.getRawUserInfo();
this.userInfo = uri.getUserInfo();
this.encodedPath = uri.getRawPath();
this.path = uri.getPath();
this.encodedQuery = uri.getQuery();
this.queryParams = parseQuery(uri.getRawQuery(), Consts.UTF_8);
this.encodedFragment = uri.getRawFragment();
this.fragment = uri.getFragment();
}
private String encodeUserInfo(final String userInfo) {
return URLEncodedUtils.enc(userInfo, Consts.UTF_8);
}
private String encodePath(final String path) {
return URLEncodedUtils.encPath(path, Consts.UTF_8);
}
private String encodeQuery(final List<NameValuePair> params) {
return URLEncodedUtils.format(params, Consts.UTF_8);
}
private String encodeFragment(final String fragment) {
return URLEncodedUtils.enc(fragment, Consts.UTF_8);
}
/**
* Sets URI scheme.
*/
@ -131,17 +174,20 @@ public class URIBuilder {
}
/**
* Sets URI user info.
* Sets URI user info. The value is expected to be unescaped and may contain non ASCII
* characters.
*/
public URIBuilder setUserInfo(final String userInfo) {
this.userInfo = userInfo;
this.schemeSpecificPart = null;
this.authority = null;
this.encodedSchemeSpecificPart = null;
this.encodedAuthority = null;
this.encodedUserInfo = null;
return this;
}
/**
* Sets URI user info as a combination of username and password.
* Sets URI user info as a combination of username and password. These values are expected to
* be unescaped and may contain non ASCII characters.
*/
public URIBuilder setUserInfo(final String username, final String password) {
return setUserInfo(username + ':' + password);
@ -152,8 +198,8 @@ public class URIBuilder {
*/
public URIBuilder setHost(final String host) {
this.host = host;
this.schemeSpecificPart = null;
this.authority = null;
this.encodedSchemeSpecificPart = null;
this.encodedAuthority = null;
return this;
}
@ -162,8 +208,8 @@ public class URIBuilder {
*/
public URIBuilder setPort(final int port) {
this.port = port < 0 ? -1 : port;
this.schemeSpecificPart = null;
this.authority = null;
this.encodedSchemeSpecificPart = null;
this.encodedAuthority = null;
return this;
}
@ -172,7 +218,8 @@ public class URIBuilder {
*/
public URIBuilder setPath(final String path) {
this.path = path;
this.schemeSpecificPart = null;
this.encodedSchemeSpecificPart = null;
this.encodedPath = null;
return this;
}
@ -181,7 +228,8 @@ public class URIBuilder {
*/
public URIBuilder removeQuery() {
this.queryParams = null;
this.schemeSpecificPart = null;
this.encodedQuery = null;
this.encodedSchemeSpecificPart = null;
return this;
}
@ -190,12 +238,13 @@ public class URIBuilder {
*/
public URIBuilder setQuery(final String query) {
this.queryParams = parseQuery(query, Consts.UTF_8);
this.schemeSpecificPart = null;
this.encodedQuery = null;
this.encodedSchemeSpecificPart = null;
return this;
}
/**
* Adds parameter to URI query. The parameter name and value are expected to be unescaped
* Adds parameter to URI query. The parameter name and value are expected to be unescaped
* and may contain non ASCII characters.
*/
public URIBuilder addParameter(final String param, final String value) {
@ -203,12 +252,13 @@ public class URIBuilder {
this.queryParams = new ArrayList<NameValuePair>();
}
this.queryParams.add(new BasicNameValuePair(param, value));
this.schemeSpecificPart = null;
this.encodedQuery = null;
this.encodedSchemeSpecificPart = null;
return this;
}
/**
* Sets parameter of URI query overriding existing value if set. The parameter name and value
* Sets parameter of URI query overriding existing value if set. The parameter name and value
* are expected to be unescaped and may contain non ASCII characters.
*/
public URIBuilder setParameter(final String param, final String value) {
@ -224,15 +274,18 @@ public class URIBuilder {
}
}
this.queryParams.add(new BasicNameValuePair(param, value));
this.schemeSpecificPart = null;
this.encodedQuery = null;
this.encodedSchemeSpecificPart = null;
return this;
}
/**
* Sets URI fragment.
* Sets URI fragment. The value is expected to be unescaped and may contain non ASCII
* characters.
*/
public URIBuilder setFragment(final String fragment) {
this.fragment = fragment;
this.encodedFragment = null;
return this;
}
@ -270,13 +323,7 @@ public class URIBuilder {
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("URI [scheme=").append(this.scheme)
.append(", userInfo=").append(this.userInfo).append(", host=").append(this.host)
.append(", port=").append(this.port).append(", path=").append(this.path)
.append(", queryParams=").append(this.queryParams).append(", fragment=")
.append(this.fragment).append("]");
return builder.toString();
return buildString();
}
}

View File

@ -28,12 +28,12 @@
package org.apache.http.client.utils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
@ -258,42 +258,138 @@ public class URLEncodedUtils {
return result.toString();
}
private static final BitSet UNRESERVED = new BitSet(256);
private static final BitSet PUNCT = new BitSet(256);
private static final BitSet SAFE = new BitSet(256);
private static final BitSet PATHSAFE = new BitSet(256);
static {
// unreserved chars
// alpha characters
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('.');
UNRESERVED.set('~');
UNRESERVED.set('\'');
UNRESERVED.set('(');
UNRESERVED.set(')');
UNRESERVED.set('*');
// punct chats
PUNCT.set(',');
PUNCT.set(';');
PUNCT.set(':');
PUNCT.set('$');
PUNCT.set('&');
PUNCT.set('+');
PUNCT.set('=');
// URL path safe
SAFE.or(UNRESERVED);
SAFE.or(PUNCT);
// URL path safe
PATHSAFE.or(UNRESERVED);
PATHSAFE.or(PUNCT);
PATHSAFE.set('/');
PATHSAFE.set('@');
}
private static final int RADIX = 16;
private static String urlencode(
final String content, final Charset charset, final BitSet safechars) {
if (content == null) {
return null;
}
StringBuilder buf = new StringBuilder();
ByteBuffer bb = charset.encode(content);
while (bb.hasRemaining()) {
int b = bb.get() & 0xff;
if (safechars.get(b)) {
buf.append((char) b);
} else {
buf.append("%");
char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, RADIX));
char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, RADIX));
buf.append(hex1);
buf.append(hex2);
}
}
return buf.toString();
}
private static String urldecode(
final String content, final Charset charset) {
if (content == null) {
return null;
}
ByteBuffer bb = ByteBuffer.allocate(content.length());
CharBuffer cb = CharBuffer.wrap(content);
while (cb.hasRemaining()) {
char c = cb.get();
if (c == '%' && cb.remaining() >= 2) {
char uc = cb.get();
char lc = cb.get();
int u = Character.digit(uc, 16);
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 {
bb.put((byte) c);
}
}
bb.flip();
return charset.decode(bb).toString();
}
private static String decode (final String content, final String charset) {
if (content == null) {
return null;
}
try {
return URLDecoder.decode(content,
charset != null ? charset : HTTP.DEF_CONTENT_CHARSET.name());
} catch (UnsupportedEncodingException ex) {
throw new IllegalArgumentException(ex);
}
return urldecode(content, charset != null ? Charset.forName(charset) : Consts.UTF_8);
}
private static String decode (final String content, final Charset charset) {
if (content == null) {
return null;
}
return decode(content, charset != null ? charset.name() : null);
return urldecode(content, charset != null ? charset : Consts.UTF_8);
}
private static String encode (final String content, final String charset) {
private static String encode(final String content, final String charset) {
if (content == null) {
return null;
}
try {
return URLEncoder.encode(content,
charset != null ? charset : HTTP.DEF_CONTENT_CHARSET.name());
} catch (UnsupportedEncodingException ex) {
throw new IllegalArgumentException(ex);
}
return urlencode(content, charset != null ? Charset.forName(charset) : Consts.UTF_8, UNRESERVED);
}
private static String encode (final String content, final Charset charset) {
private static String encode(final String content, final Charset charset) {
if (content == null) {
return null;
}
return encode(content, charset != null ? charset.name() : null);
return urlencode(content, charset != null ? charset : Consts.UTF_8, UNRESERVED);
}
static String enc(final String content, final Charset charset) {
return urlencode(content, charset, SAFE);
}
static String encPath(final String content, final Charset charset) {
return urlencode(content, charset, PATHSAFE);
}
}

View File

@ -38,7 +38,36 @@ public class TestURIBuilder {
URI uri = new URI("http", "stuff", "localhost", 80, "/some stuff", "param=stuff", "fragment");
URIBuilder uribuilder = new URIBuilder(uri);
URI result = uribuilder.build();
Assert.assertEquals(uri, result);
Assert.assertEquals(new URI("http://stuff@localhost:80/some%20stuff?param=stuff#fragment"), result);
}
@Test
public void testMutationToRelativeUri() throws Exception {
URI uri = new URI("http://stuff@localhost:80/stuff?param=stuff#fragment");
URIBuilder uribuilder = new URIBuilder(uri).setHost(null);
URI result = uribuilder.build();
Assert.assertEquals(new URI("http:///stuff?param=stuff#fragment"), result);
}
@Test
public void testMutationRemoveFragment() throws Exception {
URI uri = new URI("http://stuff@localhost:80/stuff?param=stuff#fragment");
URI result = new URIBuilder(uri).setFragment(null).build();
Assert.assertEquals(new URI("http://stuff@localhost:80/stuff?param=stuff"), result);
}
@Test
public void testMutationRemoveUserInfo() throws Exception {
URI uri = new URI("http://stuff@localhost:80/stuff?param=stuff#fragment");
URI result = new URIBuilder(uri).setUserInfo(null).build();
Assert.assertEquals(new URI("http://localhost:80/stuff?param=stuff#fragment"), result);
}
@Test
public void testMutationRemovePort() throws Exception {
URI uri = new URI("http://stuff@localhost:80/stuff?param=stuff#fragment");
URI result = new URIBuilder(uri).setPort(-1).build();
Assert.assertEquals(new URI("http://stuff@localhost/stuff?param=stuff#fragment"), result);
}
@Test
@ -94,6 +123,16 @@ public class TestURIBuilder {
Assert.assertEquals(new URI("http://localhost:80/?param=some%20other%20stuff&blah=blah"), result);
}
@Test
public void testParameterWithSpecialChar() throws Exception {
URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff", null);
URIBuilder uribuilder = new URIBuilder(uri).addParameter("param", "1 + 1 = 2")
.addParameter("param", "blah&blah");
URI result = uribuilder.build();
Assert.assertEquals(new URI("http://localhost:80/?param=stuff&param=1%20%2B%201%20%3D%202&" +
"param=blah%26blah"), result);
}
@Test
public void testAddParameter() throws Exception {
URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff&blah&blah", null);
@ -107,13 +146,13 @@ public class TestURIBuilder {
@Test
public void testQueryEncoding() throws Exception {
URI uri1 = new URI("https://somehost.com/stuff?client_id=1234567890" +
"&redirect_uri=https://somehost.com/blah%20blah/");
"&redirect_uri=https%3A%2F%2Fsomehost.com%2Fblah%20blah%2F");
URI uri2 = new URIBuilder("https://somehost.com/stuff")
.addParameter("client_id","1234567890")
.addParameter("redirect_uri","https://somehost.com/blah blah/").build();
Assert.assertEquals(uri1, uri2);
}
@Test
public void testPathEncoding() throws Exception {
URI uri1 = new URI("https://somehost.com/some%20path%20with%20blanks/");
@ -124,5 +163,5 @@ public class TestURIBuilder {
.build();
Assert.assertEquals(uri1, uri2);
}
}

View File

@ -27,7 +27,6 @@
package org.apache.http.client.utils;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
@ -43,54 +42,75 @@ import org.junit.Test;
public class TestURLEncodedUtils {
@Test
public void testParseURI () throws Exception {
public void testParseURLCodedContent () throws Exception {
List <NameValuePair> result;
result = parse("", null);
result = parse("");
Assert.assertTrue(result.isEmpty());
result = parse("Name0", null);
result = parse("Name0");
Assert.assertEquals(1, result.size());
assertNameValuePair(result.get(0), "Name0", null);
result = parse("Name1=Value1", null);
result = parse("Name1=Value1");
Assert.assertEquals(1, result.size());
assertNameValuePair(result.get(0), "Name1", "Value1");
result = parse("Name2=", null);
result = parse("Name2=");
Assert.assertEquals(1, result.size());
assertNameValuePair(result.get(0), "Name2", "");
result = parse("Name3", null);
result = parse("Name3");
Assert.assertEquals(1, result.size());
assertNameValuePair(result.get(0), "Name3", null);
result = parse("Name4=Value+4%21", 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", null);
result = parse("Name4=Value%2B4%21");
Assert.assertEquals(1, result.size());
assertNameValuePair(result.get(0), "Name4", "Value+4!");
result = parse("Name4=Value+4%21+%214", null);
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", null);
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", null);
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++yy++%2Czz", null);
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 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 ");
}
@Test
@ -172,7 +192,7 @@ public class TestURLEncodedUtils {
String s = URLEncodedUtils.format(parameters, HTTP.DEF_CONTENT_CHARSET);
Assert.assertEquals("english=hi+there&swiss=Gr%FCezi_z%E4m%E4", s);
Assert.assertEquals("english=hi%20there&swiss=Gr%FCezi_z%E4m%E4", s);
StringEntity entity = new StringEntity(s, ContentType.create(
URLEncodedUtils.CONTENT_TYPE, HTTP.DEF_CONTENT_CHARSET));
@ -214,16 +234,16 @@ public class TestURLEncodedUtils {
Assert.assertEquals("Name2=", URLEncodedUtils.format(params, Consts.ASCII));
params.clear();
params.add(new BasicNameValuePair("Name4", "Value 4!"));
Assert.assertEquals("Name4=Value+4%21", URLEncodedUtils.format(params, Consts.ASCII));
params.add(new BasicNameValuePair("Name4", "Value 4&"));
Assert.assertEquals("Name4=Value%204%26", URLEncodedUtils.format(params, Consts.ASCII));
params.clear();
params.add(new BasicNameValuePair("Name4", "Value+4!"));
Assert.assertEquals("Name4=Value%2B4%21", URLEncodedUtils.format(params, Consts.ASCII));
params.add(new BasicNameValuePair("Name4", "Value+4&"));
Assert.assertEquals("Name4=Value%2B4%26", URLEncodedUtils.format(params, Consts.ASCII));
params.clear();
params.add(new BasicNameValuePair("Name4", "Value 4! !4"));
Assert.assertEquals("Name4=Value+4%21+%214", URLEncodedUtils.format(params, Consts.ASCII));
params.add(new BasicNameValuePair("Name4", "Value 4& =4"));
Assert.assertEquals("Name4=Value%204%26%20%3D4", URLEncodedUtils.format(params, Consts.ASCII));
params.clear();
params.add(new BasicNameValuePair("Name5", "aaa"));
@ -238,11 +258,11 @@ public class TestURLEncodedUtils {
params.clear();
params.add(new BasicNameValuePair("Name8", "xx, yy ,zz"));
Assert.assertEquals("Name8=xx%2C++yy++%2Czz", URLEncodedUtils.format(params, Consts.ASCII));
Assert.assertEquals("Name8=xx%2C%20%20yy%20%20%2Czz", URLEncodedUtils.format(params, Consts.ASCII));
}
private List <NameValuePair> parse (final String params, final String encoding) {
return URLEncodedUtils.parse(URI.create("http://hc.apache.org/params?" + params), encoding);
private List <NameValuePair> parse (final String params) {
return URLEncodedUtils.parse(params, Consts.UTF_8);
}
private static void assertNameValuePair (