add IRI implementation

This commit is contained in:
Jörg Prante 2022-11-01 18:40:36 +01:00
parent 99ba51369d
commit cdc4639347
21 changed files with 15531 additions and 225 deletions

View file

@ -1,5 +1,5 @@
group = org.xbib group = org.xbib
name = net name = net
version = 3.0.0 version = 3.0.1
org.gradle.warning.mode = ALL org.gradle.warning.mode = ALL

3
net-resource/NOTICE.txt Normal file
View file

@ -0,0 +1,3 @@
This IRI implementation is taken from Daniel Fuchs' writeup for java.net.IRI
http://cr.openjdk.java.net/%7Edfuchs/writeups/updating-uri/

View file

@ -0,0 +1,4 @@
module org.xbib.net.resource {
exports org.xbib.net.resource;
}

View file

@ -0,0 +1,287 @@
/*
* Copyright (c) 2004, 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.xbib.net.resource;
public class IPAddressUtil {
private final static int INADDR4SZ = 4;
private final static int INADDR16SZ = 16;
private final static int INT16SZ = 2;
/*
* Converts IPv4 address in its textual presentation form
* into its numeric binary form.
*
* @param src a String representing an IPv4 address in standard format
* @return a byte array representing the IPv4 numeric address
*/
@SuppressWarnings("fallthrough")
public static byte[] textToNumericFormatV4(String src)
{
byte[] res = new byte[INADDR4SZ];
long tmpValue = 0;
int currByte = 0;
int len = src.length();
if (len == 0 || len > 15) {
return null;
}
/*
* When only one part is given, the value is stored directly in
* the network address without any byte rearrangement.
*
* When a two part address is supplied, the last part is
* interpreted as a 24-bit quantity and placed in the right
* most three bytes of the network address. This makes the
* two part address format convenient for specifying Class A
* network addresses as net.host.
*
* When a three part address is specified, the last part is
* interpreted as a 16-bit quantity and placed in the right
* most two bytes of the network address. This makes the
* three part address format convenient for specifying
* Class B net- work addresses as 128.net.host.
*
* When four parts are specified, each is interpreted as a
* byte of data and assigned, from left to right, to the
* four bytes of an IPv4 address.
*
* We determine and parse the leading parts, if any, as single
* byte values in one pass directly into the resulting byte[],
* then the remainder is treated as a 8-to-32-bit entity and
* translated into the remaining bytes in the array.
*/
for (int i = 0; i < len; i++) {
char c = src.charAt(i);
if (c == '.') {
if (tmpValue < 0 || tmpValue > 0xff || currByte == 3) {
return null;
}
res[currByte++] = (byte) (tmpValue & 0xff);
tmpValue = 0;
} else {
int digit = Character.digit(c, 10);
if (digit < 0) {
return null;
}
tmpValue *= 10;
tmpValue += digit;
}
}
if (tmpValue < 0 || tmpValue >= (1L << ((4 - currByte) * 8))) {
return null;
}
switch (currByte) {
case 0:
res[0] = (byte) ((tmpValue >> 24) & 0xff);
case 1:
res[1] = (byte) ((tmpValue >> 16) & 0xff);
case 2:
res[2] = (byte) ((tmpValue >> 8) & 0xff);
case 3:
res[3] = (byte) ((tmpValue >> 0) & 0xff);
}
return res;
}
/*
* Convert IPv6 presentation level address to network order binary form.
* credit:
* Converted from C code from Solaris 8 (inet_pton)
*
* Any component of the string following a per-cent % is ignored.
*
* @param src a String representing an IPv6 address in textual format
* @return a byte array representing the IPv6 numeric address
*/
public static byte[] textToNumericFormatV6(String src)
{
// Shortest valid string is "::", hence at least 2 chars
if (src.length() < 2) {
return null;
}
int colonp;
char ch;
boolean saw_xdigit;
int val;
char[] srcb = src.toCharArray();
byte[] dst = new byte[INADDR16SZ];
int srcb_length = srcb.length;
int pc = src.indexOf ("%");
if (pc == srcb_length -1) {
return null;
}
if (pc != -1) {
srcb_length = pc;
}
colonp = -1;
int i = 0, j = 0;
/* Leading :: requires some special handling. */
if (srcb[i] == ':')
if (srcb[++i] != ':')
return null;
int curtok = i;
saw_xdigit = false;
val = 0;
while (i < srcb_length) {
ch = srcb[i++];
int chval = Character.digit(ch, 16);
if (chval != -1) {
val <<= 4;
val |= chval;
if (val > 0xffff)
return null;
saw_xdigit = true;
continue;
}
if (ch == ':') {
curtok = i;
if (!saw_xdigit) {
if (colonp != -1)
return null;
colonp = j;
continue;
} else if (i == srcb_length) {
return null;
}
if (j + INT16SZ > INADDR16SZ)
return null;
dst[j++] = (byte) ((val >> 8) & 0xff);
dst[j++] = (byte) (val & 0xff);
saw_xdigit = false;
val = 0;
continue;
}
if (ch == '.' && ((j + INADDR4SZ) <= INADDR16SZ)) {
String ia4 = src.substring(curtok, srcb_length);
/* check this IPv4 address has 3 dots, ie. A.B.C.D */
int dot_count = 0, index=0;
while ((index = ia4.indexOf ('.', index)) != -1) {
dot_count ++;
index ++;
}
if (dot_count != 3) {
return null;
}
byte[] v4addr = textToNumericFormatV4(ia4);
if (v4addr == null) {
return null;
}
for (int k = 0; k < INADDR4SZ; k++) {
dst[j++] = v4addr[k];
}
saw_xdigit = false;
break; /* '\0' was seen by inet_pton4(). */
}
return null;
}
if (saw_xdigit) {
if (j + INT16SZ > INADDR16SZ)
return null;
dst[j++] = (byte) ((val >> 8) & 0xff);
dst[j++] = (byte) (val & 0xff);
}
if (colonp != -1) {
int n = j - colonp;
if (j == INADDR16SZ)
return null;
for (i = 1; i <= n; i++) {
dst[INADDR16SZ - i] = dst[colonp + n - i];
dst[colonp + n - i] = 0;
}
j = INADDR16SZ;
}
if (j != INADDR16SZ)
return null;
byte[] newdst = convertFromIPv4MappedAddress(dst);
if (newdst != null) {
return newdst;
} else {
return dst;
}
}
/**
* @param src a String representing an IPv4 address in textual format
* @return a boolean indicating whether src is an IPv4 literal address
*/
public static boolean isIPv4LiteralAddress(String src) {
return textToNumericFormatV4(src) != null;
}
/**
* @param src a String representing an IPv6 address in textual format
* @return a boolean indicating whether src is an IPv6 literal address
*/
public static boolean isIPv6LiteralAddress(String src) {
return textToNumericFormatV6(src) != null;
}
/*
* Convert IPv4-Mapped address to IPv4 address. Both input and
* returned value are in network order binary form.
*
* @param src a String representing an IPv4-Mapped address in textual format
* @return a byte array representing the IPv4 numeric address
*/
public static byte[] convertFromIPv4MappedAddress(byte[] addr) {
if (isIPv4MappedAddress(addr)) {
byte[] newAddr = new byte[INADDR4SZ];
System.arraycopy(addr, 12, newAddr, 0, INADDR4SZ);
return newAddr;
}
return null;
}
/**
* Utility routine to check if the InetAddress is an
* IPv4 mapped IPv6 address.
*
* @return a <code>boolean</code> indicating if the InetAddress is
* an IPv4 mapped IPv6 address; or false if address is IPv4 address.
*/
private static boolean isIPv4MappedAddress(byte[] addr) {
if (addr.length < INADDR16SZ) {
return false;
}
if ((addr[0] == 0x00) && (addr[1] == 0x00) &&
(addr[2] == 0x00) && (addr[3] == 0x00) &&
(addr[4] == 0x00) && (addr[5] == 0x00) &&
(addr[6] == 0x00) && (addr[7] == 0x00) &&
(addr[8] == 0x00) && (addr[9] == 0x00) &&
(addr[10] == (byte)0xff) &&
(addr[11] == (byte)0xff)) {
return true;
}
return false;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,204 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.xbib.net.resource;
/**
* Represents an abstract Resource Identifier reference.
*
* <p> The particular conformance with any standards, parsing and construction
* behaviour, and operational aspects of the Resource Identifier are specified
* by a concrete subtype. This class does not have a public accessible
* constructor, therefore the only valid subtypes are those that are defined
* by the Java Platform. The only known subtypes are {@link URI} ( that conforms
* to the obsoleted <i>RFC 2396</i>) and {@link IRI} ( that conforms to the
* more recent <i>RFC 3986 / STD 66</i> and <i>RFC 3987</i> ).
*
* <p> The components of the Resource Identifier are retrievable through the
* methods of this class. Both the raw and decoded forms of the components are
* retrievable. The raw form of a component is the value of the component
* without any interpretation or conversation. The decoded form of a component
* is the value of the raw form after a single decoding pass that decodes UTF-8
* percent-encoded triplets. A concrete subtype will further define the specific
* set of allowable characters within each component.
*
*/
public abstract class ResourceIdentifier {
/* package-private */ ResourceIdentifier() { }
/**
* Tells whether or not this resource identifier is absolute.
*
* <p> A resource identifier is absolute if, and only if, it has a
* scheme component.
*
* @return {@code true} if, and only if, this resource identifier
* is absolute
*/
public abstract boolean isAbsolute();
/**
* Tells whether or not this resource identifier is considered opaque.
*
* @return {@code true} if, and only if, this resource identifier
* is considered opaque
*/
public abstract boolean isOpaque();
/**
* Returns the scheme component of this resource identifier.
*
* @return The scheme component of this resource identifier,
* or {@code null} if the scheme is undefined
*/
public abstract String getScheme();
/**
* Returns the raw authority component of this resource identifier.
*
* @return The raw authority component of this resource identifier,
* or {@code null} if the authority is undefined
*/
public abstract String getRawAuthority();
/**
* Returns the decoded authority component of this resource identifier.
*
* @return The decoded authority component of this resource identifier,
* or {@code null} if the authority is undefined
*/
public abstract String getAuthority();
/**
* Returns the raw user-information component of this resource identifier.
*
* @return The raw user-information component of this resource identifier,
* or {@code null} if the user information is undefined
*/
public abstract String getRawUserInfo();
/**
* Returns the decoded user-information component of this resource identifier.
*
* @return The decoded user-information component of this resource identifier,
* or {@code null} if the user information is undefined
*/
public abstract String getUserInfo();
/**
* Returns the host component of this resource identifier.
*
* @return The host component of this resource identifier,
* or {@code null} if the host is undefined, or does
* not parse as a syntactically valid internet name.
*/
public abstract String getHost();
/**
* Returns the port number of this resource identifier.
*
* @return The port component of this resource identifier,
* or {@code -1} if the port is undefined
*/
public abstract int getPort();
/**
* Returns the raw path component of this resource identifier.
*
* @apiNote
*
* Different subclasses may return {@code null} on different
* conditions. For instance {@code java.net.URI} will always
* return {@code null} if the URI is opaque, while {@code
* java.net.IRI} will simply return the opaque path.
*
* @return The path component of this resource identifier,
* or {@code null} if the path is undefined
*/
public abstract String getRawPath();
/**
* Returns the decoded path component of this resource identifier.
*
* @apiNote
*
* Different subclasses may return {@code null} on different
* conditions. For instance {@code java.net.URI} will always
* return {@code null} if the URI is opaque, while {@code
* java.net.IRI} will simply return the decoded opaque path.
*
* @return The decoded path component of this resource identifier,
* or {@code null} if the path is undefined
*/
public abstract String getPath();
/**
* Returns the raw query component of this resource identifier.
*
* @return The raw query component of this resource identifier,
* or {@code null} if the query is undefined
*/
public abstract String getRawQuery();
/**
* Returns the decoded query component of this resource identifier.
*
* @apiNote
*
* Different subclasses may return {@code null} on different
* conditions. For instance {@code java.net.URI} will always
* return {@code null} if the URI is opaque.
*
* @return The decoded query component of this resource identifier,
* or {@code null} if the query is undefined
*/
public abstract String getQuery();
/**
* Returns the raw fragment component of this resource identifier.
*
* @return The raw fragment component of this resource identifier,
* or {@code null} if the fragment is undefined
*/
public abstract String getRawFragment();
/**
* Returns the decoded fragment component of this resource identifier.
*
* @return The decoded fragment component of this URresource identifierI,
* or {@code null} if the fragment is undefined
*/
public abstract String getFragment();
/**
* Returns the content of this resource identifier as a US-ASCII string.
*
* @return The string form of this resource identifier, encoded as needed
* so that it only contains characters in the US-ASCII charset
*/
public abstract String toASCIIString();
}

View file

@ -0,0 +1,136 @@
/*
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.xbib.net.resource;
import java.nio.charset.*;
/**
* Utility class for caching per-thread decoders and encoders.
*/
public class ThreadLocalCoders {
private static final int CACHE_SIZE = 3;
private static abstract class Cache {
// Thread-local reference to array of cached objects, in LRU order
private final ThreadLocal<Object[]> cache = new ThreadLocal<>();
private final int size;
Cache(int size) {
this.size = size;
}
abstract Object create(Object name);
private void moveToFront(Object[] oa, int i) {
Object ob = oa[i];
for (int j = i; j > 0; j--)
oa[j] = oa[j - 1];
oa[0] = ob;
}
abstract boolean hasName(Object ob, Object name);
Object forName(Object name) {
Object[] oa = cache.get();
if (oa == null) {
oa = new Object[size];
cache.set(oa);
} else {
for (int i = 0; i < oa.length; i++) {
Object ob = oa[i];
if (ob == null)
continue;
if (hasName(ob, name)) {
if (i > 0)
moveToFront(oa, i);
return ob;
}
}
}
// Create a new object
Object ob = create(name);
oa[oa.length - 1] = ob;
moveToFront(oa, oa.length - 1);
return ob;
}
}
private static Cache decoderCache = new Cache(CACHE_SIZE) {
boolean hasName(Object ob, Object name) {
if (name instanceof String)
return (((CharsetDecoder)ob).charset().name().equals(name));
if (name instanceof Charset)
return ((CharsetDecoder)ob).charset().equals(name);
return false;
}
Object create(Object name) {
if (name instanceof String)
return Charset.forName((String)name).newDecoder();
if (name instanceof Charset)
return ((Charset)name).newDecoder();
assert false;
return null;
}
};
public static CharsetDecoder decoderFor(Object name) {
CharsetDecoder cd = (CharsetDecoder)decoderCache.forName(name);
cd.reset();
return cd;
}
private static Cache encoderCache = new Cache(CACHE_SIZE) {
boolean hasName(Object ob, Object name) {
if (name instanceof String)
return (((CharsetEncoder)ob).charset().name().equals(name));
if (name instanceof Charset)
return ((CharsetEncoder)ob).charset().equals(name);
return false;
}
Object create(Object name) {
if (name instanceof String)
return Charset.forName((String)name).newEncoder();
if (name instanceof Charset)
return ((Charset)name).newEncoder();
assert false;
return null;
}
};
public static CharsetEncoder encoderFor(Object name) {
CharsetEncoder ce = (CharsetEncoder)encoderCache.forName(name);
ce.reset();
return ce;
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,186 @@
package org.xbib.net.resource;/*
* Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 4768755 4677045 8147462
* @summary URL.equal(URL) is inconsistent for opaque IRI.toURL()
* and new URL(IRI.toString)
* IRI.toURL() does not always work as specified
* Ensure URIs representing invalid/malformed URLs throw similar
* exception with new URL(IRI.toString()) and IRI.toURL()
*/
import java.net.MalformedURLException;
import java.net.URL;
public class IRItoURLTest {
public static void main(String args[]) throws Exception {
URL classUrl = new URL("jrt:/java.base/java/lang/Object.class");
String[] uris = {
"mailto:xyz@abc.de",
"file:xyz#ab",
"http:abc/xyz/pqr",
"http:abc/xyz/pqr?id=x%0a&ca=true",
"file:/C:/v700/dev/unitTesting/tests/apiUtil/uri",
"http:///p",
"file:/C:/v700/dev/unitTesting/tests/apiUtil/uri",
"file:/C:/v700/dev%20src/unitTesting/tests/apiUtil/uri",
"file:/C:/v700/dev%20src/./unitTesting/./tests/apiUtil/uri",
"http://localhost:80/abc/./xyz/../pqr?id=x%0a&ca=true",
"file:./test/./x",
"file:./././%20#i=3",
"file:?hmm",
"file:.#hmm",
classUrl.toExternalForm(),
};
// Strings that represent valid URIs but invalid URLs that should throw
// MalformedURLException both when calling toURL and new URL(String)
String[] malformedUrls = {
"test:/test",
"fiel:test",
};
// Non-absolute URIs should throw IAE when calling toURL but will throw
// MalformedURLException when calling new URL
String[] illegalUris = {
"./test",
"/test",
};
boolean isTestFailed = false;
boolean isURLFailed = false;
for (String uriString : uris) {
IRI uri = IRI.of(uriString);
URL url1 = new URL(uri.toString());
URL url2 = uri.toURL();
System.out.println("Testing URI " + uri);
if (!url1.equals(url2)) {
System.out.println("equals() FAILED");
isURLFailed = true;
}
if (url1.hashCode() != url2.hashCode()) {
System.out.println("hashCode() DIDN'T MATCH");
isURLFailed = true;
}
if (!url1.sameFile(url2)) {
System.out.println("sameFile() FAILED");
isURLFailed = true;
}
if (!equalsComponents("getPath()", url1.getPath(),
url2.getPath())) {
isURLFailed = true;
}
if (!equalsComponents("getFile()", url1.getFile(),
url2.getFile())) {
isURLFailed = true;
}
if (!equalsComponents("getHost()", url1.getHost(),
url2.getHost())) {
isURLFailed = true;
}
if (!equalsComponents("getAuthority()",
url1.getAuthority(), url2.getAuthority())) {
isURLFailed = true;
}
if (!equalsComponents("getRef()", url1.getRef(),
url2.getRef())) {
isURLFailed = true;
}
if (!equalsComponents("getUserInfo()", url1.getUserInfo(),
url2.getUserInfo())) {
isURLFailed = true;
}
if (!equalsComponents("toString()", url1.toString(),
url2.toString())) {
isURLFailed = true;
}
if (isURLFailed) {
isTestFailed = true;
} else {
System.out.println("PASSED ..");
}
System.out.println();
isURLFailed = false;
}
for (String malformedUrl : malformedUrls) {
Exception toURLEx = null;
Exception newURLEx = null;
try {
IRI.parseIRI(malformedUrl).toURL();
} catch (Exception e) {
// expected
toURLEx = e;
}
try {
new URL(IRI.parseIRI(malformedUrl).toString());
} catch (Exception e) {
// expected
newURLEx = e;
}
if (!(toURLEx instanceof MalformedURLException) ||
!(newURLEx instanceof MalformedURLException) ||
!toURLEx.getMessage().equals(newURLEx.getMessage())) {
isTestFailed = true;
System.out.println("Expected the same MalformedURLException: " +
newURLEx + " vs " + toURLEx);
}
}
for (String illegalUri : illegalUris) {
try {
IRI.parseIRI(illegalUri).toURL();
} catch (IllegalArgumentException e) {
// pass
}
try {
new URL(illegalUri);
} catch (MalformedURLException e) {
// pass
}
}
if (isTestFailed) {
throw new Exception("URI.toURL() test failed");
}
}
static boolean equalsComponents(String method, String comp1, String comp2) {
if ((comp1 != null) && (!comp1.equals(comp2))) {
System.out.println(method + " DIDN'T MATCH" +
" ===>");
System.out.println(" URL(URI.toString()) returns:" + comp1);
System.out.println(" URI.toURL() returns:" + comp2);
return false;
}
return true;
}
}

View file

@ -0,0 +1,61 @@
package org.xbib.net.resource;
/*
* Copyright (c) 2003, 2019 Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 4866303
* @summary URI.resolve escapes characters in parameter URI
*/
import java.io.File;
import java.net.URISyntaxException;
import java.util.Objects;
public class RelativeEncoding {
public static void main(String[] args) {
try {
IRI one = IRI.parseIRI("Relative%20with%20spaces");
// TODO: add a File.toIRI() method?
IRI two = IRI.parseIRI((new File("/tmp/dir with spaces/File with spaces")).toURI().toString());
IRI three = two.resolve(one);
if (three.isOpaque())
throw new RuntimeException("Bad encoding on IRI.resolve: should not be opaque");
System.out.println(String.format("resolved path is: \"%s\" [\"%s\"]",
three.getPath(), three.getRawPath()));
checkEquals("/tmp/dir with spaces/Relative with spaces",
three.getPath(), "path");
checkEquals("/tmp/dir%20with%20spaces/Relative%20with%20spaces",
three.getRawPath(), "raw path");
} catch (URISyntaxException e) {
throw new RuntimeException("Unexpected exception: " + e);
}
}
static void checkEquals(String expected, String found, String name) {
if (!Objects.equals(expected, found)) {
throw new RuntimeException(
String.format("Unexpected %s: \"%s\"\n\t expected \"%s\"",
name, found, expected));
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,8 +0,0 @@
package org.xbib.net;
public interface Context<Req, Resp> {
Req request();
Resp response();
}

View file

@ -1,10 +0,0 @@
package org.xbib.net;
import java.io.IOException;
@SuppressWarnings("rawtypes")
@FunctionalInterface
public interface Handler<C extends Context> {
void handle(C context) throws IOException;
}

View file

@ -1,21 +0,0 @@
package org.xbib.net;
@SuppressWarnings("serial")
public class HandlerException extends RuntimeException {
public HandlerException() {
super();
}
public HandlerException(String message) {
super(message);
}
public HandlerException(Exception e) {
super(e);
}
public HandlerException(String message, Exception e) {
super(message, e);
}
}

View file

@ -8,6 +8,7 @@ import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.xbib.net.scheme.Scheme; import org.xbib.net.scheme.Scheme;
import org.xbib.net.scheme.SchemeRegistry; import org.xbib.net.scheme.SchemeRegistry;
import org.xbib.net.util.CharUtils; import org.xbib.net.util.CharUtils;

View file

@ -38,10 +38,7 @@ import java.util.Objects;
* The reason for the name {@code URL} is merely because of the popularity of the name, which * The reason for the name {@code URL} is merely because of the popularity of the name, which
* overweighs the URI or IRI popularity. * overweighs the URI or IRI popularity.
* *
* [source,java]
* --
* URL url = URL.http().resolveFromHost("google.com").build(); * URL url = URL.http().resolveFromHost("google.com").build();
* --
* *
*/ */
public class URL implements Comparable<URL> { public class URL implements Comparable<URL> {

View file

@ -1,176 +0,0 @@
package org.xbib.net;
import org.junit.jupiter.api.Test;
import java.text.Normalizer;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
class IRITest {
@Test
void testIpv4() {
URL iri = URL.create("http://127.0.0.1");
assertEquals("http://127.0.0.1", iri.toExternalForm());
}
@Test
void testIpv6() {
URL iri = URL.from("http://[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]");
assertEquals(iri.getProtocolVersion(), ProtocolVersion.IPV6);
assertEquals("http://[2001:db8:85a3:8d3:1319:8a2e:370:7344]", iri.toString());
}
@Test
void testIpv6Invalid() {
URL iri = URL.from("http://[2001:0db8:85a3:08d3:1319:8a2e:0370:734o]");
assertEquals(URL.nullUrl(), iri);
}
@Test
void testSimple() {
URL iri = URL.create("http://validator.w3.org/check?uri=http%3A%2F%2Fr\u00E9sum\u00E9.example.org");
assertEquals("http://validator.w3.org/check?uri=http%3A%2F%2Fr\u00E9sum\u00E9.example.org", iri.toString());
}
@Test
void testFile() throws Exception {
URL iri = URL.create("file:///tmp/test/foo");
assertEquals("", iri.getHost());
assertEquals("/tmp/test/foo", iri.getPath());
assertEquals("file:///tmp/test/foo", iri.toExternalForm());
assertEquals("file:///tmp/test/foo", iri.toString());
}
@Test
void testSimple2() throws Exception {
URL iri = URL.create("http://www.example.org/red%09ros\u00E9#red");
assertEquals("http://www.example.org/red%09ros%C3%A9#red", iri.toExternalForm());
}
@Test
void testNotSoSimple() throws Exception {
URL iri = URL.create("http://example.com/\uD800\uDF00\uD800\uDF01\uD800\uDF02");
assertEquals("http://example.com/%F0%90%8C%80%F0%90%8C%81%F0%90%8C%82", iri.toExternalForm());
}
@Test
void testIRItoURI() throws Exception {
URL iri = URL.from("http://\u7D0D\u8C46.example.org/%E2%80%AE");
assertEquals("http://xn--99zt52a.example.org/%E2%80%AE", iri.toExternalForm());
}
@Test
void testComparison() {
URL url1 = URL.create("http://www.example.org/");
URL url2 = URL.create("http://www.example.org/..");
URL url3 = URL.create("http://www.Example.org:80");
assertNotEquals(url1, url2);
assertNotEquals(url1, url3);
assertNotEquals(url2, url1);
assertNotEquals(url2, url3);
assertNotEquals(url3, url1);
assertNotEquals(url3, url2);
assertEquals(url1.normalize(), url2.normalize());
assertEquals(url1.normalize(), url3.normalize());
assertEquals(url2.normalize(), url1.normalize());
assertEquals(url2.normalize(), url3.normalize());
assertEquals(url3.normalize(), url1.normalize());
assertEquals(url3.normalize(), url2.normalize());
}
@Test
void testUCN() {
URL iri1 = URL.create("http://www.example.org/r\u00E9sum\u00E9.html");
String s = Normalizer.normalize("http://www.example.org/re\u0301sume\u0301.html", Normalizer.Form.NFC);
URL iri2 = URL.create(s);
assertEquals(iri2, iri1);
}
@Test
void testNormalizePath() {
URL iri1 = URL.create("http://example.org/%7e%2Fuser%2f");
URL iri2 = URL.create("http://example.org/%7E%2fuser/");
assertEquals(iri1.normalize(), iri2.normalize());
}
@Test
void testIDN() {
URL iri1 = URL.from("http://r\u00E9sum\u00E9.example.org");
assertEquals("xn--rsum-bpad.example.org", iri1.getHost());
}
@Test
void testResolveRelative() {
URL base = URL.create("http://example.org/foo/");
assertEquals("http://example.org/", base.resolve("/").toString());
assertEquals("http://example.org/test", base.resolve("/test").toString());
assertEquals("http://example.org/foo/test", base.resolve("test").toString());
assertEquals("http://example.org/test", base.resolve("../test").toString());
assertEquals("http://example.org/foo/test", base.resolve("./test").toString());
assertEquals("http://example.org/foo/", base.resolve("test/test/../../").toString());
assertEquals("http://example.org/foo/?test", base.resolve("?test").toString());
assertEquals("http://example.org/foo/#test", base.resolve("#test").toString());
assertEquals("http://example.org/foo/", base.resolve(".").toString());
}
@Test
void testSchemes() {
URL iri = URL.create("http://a:b@c.org:80/d/e?f#g");
assertEquals("http", iri.getScheme());
assertEquals("a:b", iri.getUserInfo());
assertEquals("c.org", iri.getHost());
assertEquals(Integer.valueOf(80), iri.getPort());
assertEquals("/d/e", iri.getPath());
assertEquals("f", iri.getQuery());
assertEquals("g", iri.getFragment());
iri = URL.create("https://a:b@c.org:80/d/e?f#g");
assertEquals("https", iri.getScheme());
assertEquals("a:b", iri.getUserInfo());
assertEquals("c.org", iri.getHost());
assertEquals(Integer.valueOf(80), iri.getPort());
assertEquals("/d/e", iri.getPath());
assertEquals("f", iri.getQuery());
assertEquals("g", iri.getFragment());
iri = URL.create("ftp://a:b@c.org:80/d/e?f#g");
assertEquals("ftp", iri.getScheme());
assertEquals("a:b", iri.getUserInfo());
assertEquals("c.org", iri.getHost());
assertEquals(Integer.valueOf(80), iri.getPort());
assertEquals("/d/e", iri.getPath());
assertEquals("f", iri.getQuery());
assertEquals("g", iri.getFragment());
iri = URL.create("mailto:joe@example.org?subject=foo");
assertEquals("mailto", iri.getScheme());
assertEquals(null, iri.getUserInfo());
assertEquals(null, iri.getHost());
assertEquals(null, iri.getPort());
assertEquals("joe@example.org?subject=foo", iri.getSchemeSpecificPart());
assertEquals(null, iri.getFragment());
iri = URL.create("tag:example.org,2006:foo");
assertEquals("tag", iri.getScheme());
assertEquals(null, iri.getUserInfo());
assertEquals(null, iri.getHost());
assertEquals(null, iri.getPort());
assertEquals("example.org,2006:foo", iri.getSchemeSpecificPart());
assertEquals(null, iri.getQuery());
assertEquals(null, iri.getFragment());
iri = URL.create("urn:lsid:ibm.com:example:82437234964354895798234d");
assertEquals("urn", iri.getScheme());
assertEquals(null, iri.getUserInfo());
assertEquals(null, iri.getHost());
assertEquals(null, iri.getPort());
assertEquals("lsid:ibm.com:example:82437234964354895798234d", iri.getSchemeSpecificPart());
assertEquals(null, iri.getQuery());
assertEquals(null, iri.getFragment());
iri = URL.create("data:image/gif;base64,R0lGODdhMAAwAPAAAAAAAP");
assertEquals("data", iri.getScheme());
assertEquals(null, iri.getUserInfo());
assertEquals(null, iri.getHost());
assertEquals(null, iri.getPort());
assertEquals("image/gif;base64,R0lGODdhMAAwAPAAAAAAAP", iri.getSchemeSpecificPart());
assertEquals(null, iri.getQuery());
assertEquals(null, iri.getFragment());
}
}

View file

@ -6,10 +6,12 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectReader;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.text.Normalizer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
class URLTest { class URLTest {
@ -116,4 +118,168 @@ class URLTest {
boolean skip; boolean skip;
} }
@Test
void testIpv4() {
URL iri = URL.create("http://127.0.0.1");
assertEquals("http://127.0.0.1", iri.toExternalForm());
}
@Test
void testIpv6() {
URL iri = URL.from("http://[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]");
assertEquals(iri.getProtocolVersion(), ProtocolVersion.IPV6);
assertEquals("http://[2001:db8:85a3:8d3:1319:8a2e:370:7344]", iri.toString());
}
@Test
void testIpv6Invalid() {
URL iri = URL.from("http://[2001:0db8:85a3:08d3:1319:8a2e:0370:734o]");
assertEquals(URL.nullUrl(), iri);
}
@Test
void testSimple() {
URL iri = URL.create("http://validator.w3.org/check?uri=http%3A%2F%2Fr\u00E9sum\u00E9.example.org");
assertEquals("http://validator.w3.org/check?uri=http%3A%2F%2Fr\u00E9sum\u00E9.example.org", iri.toString());
}
@Test
void testFile() throws Exception {
URL iri = URL.create("file:///tmp/test/foo");
assertEquals("", iri.getHost());
assertEquals("/tmp/test/foo", iri.getPath());
assertEquals("file:///tmp/test/foo", iri.toExternalForm());
assertEquals("file:///tmp/test/foo", iri.toString());
}
@Test
void testSimple2() throws Exception {
URL iri = URL.create("http://www.example.org/red%09ros\u00E9#red");
assertEquals("http://www.example.org/red%09ros%C3%A9#red", iri.toExternalForm());
}
@Test
void testNotSoSimple() throws Exception {
URL iri = URL.create("http://example.com/\uD800\uDF00\uD800\uDF01\uD800\uDF02");
assertEquals("http://example.com/%F0%90%8C%80%F0%90%8C%81%F0%90%8C%82", iri.toExternalForm());
}
@Test
void testIRItoURI() throws Exception {
URL iri = URL.from("http://\u7D0D\u8C46.example.org/%E2%80%AE");
assertEquals("http://xn--99zt52a.example.org/%E2%80%AE", iri.toExternalForm());
}
@Test
void testComparison() {
URL url1 = URL.create("http://www.example.org/");
URL url2 = URL.create("http://www.example.org/..");
URL url3 = URL.create("http://www.Example.org:80");
assertNotEquals(url1, url2);
assertNotEquals(url1, url3);
assertNotEquals(url2, url1);
assertNotEquals(url2, url3);
assertNotEquals(url3, url1);
assertNotEquals(url3, url2);
assertEquals(url1.normalize(), url2.normalize());
assertEquals(url1.normalize(), url3.normalize());
assertEquals(url2.normalize(), url1.normalize());
assertEquals(url2.normalize(), url3.normalize());
assertEquals(url3.normalize(), url1.normalize());
assertEquals(url3.normalize(), url2.normalize());
}
@Test
void testUCN() {
URL iri1 = URL.create("http://www.example.org/r\u00E9sum\u00E9.html");
String s = Normalizer.normalize("http://www.example.org/re\u0301sume\u0301.html", Normalizer.Form.NFC);
URL iri2 = URL.create(s);
assertEquals(iri2, iri1);
}
@Test
void testNormalizePath() {
URL iri1 = URL.create("http://example.org/%7e%2Fuser%2f");
URL iri2 = URL.create("http://example.org/%7E%2fuser/");
assertEquals(iri1.normalize(), iri2.normalize());
}
@Test
void testIDN() {
URL iri1 = URL.from("http://r\u00E9sum\u00E9.example.org");
assertEquals("xn--rsum-bpad.example.org", iri1.getHost());
}
@Test
void testResolveRelative() {
URL base = URL.create("http://example.org/foo/");
assertEquals("http://example.org/", base.resolve("/").toString());
assertEquals("http://example.org/test", base.resolve("/test").toString());
assertEquals("http://example.org/foo/test", base.resolve("test").toString());
assertEquals("http://example.org/test", base.resolve("../test").toString());
assertEquals("http://example.org/foo/test", base.resolve("./test").toString());
assertEquals("http://example.org/foo/", base.resolve("test/test/../../").toString());
assertEquals("http://example.org/foo/?test", base.resolve("?test").toString());
assertEquals("http://example.org/foo/#test", base.resolve("#test").toString());
assertEquals("http://example.org/foo/", base.resolve(".").toString());
}
@Test
void testSchemes() {
URL iri = URL.create("http://a:b@c.org:80/d/e?f#g");
assertEquals("http", iri.getScheme());
assertEquals("a:b", iri.getUserInfo());
assertEquals("c.org", iri.getHost());
assertEquals(Integer.valueOf(80), iri.getPort());
assertEquals("/d/e", iri.getPath());
assertEquals("f", iri.getQuery());
assertEquals("g", iri.getFragment());
iri = URL.create("https://a:b@c.org:80/d/e?f#g");
assertEquals("https", iri.getScheme());
assertEquals("a:b", iri.getUserInfo());
assertEquals("c.org", iri.getHost());
assertEquals(Integer.valueOf(80), iri.getPort());
assertEquals("/d/e", iri.getPath());
assertEquals("f", iri.getQuery());
assertEquals("g", iri.getFragment());
iri = URL.create("ftp://a:b@c.org:80/d/e?f#g");
assertEquals("ftp", iri.getScheme());
assertEquals("a:b", iri.getUserInfo());
assertEquals("c.org", iri.getHost());
assertEquals(Integer.valueOf(80), iri.getPort());
assertEquals("/d/e", iri.getPath());
assertEquals("f", iri.getQuery());
assertEquals("g", iri.getFragment());
iri = URL.create("mailto:joe@example.org?subject=foo");
assertEquals("mailto", iri.getScheme());
assertEquals(null, iri.getUserInfo());
assertEquals(null, iri.getHost());
assertEquals(null, iri.getPort());
assertEquals("joe@example.org?subject=foo", iri.getSchemeSpecificPart());
assertEquals(null, iri.getFragment());
iri = URL.create("tag:example.org,2006:foo");
assertEquals("tag", iri.getScheme());
assertEquals(null, iri.getUserInfo());
assertEquals(null, iri.getHost());
assertEquals(null, iri.getPort());
assertEquals("example.org,2006:foo", iri.getSchemeSpecificPart());
assertEquals(null, iri.getQuery());
assertEquals(null, iri.getFragment());
iri = URL.create("urn:lsid:ibm.com:example:82437234964354895798234d");
assertEquals("urn", iri.getScheme());
assertEquals(null, iri.getUserInfo());
assertEquals(null, iri.getHost());
assertEquals(null, iri.getPort());
assertEquals("lsid:ibm.com:example:82437234964354895798234d", iri.getSchemeSpecificPart());
assertEquals(null, iri.getQuery());
assertEquals(null, iri.getFragment());
iri = URL.create("data:image/gif;base64,R0lGODdhMAAwAPAAAAAAAP");
assertEquals("data", iri.getScheme());
assertEquals(null, iri.getUserInfo());
assertEquals(null, iri.getHost());
assertEquals(null, iri.getPort());
assertEquals("image/gif;base64,R0lGODdhMAAwAPAAAAAAAP", iri.getSchemeSpecificPart());
assertEquals(null, iri.getQuery());
assertEquals(null, iri.getFragment());
}
} }

View file

@ -1,14 +1,16 @@
package org.xbib.net; package org.xbib.net.resource;
import org.junit.jupiter.api.Test;
import org.xbib.net.IRI;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
@Disabled class IRITest {
public class OtherIRITest {
@Test @Test
public void testSimple() throws Exception { public void testSimple() throws Exception {
@ -199,4 +201,3 @@ public class OtherIRITest {
} }
} }

View file

@ -23,6 +23,7 @@ include 'net'
include 'net-bouncycastle' include 'net-bouncycastle'
include 'net-mime' include 'net-mime'
include 'net-path' include 'net-path'
include 'net-resource'
include 'net-security' include 'net-security'
include 'net-socket' include 'net-socket'
include 'benchmark' include 'benchmark'