Merge pull request #137 from ok2c/HTTPCLIENT-1968

URIBuilder and URIUtils improvements
This commit is contained in:
Oleg Kalnichevski 2019-02-19 09:27:04 +01:00 committed by GitHub
commit acd8fcd95f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 361 additions and 151 deletions

View File

@ -27,9 +27,8 @@
package org.apache.http.impl.client.cache;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
@ -44,7 +43,11 @@ import org.apache.http.annotation.Contract;
import org.apache.http.annotation.ThreadingBehavior;
import org.apache.http.client.cache.HeaderConstants;
import org.apache.http.client.cache.HttpCacheEntry;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.util.Args;
/**
* @since 4.1
@ -54,6 +57,54 @@ class CacheKeyGenerator {
private static final URI BASE_URI = URI.create("http://example.com/");
static URIBuilder getRequestUriBuilder(final HttpRequest request) throws URISyntaxException {
if (request instanceof HttpUriRequest) {
final URI uri = ((HttpUriRequest) request).getURI();
if (uri != null) {
return new URIBuilder(uri);
}
}
return new URIBuilder(request.getRequestLine().getUri());
}
static URI getRequestUri(final HttpRequest request, final HttpHost target) throws URISyntaxException {
Args.notNull(request, "HTTP request");
Args.notNull(target, "Target");
final URIBuilder uriBuilder = getRequestUriBuilder(request);
// Decode path segments to preserve original behavior for backward compatibility
final String path = uriBuilder.getPath();
if (path != null) {
uriBuilder.setPathSegments(URLEncodedUtils.parsePathSegments(path));
}
if (!uriBuilder.isAbsolute()) {
uriBuilder.setScheme(target.getSchemeName());
uriBuilder.setHost(target.getHostName());
uriBuilder.setPort(target.getPort());
}
return uriBuilder.build();
}
static URI normalize(final URI requestUri) throws URISyntaxException {
Args.notNull(requestUri, "URI");
final URIBuilder builder = new URIBuilder(requestUri.isAbsolute() ? URIUtils.resolve(BASE_URI, requestUri) : requestUri) ;
if (builder.getHost() != null) {
if (builder.getScheme() == null) {
builder.setScheme("http");
}
if (builder.getPort() <= -1) {
if ("http".equalsIgnoreCase(builder.getScheme())) {
builder.setPort(80);
} else if ("https".equalsIgnoreCase(builder.getScheme())) {
builder.setPort(443);
}
}
}
builder.setFragment(null);
return builder.build();
}
/**
* For a given {@link HttpHost} and {@link HttpRequest} get a URI from the
* pair that I can use as an identifier KEY into my HttpCache
@ -63,45 +114,23 @@ class CacheKeyGenerator {
* @return String the extracted URI
*/
public String getURI(final HttpHost host, final HttpRequest req) {
if (isRelativeRequest(req)) {
return canonicalizeUri(String.format("%s%s", host.toString(), req.getRequestLine().getUri()));
try {
final URI uri = normalize(getRequestUri(req, host));
return uri.toASCIIString();
} catch (final URISyntaxException ex) {
return req.getRequestLine().getUri();
}
return canonicalizeUri(req.getRequestLine().getUri());
}
public String canonicalizeUri(final String uri) {
try {
final URI normalized = URIUtils.resolve(BASE_URI, uri);
final URL u = new URL(normalized.toASCIIString());
final String protocol = u.getProtocol();
final String hostname = u.getHost();
final int port = canonicalizePort(u.getPort(), protocol);
final String path = u.getPath();
final String query = u.getQuery();
final String file = (query != null) ? (path + "?" + query) : path;
final URL out = new URL(protocol, hostname, port, file);
return out.toString();
} catch (final IllegalArgumentException e) {
return uri;
} catch (final MalformedURLException e) {
final URI normalized = normalize(URIUtils.resolve(BASE_URI, uri));
return normalized.toASCIIString();
} catch (final URISyntaxException ex) {
return uri;
}
}
private int canonicalizePort(final int port, final String protocol) {
if (port == -1 && "http".equalsIgnoreCase(protocol)) {
return 80;
} else if (port == -1 && "https".equalsIgnoreCase(protocol)) {
return 443;
}
return port;
}
private boolean isRelativeRequest(final HttpRequest req) {
final String requestUri = req.getRequestLine().getUri();
return ("*".equals(requestUri) || requestUri.startsWith("/"));
}
protected String getFullHeaderValue(final Header[] headers) {
if (headers == null) {
return "";

View File

@ -30,6 +30,8 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@ -53,8 +55,8 @@ public class URIBuilder {
private String encodedUserInfo;
private String host;
private int port;
private String path;
private String encodedPath;
private List<String> pathSegments;
private String encodedQuery;
private List<NameValuePair> queryParams;
private String query;
@ -112,6 +114,13 @@ public class URIBuilder {
return null;
}
private List <String> parsePath(final String path, final Charset charset) {
if (path != null && !path.isEmpty()) {
return URLEncodedUtils.parsePathSegments(path, charset);
}
return null;
}
/**
* Builds a {@link URI} instance.
*/
@ -147,8 +156,8 @@ public class URIBuilder {
}
if (this.encodedPath != null) {
sb.append(normalizePath(this.encodedPath, sb.length() == 0));
} else if (this.path != null) {
sb.append(encodePath(normalizePath(this.path, sb.length() == 0)));
} else if (this.pathSegments != null) {
sb.append(encodePath(this.pathSegments));
}
if (this.encodedQuery != null) {
sb.append("?").append(this.encodedQuery);
@ -186,7 +195,7 @@ public class URIBuilder {
this.encodedUserInfo = uri.getRawUserInfo();
this.userInfo = uri.getUserInfo();
this.encodedPath = uri.getRawPath();
this.path = uri.getPath();
this.pathSegments = parsePath(uri.getRawPath(), this.charset != null ? this.charset : Consts.UTF_8);
this.encodedQuery = uri.getRawQuery();
this.queryParams = parseQuery(uri.getRawQuery(), this.charset != null ? this.charset : Consts.UTF_8);
this.encodedFragment = uri.getRawFragment();
@ -197,8 +206,8 @@ public class URIBuilder {
return URLEncodedUtils.encUserInfo(userInfo, this.charset != null ? this.charset : Consts.UTF_8);
}
private String encodePath(final String path) {
return URLEncodedUtils.encPath(path, this.charset != null ? this.charset : Consts.UTF_8);
private String encodePath(final List<String> pathSegments) {
return URLEncodedUtils.formatSegments(pathSegments, this.charset != null ? this.charset : Consts.UTF_8);
}
private String encodeUrlForm(final List<NameValuePair> params) {
@ -259,9 +268,36 @@ public class URIBuilder {
/**
* Sets URI path. The value is expected to be unescaped and may contain non ASCII characters.
*
* @return this.
*/
public URIBuilder setPath(final String path) {
this.path = path;
return setPathSegments(path != null ? URLEncodedUtils.splitPathSegments(path) : null);
}
/**
* Sets URI path. The value is expected to be unescaped and may contain non ASCII characters.
*
* @return this.
*
* @since 4.5.8
*/
public URIBuilder setPathSegments(final String... pathSegments) {
this.pathSegments = pathSegments.length > 0 ? Arrays.asList(pathSegments) : null;
this.encodedSchemeSpecificPart = null;
this.encodedPath = null;
return this;
}
/**
* Sets URI path. The value is expected to be unescaped and may contain non ASCII characters.
*
* @return this.
*
* @since 4.5.8
*/
public URIBuilder setPathSegments(final List<String> pathSegments) {
this.pathSegments = pathSegments != null && pathSegments.size() > 0 ? new ArrayList<String>(pathSegments) : null;
this.encodedSchemeSpecificPart = null;
this.encodedPath = null;
return this;
@ -462,7 +498,7 @@ public class URIBuilder {
* @since 4.3
*/
public boolean isOpaque() {
return this.path == null;
return isPathEmpty();
}
public String getScheme() {
@ -481,14 +517,40 @@ public class URIBuilder {
return this.port;
}
/**
* @since 4.5.8
*/
public boolean isPathEmpty() {
return (this.pathSegments == null || this.pathSegments.isEmpty()) && this.encodedPath == null;
}
/**
* @since 4.5.8
*/
public List<String> getPathSegments() {
return this.pathSegments != null ? new ArrayList<String>(this.pathSegments) : Collections.<String>emptyList();
}
public String getPath() {
return this.path;
if (this.pathSegments == null) {
return null;
}
final StringBuilder result = new StringBuilder();
for (final String segment : this.pathSegments) {
result.append('/').append(segment);
}
return result.toString();
}
/**
* @since 4.5.8
*/
public boolean isQueryEmpty() {
return (this.queryParams == null || this.queryParams.isEmpty()) && this.encodedQuery == null;
}
public List<NameValuePair> getQueryParams() {
return this.queryParams != null
? new ArrayList<NameValuePair>(this.queryParams)
: new ArrayList<NameValuePair>();
return this.queryParams != null ? new ArrayList<NameValuePair>(this.queryParams) : Collections.<NameValuePair>emptyList();
}
public String getFragment() {

View File

@ -28,7 +28,9 @@ package org.apache.http.client.utils;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Stack;
@ -49,7 +51,7 @@ public class URIUtils {
/**
* Flags that control how URI is being rewritten.
*
* @since 5.7.8
* @since 4.5.8
*/
public enum UriFlag {
DROP_FRAGMENT,
@ -59,28 +61,28 @@ public class URIUtils {
/**
* Empty set of uri flags.
*
* @since 5.7.8
* @since 4.5.8
*/
public static final EnumSet<UriFlag> NO_FLAGS = EnumSet.noneOf(UriFlag.class);
/**
* Set of uri flags containing {@link UriFlag#DROP_FRAGMENT}.
*
* @since 5.7.8
* @since 4.5.8
*/
public static final EnumSet<UriFlag> DROP_FRAGMENT = EnumSet.of(UriFlag.DROP_FRAGMENT);
/**
* Set of uri flags containing {@link UriFlag#NORMALIZE}.
*
* @since 5.7.8
* @since 4.5.8
*/
public static final EnumSet<UriFlag> NORMALIZE = EnumSet.of(UriFlag.NORMALIZE);
/**
* Set of uri flags containing {@link UriFlag#DROP_FRAGMENT} and {@link UriFlag#NORMALIZE}.
*
* @since 5.7.8
* @since 4.5.8
*/
public static final EnumSet<UriFlag> DROP_FRAGMENT_AND_NORMALIZE = EnumSet.of(UriFlag.DROP_FRAGMENT, UriFlag.NORMALIZE);
@ -164,7 +166,7 @@ public class URIUtils {
*
* @throws URISyntaxException
* If the resulting URI is invalid.
* @deprecated (5.7.8) Use {@link #rewriteURI(URI, HttpHost, EnumSet)}
* @deprecated (4.5.8) Use {@link #rewriteURI(URI, HttpHost, EnumSet)}
*/
@Deprecated
public static URI rewriteURI(
@ -190,7 +192,7 @@ public class URIUtils {
*
* @throws URISyntaxException
* If the resulting URI is invalid.
* @since 5.7.8
* @since 4.5.8
*/
public static URI rewriteURI(
final URI uri,
@ -214,24 +216,18 @@ public class URIUtils {
if (flags.contains(UriFlag.DROP_FRAGMENT)) {
uribuilder.setFragment(null);
}
final String path = uribuilder.getPath();
if (TextUtils.isEmpty(path)) {
uribuilder.setPath("/");
} else {
if (flags.contains(UriFlag.NORMALIZE)) {
final StringBuilder buf = new StringBuilder(path.length());
boolean foundSlash = false;
for (int i = 0; i < path.length(); i++) {
final char ch = path.charAt(i);
if (ch != '/' || !foundSlash) {
buf.append(ch);
}
foundSlash = ch == '/';
if (flags.contains(UriFlag.NORMALIZE)) {
final List<String> pathSegments = new ArrayList<String>(uribuilder.getPathSegments());
for (final Iterator<String> it = pathSegments.iterator(); it.hasNext(); ) {
final String pathSegment = it.next();
if (pathSegment.isEmpty() && it.hasNext()) {
it.remove();
}
uribuilder.setPath(buf.toString());
} else {
uribuilder.setPath(path);
}
uribuilder.setPathSegments(pathSegments);
}
if (uribuilder.isPathEmpty()) {
uribuilder.setPathSegments("");
}
return uribuilder.build();
}
@ -267,6 +263,9 @@ public class URIUtils {
if (uribuilder.getUserInfo() != null) {
uribuilder.setUserInfo(null);
}
if (uribuilder.getPathSegments().isEmpty()) {
uribuilder.setPathSegments("");
}
if (TextUtils.isEmpty(uribuilder.getPath())) {
uribuilder.setPath("/");
}
@ -288,6 +287,21 @@ public class URIUtils {
*
* @since 4.4
*/
public static URI rewriteURIForRoute(final URI uri, final RouteInfo route) throws URISyntaxException {
return rewriteURIForRoute(uri, route, true);
}
/**
* A convenience method that optionally converts the original {@link java.net.URI} either
* to a relative or an absolute form as required by the specified route.
*
* @param uri
* original URI.
* @throws URISyntaxException
* If the resulting URI is invalid.
*
* @since 4.5.8
*/
public static URI rewriteURIForRoute(final URI uri, final RouteInfo route, final boolean normalizeUri) throws URISyntaxException {
if (uri == null) {
return null;
@ -295,8 +309,8 @@ public class URIUtils {
if (route.getProxyHost() != null && !route.isTunnelled()) {
// Make sure the request URI is absolute
return uri.isAbsolute()
? rewriteURI(uri)
: rewriteURI(uri, route.getTargetHost(), normalizeUri ? DROP_FRAGMENT_AND_NORMALIZE : DROP_FRAGMENT);
? rewriteURI(uri)
: rewriteURI(uri, route.getTargetHost(), normalizeUri ? DROP_FRAGMENT_AND_NORMALIZE : DROP_FRAGMENT);
}
// Make sure the request URI is relative
return uri.isAbsolute() ? rewriteURI(uri, null, normalizeUri ? DROP_FRAGMENT_AND_NORMALIZE : DROP_FRAGMENT) : rewriteURI(uri);
@ -354,39 +368,32 @@ public class URIUtils {
*
* @param uri the original URI
* @return the URI without dot segments
*
* @since 4.5
*/
static URI normalizeSyntax(final URI uri) throws URISyntaxException {
public static URI normalizeSyntax(final URI uri) throws URISyntaxException {
if (uri.isOpaque() || uri.getAuthority() == null) {
// opaque and file: URIs
return uri;
}
Args.check(uri.isAbsolute(), "Base URI must be absolute");
final URIBuilder builder = new URIBuilder(uri);
final String path = builder.getPath();
if (path != null && !path.equals("/")) {
final String[] inputSegments = path.split("/");
final Stack<String> outputSegments = new Stack<String>();
for (final String inputSegment : inputSegments) {
if ((inputSegment.isEmpty()) || (".".equals(inputSegment))) {
// Do nothing
} else if ("..".equals(inputSegment)) {
if (!outputSegments.isEmpty()) {
outputSegments.pop();
}
} else {
outputSegments.push(inputSegment);
final List<String> inputSegments = builder.getPathSegments();
final Stack<String> outputSegments = new Stack<String>();
for (final String inputSegment : inputSegments) {
if (".".equals(inputSegment)) {
// Do nothing
} else if ("..".equals(inputSegment)) {
if (!outputSegments.isEmpty()) {
outputSegments.pop();
}
} else {
outputSegments.push(inputSegment);
}
final StringBuilder outputBuffer = new StringBuilder();
for (final String outputSegment : outputSegments) {
outputBuffer.append('/').append(outputSegment);
}
if (path.lastIndexOf('/') == path.length() - 1) {
// path.endsWith("/") || path.equals("")
outputBuffer.append('/');
}
builder.setPath(outputBuffer.toString());
}
if (outputSegments.size() == 0) {
outputSegments.add("");
}
builder.setPathSegments(outputSegments);
if (builder.getScheme() != null) {
builder.setScheme(builder.getScheme().toLowerCase(Locale.ROOT));
}

View File

@ -36,7 +36,9 @@ import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
@ -68,6 +70,12 @@ 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);
}
/**
* @deprecated 4.5 Use {@link #parse(URI, Charset)}
@ -78,19 +86,12 @@ public class URLEncodedUtils {
}
/**
* Returns a list of {@link NameValuePair NameValuePairs} as built from the URI's query portion. For example, a URI
* of {@code http://example.org/path/to/file?a=1&b=2&c=3} would return a list of three NameValuePairs, one for a=1,
* one for b=2, and one for c=3. By convention, {@code '&'} and {@code ';'} are accepted as parameter separators.
* <p>
* This is typically useful while parsing an HTTP PUT.
* Returns a list of {@link NameValuePair}s URI query parameters.
* By convention, {@code '&'} and {@code ';'} are accepted as parameter separators.
*
* This API is currently only used for testing.
*
* @param uri
* URI to parse
* @param charset
* Charset to use while parsing the query
* @return a list of {@link NameValuePair} as built from the URI's query portion.
* @param uri input URI.
* @param charset parameter charset.
* @return list of query parameters.
*
* @since 4.5
*/
@ -230,14 +231,12 @@ public class URLEncodedUtils {
}
/**
* Returns a list of {@link NameValuePair NameValuePairs} as parsed from the given string using the given character
* encoding. By convention, {@code '&'} and {@code ';'} are accepted as parameter separators.
* Returns a list of {@link NameValuePair}s URI query parameters.
* By convention, {@code '&'} and {@code ';'} are accepted as parameter separators.
*
* @param s
* text to parse.
* @param charset
* Encoding to use when decoding the parameters.
* @return a list of {@link NameValuePair} as built from the URI's query portion.
* @param s URI query component.
* @param charset charset to use when decoding the parameters.
* @return list of query parameters.
*
* @since 4.2
*/
@ -254,13 +253,10 @@ public class URLEncodedUtils {
* Returns a list of {@link NameValuePair NameValuePairs} as parsed from the given string using the given character
* encoding.
*
* @param s
* text to parse.
* @param charset
* Encoding to use when decoding the parameters.
* @param separators
* element separators.
* @return a list of {@link NameValuePair} as built from the URI's query portion.
* @param s input text.
* @param charset parameter charset.
* @param separators parameter separators.
* @return list of query parameters.
*
* @since 4.3
*/
@ -274,8 +270,7 @@ public class URLEncodedUtils {
}
/**
* Returns a list of {@link NameValuePair NameValuePairs} as parsed from the given string using
* the given character encoding.
* Returns a list of {@link NameValuePair}s parameters.
*
* @param buf
* text to parse.
@ -321,6 +316,98 @@ public class URLEncodedUtils {
return list;
}
static List<String> splitSegments(final CharSequence s, final BitSet separators) {
final ParserCursor cursor = new ParserCursor(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<String>();
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);
}
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.
*
* @param s URI path component.
* @param charset parameter charset.
* @return list of segments.
*
* @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 : Consts.UTF_8, false));
}
return list;
}
/**
* Returns a list of URI path segments.
*
* @param s URI path component.
* @return list of segments.
*
* @since 4.5
*/
public static List<String> parsePathSegments(final CharSequence s) {
return parsePathSegments(s, Consts.UTF_8);
}
/**
* Returns a string consisting of joint encoded path segments.
*
* @param segments the segments.
* @param charset parameter charset.
* @return URI path component
*
* @since 4.5
*/
public static String formatSegments(final Iterable<String> segments, final Charset charset) {
Args.notNull(segments, "Segments");
final StringBuilder result = new StringBuilder();
for (final String segment : segments) {
result.append(PATH_SEPARATOR).append(urlEncode(segment, charset, PATHSAFE, false));
}
return result.toString();
}
/**
* Returns a string consisting of joint encoded path segments.
*
* @param segments the segments.
* @return URI path component
*
* @since 4.5
*/
public static String formatSegments(final String... segments) {
return formatSegments(Arrays.asList(segments), Consts.UTF_8);
}
/**
* 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.
@ -454,6 +541,8 @@ public class URLEncodedUtils {
*/
private static final BitSet URLENCODER = new BitSet(256);
private static final BitSet PATH_SPECIAL = new BitSet(256);
static {
// unreserved chars
// alpha characters
@ -491,9 +580,8 @@ public class URLEncodedUtils {
// URL path safe
PATHSAFE.or(UNRESERVED);
PATHSAFE.set('/'); // segment separator
PATHSAFE.set(';'); // param separator
PATHSAFE.set(':'); // rest as per list in 2396, i.e. : @ & = + $ ,
PATHSAFE.set(':'); // RFC 2396
PATHSAFE.set('@');
PATHSAFE.set('&');
PATHSAFE.set('=');
@ -501,6 +589,9 @@ public class URLEncodedUtils {
PATHSAFE.set('$');
PATHSAFE.set(',');
PATH_SPECIAL.or(PATHSAFE);
PATH_SPECIAL.set('/');
RESERVED.set(';');
RESERVED.set('/');
RESERVED.set('?');
@ -683,7 +774,7 @@ public class URLEncodedUtils {
}
/**
* Encode a String using the {@link #PATHSAFE} set of characters.
* Encode a String using the {@link #PATH_SPECIAL} set of characters.
* <p>
* Used by URIBuilder to encode path segments.
*
@ -692,7 +783,7 @@ public class URLEncodedUtils {
* @return the encoded string
*/
static String encPath(final String content, final Charset charset) {
return urlEncode(content, charset, PATHSAFE, false);
return urlEncode(content, charset, PATH_SPECIAL, false);
}
}

View File

@ -29,7 +29,6 @@ package org.apache.http.impl.client;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Locale;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -49,12 +48,10 @@ import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.Args;
import org.apache.http.util.Asserts;
import org.apache.http.util.TextUtils;
/**
* Default implementation of {@link RedirectStrategy}. This strategy honors the restrictions
@ -146,13 +143,14 @@ public class DefaultRedirectStrategy implements RedirectStrategy {
final RequestConfig config = clientContext.getRequestConfig();
URI uri = createLocationURI(location);
if (config.isNormalizeUri()) {
uri = uri.normalize();
}
// rfc2616 demands the location value be a complete URI
// Location = "Location" ":" absoluteURI
try {
if (config.isNormalizeUri()) {
uri = URIUtils.normalizeSyntax(uri);
}
// rfc2616 demands the location value be a complete URI
// Location = "Location" ":" absoluteURI
if (!uri.isAbsolute()) {
if (!config.isRelativeRedirectsAllowed()) {
throw new ProtocolException("Relative redirect location '"
@ -190,16 +188,7 @@ public class DefaultRedirectStrategy implements RedirectStrategy {
*/
protected URI createLocationURI(final String location) throws ProtocolException {
try {
final URIBuilder b = new URIBuilder(new URI(location));
final String host = b.getHost();
if (host != null) {
b.setHost(host.toLowerCase(Locale.ROOT));
}
final String path = b.getPath();
if (TextUtils.isEmpty(path)) {
b.setPath("/");
}
return b.build();
return new URI(location);
} catch (final URISyntaxException ex) {
throw new ProtocolException("Invalid redirect URI: " + location, ex);
}

View File

@ -137,7 +137,7 @@ public class TestURIUtils {
Assert.assertEquals("example://a/b/c/%7Bfoo%7D", URIUtils.resolve(this.baseURI, "eXAMPLE://a/./b/../b/%63/%7bfoo%7d").toString());
Assert.assertEquals("http://www.example.com/%3C", URIUtils.resolve(this.baseURI, "http://www.example.com/%3c").toString());
Assert.assertEquals("http://www.example.com/", URIUtils.resolve(this.baseURI, "HTTP://www.EXAMPLE.com/").toString());
Assert.assertEquals("http://www.example.com/a/", URIUtils.resolve(this.baseURI, "http://www.example.com/a%2f").toString());
Assert.assertEquals("http://www.example.com/a%2F", URIUtils.resolve(this.baseURI, "http://www.example.com/a%2f").toString());
Assert.assertEquals("http://www.example.com/?q=%26", URIUtils.resolve(this.baseURI, "http://www.example.com/?q=%26").toString());
Assert.assertEquals("http://www.example.com/%23?q=%26", URIUtils.resolve(this.baseURI, "http://www.example.com/%23?q=%26").toString());
Assert.assertEquals("http://www.example.com/blah-(%20-blah-%20&%20-blah-%20)-blah/",

View File

@ -29,6 +29,8 @@ package org.apache.http.client.utils;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.apache.http.Consts;
@ -37,6 +39,7 @@ import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.hamcrest.CoreMatchers;
import org.junit.Assert;
import org.junit.Test;
@ -102,6 +105,42 @@ public class TestURLEncodedUtils {
assertNameValuePair(result.get(1), "d", "e");
}
@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;

View File

@ -385,13 +385,6 @@ public class TestDefaultRedirectStrategy {
Assert.assertSame(entity, ((HttpEntityEnclosingRequest) redirect2).getEntity());
}
@Test
public void testCreateLocationURI() throws Exception {
final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
Assert.assertEquals("http://blahblah/",
redirectStrategy.createLocationURI("http://BlahBlah").toASCIIString());
}
@Test(expected=ProtocolException.class)
public void testCreateLocationURIInvalid() throws Exception {
final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy();