HTTPCLIENT-861: URI reference resolution fails examples in RFC 3986

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@794870 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2009-07-16 21:30:38 +00:00
parent d780072ad5
commit 55d9c6f161
4 changed files with 328 additions and 98 deletions

View File

@ -47,21 +47,25 @@ Important notes
Some protected variables in connection management class have been Some protected variables in connection management class have been
made final in order to help ensure their thread safety: made final in order to help ensure their thread safety:
org.apache.http.conn.BasicEofSensorWatcher#attemptReuse org.apache.http.conn.BasicEofSensorWatcher#attemptReuse
org.apache.http.conn.BasicEofSensorWatcher#managedConn org.apache.http.conn.BasicEofSensorWatcher#managedConn
org.apache.http.impl.conn.DefaultClientConnectionOperator#schemeRegistry org.apache.http.impl.conn.DefaultClientConnectionOperator#schemeRegistry
org.apache.http.impl.conn.DefaultHttpRoutePlanner#schemeRegistry org.apache.http.impl.conn.DefaultHttpRoutePlanner#schemeRegistry
org.apache.http.impl.conn.ProxySelectorRoutePlanner#schemeRegistry org.apache.http.impl.conn.ProxySelectorRoutePlanner#schemeRegistry
org.apache.http.impl.conn.SingleClientConnManager#alwaysShutDown org.apache.http.impl.conn.SingleClientConnManager#alwaysShutDown
org.apache.http.impl.conn.SingleClientConnManager#connOperator org.apache.http.impl.conn.SingleClientConnManager#connOperator
org.apache.http.impl.conn.SingleClientConnManager#schemeRegistry org.apache.http.impl.conn.SingleClientConnManager#schemeRegistry
org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager#connOperator org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager#connOperator
org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager#schemeRegistry org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager#schemeRegistry
Bug fixes since 4.0 BETA2 release Bug fixes since 4.0 BETA2 release
------------------- -------------------
* [HTTPCLIENT-861] URIUtils#resolve is now compatible with all examples given
in RFC 3986.
Contributed by Johannes Koch <johannes.koch at fit.fraunhofer.de>
* [HTTPCLIENT-860] HttpClient no longer converts redirects of PUT/POST to GET * [HTTPCLIENT-860] HttpClient no longer converts redirects of PUT/POST to GET
for status codes 301, 302, 307, as required by the HTTP spec. for status codes 301, 302, 307, as required by the HTTP spec.
Contributed by Oleg Kalnichevski <olegk at apache.org> Contributed by Oleg Kalnichevski <olegk at apache.org>
@ -153,85 +157,6 @@ All upstream projects are strongly encouraged to upgrade.
Contributed by Oleg Kalnichevski <olegk at apache.org> Contributed by Oleg Kalnichevski <olegk at apache.org>
HttpClient API changes (generated by JarDiff 0.2)
--------------------------------------
Class changed: org.apache.http.conn.scheme.PlainSocketFactory
Methods removed:
public boolean equals(java.lang.Object);
public int hashCode();
Class changed: org.apache.http.conn.ssl.AbstractVerifier
Method changed:
old:
public final boolean verify(java.lang.String, javax.net.ssl.SSLSession);
new:
deprecated: public final boolean verify(java.lang.String, javax.net.ssl.SSLSession);
Class changed: org.apache.http.impl.conn.tsccm.BasicPoolEntry
Methods added:
protected void shutdownEntry();
Class changed: org.apache.http.impl.cookie.RFC2965Spec
Methods added:
protected java.util.List parse(org.apache.http.HeaderElement[], org.apache.http.cookie.CookieOrigin) throws org.apache.http.cookie.MalformedCookieException;
API diff generated by JarDiff http://www.osjava.org/jardiff/
HttpMime API changes (generated by JarDiff 0.2)
--------------------------------------
Class added:
public org.apache.http.entity.mime.UnexpectedMimeException extends java.lang.RuntimeException
Class added:
public abstract org.apache.http.entity.mime.content.AbstractContentBody extends org.apache.james.mime4j.message.AbstractBody implements org.apache.http.entity.mime.content.ContentBody
Class changed: org.apache.http.entity.mime.content.FileBody
Methods removed:
public java.util.Map getContentTypeParameters();
public java.lang.String getMediaType();
public java.lang.String getMimeType();
public java.lang.String getSubType();
Methods added:
public FileBody(java.io.File, java.lang.String);
Class descriptor changed:
old:
public org.apache.http.entity.mime.content.FileBody extends org.apache.james.mime4j.message.AbstractBody implements org.apache.james.mime4j.message.BinaryBody, org.apache.http.entity.mime.content.ContentBody
new:
public org.apache.http.entity.mime.content.FileBody extends org.apache.http.entity.mime.content.AbstractContentBody implements org.apache.james.mime4j.message.BinaryBody
Class changed: org.apache.http.entity.mime.content.InputStreamBody
Methods removed:
public java.util.Map getContentTypeParameters();
public java.lang.String getMediaType();
public java.lang.String getMimeType();
public java.lang.String getSubType();
Methods added:
public InputStreamBody(java.io.InputStream, java.lang.String, java.lang.String);
Class descriptor changed:
old:
public org.apache.http.entity.mime.content.InputStreamBody extends org.apache.james.mime4j.message.AbstractBody implements org.apache.james.mime4j.message.BinaryBody, org.apache.http.entity.mime.content.ContentBody
new:
public org.apache.http.entity.mime.content.InputStreamBody extends org.apache.http.entity.mime.content.AbstractContentBody implements org.apache.james.mime4j.message.BinaryBody
Class changed: org.apache.http.entity.mime.content.StringBody
Methods removed:
public java.lang.String getMediaType();
public java.lang.String getMimeType();
public java.lang.String getSubType();
Methods added:
public StringBody(java.lang.String, java.lang.String, java.nio.charset.Charset) throws java.io.UnsupportedEncodingException;
Class descriptor changed:
old:
public org.apache.http.entity.mime.content.StringBody extends org.apache.james.mime4j.message.AbstractBody implements org.apache.james.mime4j.message.TextBody, org.apache.http.entity.mime.content.ContentBody
new:
public org.apache.http.entity.mime.content.StringBody extends org.apache.http.entity.mime.content.AbstractContentBody implements org.apache.james.mime4j.message.TextBody
API diff generated by JarDiff http://www.osjava.org/jardiff/
--------------------------------------
4.0 Beta 1 4.0 Beta 1
------------------- -------------------

View File

@ -28,6 +28,7 @@ package org.apache.http.client.utils;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.Stack;
import net.jcip.annotations.Immutable; import net.jcip.annotations.Immutable;
@ -171,8 +172,8 @@ public class URIUtils {
} }
/** /**
* Resolves a URI reference against a base URI. Work-around for bug in * Resolves a URI reference against a base URI. Work-around for bugs in
* java.net.URI (<http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4708535>) * java.net.URI (e.g. <http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4708535>)
* *
* @param baseURI the base URI * @param baseURI the base URI
* @param reference the URI reference * @param reference the URI reference
@ -185,7 +186,11 @@ public class URIUtils {
if (reference == null) { if (reference == null) {
throw new IllegalArgumentException("Reference URI may nor be null"); throw new IllegalArgumentException("Reference URI may nor be null");
} }
boolean emptyReference = reference.toString().length() == 0; String s = reference.toString();
if (s.startsWith("?")) {
return resolveReferenceStartingWithQueryString(baseURI, reference);
}
boolean emptyReference = s.length() == 0;
if (emptyReference) { if (emptyReference) {
reference = URI.create("#"); reference = URI.create("#");
} }
@ -195,7 +200,60 @@ public class URIUtils {
resolved = URI.create(resolvedString.substring(0, resolved = URI.create(resolvedString.substring(0,
resolvedString.indexOf('#'))); resolvedString.indexOf('#')));
} }
return resolved; return removeDotSegments(resolved);
}
/**
* Resolves a reference starting with a query string.
*
* @param baseURI the base URI
* @param reference the URI reference starting with a query string
* @return the resulting URI
*/
private static URI resolveReferenceStartingWithQueryString(
final URI baseURI, final URI reference) {
String baseUri = baseURI.toString();
baseUri = baseUri.indexOf('?') > -1 ?
baseUri.substring(0, baseUri.indexOf('?')) : baseUri;
return URI.create(baseUri + reference.toString());
}
/**
* Removes dot segments according to RFC 3986, section 5.2.4
*
* @param uri the original URI
* @return the URI without dot segments
*/
private static URI removeDotSegments(URI uri) {
String path = uri.getPath();
if ((path == null) || (path.indexOf("/.") == -1)) {
// No dot segments to remove
return uri;
}
String[] inputSegments = path.split("/");
Stack<String> outputSegments = new Stack<String>();
for (int i = 0; i < inputSegments.length; i++) {
if ((inputSegments[i].length() == 0)
|| (".".equals(inputSegments[i]))) {
// Do nothing
} else if ("..".equals(inputSegments[i])) {
if (!outputSegments.isEmpty()) {
outputSegments.pop();
}
} else {
outputSegments.push(inputSegments[i]);
}
}
StringBuffer outputBuffer = new StringBuffer();
for (String outputSegment : outputSegments) {
outputBuffer.append('/').append(outputSegment);
}
try {
return new URI(uri.getScheme(), uri.getAuthority(),
outputBuffer.toString(), uri.getQuery(), uri.getFragment());
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
} }
/** /**

View File

@ -1,8 +1,4 @@
/* /*
* $HeadURL$
* $Revision$
* $Date$
*
* ==================================================================== * ====================================================================
* *
* Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more

View File

@ -0,0 +1,251 @@
/*
* ====================================================================
*
* 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.http.client.utils;
import java.net.URI;
import junit.framework.Assert;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* This TestCase contains test methods for URI resolving according to RFC 3986.
* The examples are listed in section "5.4 Reference Resolution Examples"
*/
public class TestURIUtils extends TestCase {
private URI baseURI = URI.create("http://a/b/c/d;p?q");
public TestURIUtils(final String testName) {
super(testName);
}
public static void main(String args[]) {
String[] testCaseName = { TestURIUtils.class.getName() };
junit.textui.TestRunner.main(testCaseName);
}
public static Test suite() {
return new TestSuite(TestURIUtils.class);
}
public void testResolve00() {
Assert.assertEquals("g:h", URIUtils.resolve(this.baseURI, "g:h").toString());
}
public void testResolve01() {
Assert.assertEquals("http://a/b/c/g", URIUtils.resolve(this.baseURI, "g").toString());
}
public void testResolve02() {
Assert.assertEquals("http://a/b/c/g", URIUtils.resolve(this.baseURI, "./g").toString());
}
public void testResolve03() {
Assert.assertEquals("http://a/b/c/g/", URIUtils.resolve(this.baseURI, "g/").toString());
}
public void testResolve04() {
Assert.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "/g").toString());
}
public void testResolve05() {
Assert.assertEquals("http://g", URIUtils.resolve(this.baseURI, "//g").toString());
}
public void testResolve06() {
Assert.assertEquals("http://a/b/c/d;p?y", URIUtils.resolve(this.baseURI, "?y").toString());
}
public void testResolve06_() {
Assert.assertEquals("http://a/b/c/d;p?y#f", URIUtils.resolve(this.baseURI, "?y#f")
.toString());
}
public void testResolve07() {
Assert.assertEquals("http://a/b/c/g?y", URIUtils.resolve(this.baseURI, "g?y").toString());
}
public void testResolve08() {
Assert
.assertEquals("http://a/b/c/d;p?q#s", URIUtils.resolve(this.baseURI, "#s")
.toString());
}
public void testResolve09() {
Assert.assertEquals("http://a/b/c/g#s", URIUtils.resolve(this.baseURI, "g#s").toString());
}
public void testResolve10() {
Assert.assertEquals("http://a/b/c/g?y#s", URIUtils.resolve(this.baseURI, "g?y#s")
.toString());
}
public void testResolve11() {
Assert.assertEquals("http://a/b/c/;x", URIUtils.resolve(this.baseURI, ";x").toString());
}
public void testResolve12() {
Assert.assertEquals("http://a/b/c/g;x", URIUtils.resolve(this.baseURI, "g;x").toString());
}
public void testResolve13() {
Assert.assertEquals("http://a/b/c/g;x?y#s", URIUtils.resolve(this.baseURI, "g;x?y#s")
.toString());
}
public void testResolve14() {
Assert.assertEquals("http://a/b/c/d;p?q", URIUtils.resolve(this.baseURI, "").toString());
}
public void testResolve15() {
Assert.assertEquals("http://a/b/c/", URIUtils.resolve(this.baseURI, ".").toString());
}
public void testResolve16() {
Assert.assertEquals("http://a/b/c/", URIUtils.resolve(this.baseURI, "./").toString());
}
public void testResolve17() {
Assert.assertEquals("http://a/b/", URIUtils.resolve(this.baseURI, "..").toString());
}
public void testResolve18() {
Assert.assertEquals("http://a/b/", URIUtils.resolve(this.baseURI, "../").toString());
}
public void testResolve19() {
Assert.assertEquals("http://a/b/g", URIUtils.resolve(this.baseURI, "../g").toString());
}
public void testResolve20() {
Assert.assertEquals("http://a/", URIUtils.resolve(this.baseURI, "../..").toString());
}
public void testResolve21() {
Assert.assertEquals("http://a/", URIUtils.resolve(this.baseURI, "../../").toString());
}
public void testResolve22() {
Assert.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "../../g").toString());
}
public void testResolveAbnormal23() {
Assert.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "../../../g").toString());
}
public void testResolveAbnormal24() {
Assert.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "../../../../g")
.toString());
}
public void testResolve25() {
Assert.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "/./g").toString());
}
public void testResolve26() {
Assert.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "/../g").toString());
}
public void testResolve27() {
Assert.assertEquals("http://a/b/c/g.", URIUtils.resolve(this.baseURI, "g.").toString());
}
public void testResolve28() {
Assert.assertEquals("http://a/b/c/.g", URIUtils.resolve(this.baseURI, ".g").toString());
}
public void testResolve29() {
Assert.assertEquals("http://a/b/c/g..", URIUtils.resolve(this.baseURI, "g..").toString());
}
public void testResolve30() {
Assert.assertEquals("http://a/b/c/..g", URIUtils.resolve(this.baseURI, "..g").toString());
}
public void testResolve31() {
Assert.assertEquals("http://a/b/g", URIUtils.resolve(this.baseURI, "./../g").toString());
}
public void testResolve32() {
Assert.assertEquals("http://a/b/c/g/", URIUtils.resolve(this.baseURI, "./g/.").toString());
}
public void testResolve33() {
Assert.assertEquals("http://a/b/c/g/h", URIUtils.resolve(this.baseURI, "g/./h").toString());
}
public void testResolve34() {
Assert.assertEquals("http://a/b/c/h", URIUtils.resolve(this.baseURI, "g/../h").toString());
}
public void testResolve35() {
Assert.assertEquals("http://a/b/c/g;x=1/y", URIUtils.resolve(this.baseURI, "g;x=1/./y")
.toString());
}
public void testResolve36() {
Assert.assertEquals("http://a/b/c/y", URIUtils.resolve(this.baseURI, "g;x=1/../y")
.toString());
}
public void testResolve37() {
Assert.assertEquals("http://a/b/c/g?y/./x", URIUtils.resolve(this.baseURI, "g?y/./x")
.toString());
}
public void testResolve38() {
Assert.assertEquals("http://a/b/c/g?y/../x", URIUtils.resolve(this.baseURI, "g?y/../x")
.toString());
}
public void testResolve39() {
Assert.assertEquals("http://a/b/c/g#s/./x", URIUtils.resolve(this.baseURI, "g#s/./x")
.toString());
}
public void testResolve40() {
Assert.assertEquals("http://a/b/c/g#s/../x", URIUtils.resolve(this.baseURI, "g#s/../x")
.toString());
}
public void testResolve41() {
Assert.assertEquals("http:g", URIUtils.resolve(this.baseURI, "http:g").toString());
}
// examples from section 5.2.4
public void testResolve42() {
Assert.assertEquals("http://s/a/g", URIUtils.resolve(this.baseURI,
"http://s/a/b/c/./../../g").toString());
}
public void testResolve43() {
Assert.assertEquals("http://s/mid/6", URIUtils.resolve(this.baseURI,
"http://s/mid/content=5/../6").toString());
}
}