From c5d504ba65b79a57a5f4dd041e73493bf0a74b50 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Fri, 23 Jan 2026 14:14:20 +0100 Subject: [PATCH 1/2] init --- org.restlet.gwt/org.restlet.gwt/pom.xml | 1 - org.restlet.java/org.restlet.test/pom.xml | 2 - .../restlet/test/data/ReferenceTestCase.java | 2 +- org.restlet.java/org.restlet/pom.xml | 6 +- .../src/main/java/org/restlet/Request.java | 6 +- .../main/java/org/restlet/data/Reference.java | 262 ++++++++++++------ .../java/org/restlet/data/ReferenceTest.java | 41 +++ pom.xml | 22 +- 8 files changed, 233 insertions(+), 109 deletions(-) create mode 100644 org.restlet.java/org.restlet/src/test/java/org/restlet/data/ReferenceTest.java diff --git a/org.restlet.gwt/org.restlet.gwt/pom.xml b/org.restlet.gwt/org.restlet.gwt/pom.xml index 6a94bead49..514c298e7d 100644 --- a/org.restlet.gwt/org.restlet.gwt/pom.xml +++ b/org.restlet.gwt/org.restlet.gwt/pom.xml @@ -28,7 +28,6 @@ org.junit.jupiter junit-jupiter-api - ${lib-junit-version} test diff --git a/org.restlet.java/org.restlet.test/pom.xml b/org.restlet.java/org.restlet.test/pom.xml index 9984783f9a..559bb51e16 100644 --- a/org.restlet.java/org.restlet.test/pom.xml +++ b/org.restlet.java/org.restlet.test/pom.xml @@ -170,12 +170,10 @@ org.junit.jupiter junit-jupiter-api - ${lib-junit-version} org.junit.jupiter junit-jupiter-params - ${lib-junit-version} compile diff --git a/org.restlet.java/org.restlet.test/src/main/java/org/restlet/test/data/ReferenceTestCase.java b/org.restlet.java/org.restlet.test/src/main/java/org/restlet/test/data/ReferenceTestCase.java index 7aad74d23b..8b99446e9f 100644 --- a/org.restlet.java/org.restlet.test/src/main/java/org/restlet/test/data/ReferenceTestCase.java +++ b/org.restlet.java/org.restlet.test/src/main/java/org/restlet/test/data/ReferenceTestCase.java @@ -40,7 +40,7 @@ public class ReferenceTestCase extends RestletTestCase { protected final static String DEFAULT_SCHEMEPART = "//"; /** - * Returns a reference that is initialized with http://restlet.org. + * Returns a reference initialized with http://restlet.org. * * @return Reference instance. */ diff --git a/org.restlet.java/org.restlet/pom.xml b/org.restlet.java/org.restlet/pom.xml index 981680009f..604b518820 100644 --- a/org.restlet.java/org.restlet/pom.xml +++ b/org.restlet.java/org.restlet/pom.xml @@ -25,7 +25,11 @@ org.junit.jupiter junit-jupiter-api - ${lib-junit-version} + test + + + org.junit.jupiter + junit-jupiter-params test diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/Request.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/Request.java index 09d072b7cf..9c1ab14387 100644 --- a/org.restlet.java/org.restlet/src/main/java/org/restlet/Request.java +++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/Request.java @@ -753,7 +753,7 @@ public void setHostRef(Reference hostRef) { } /** - * Sets the host reference using an URI string. Note that when used with HTTP + * Sets the host reference using a URI string. Note that when used with HTTP * connectors, this property maps to the "Host" header. * * @param hostUri The host URI. @@ -866,7 +866,7 @@ public void setReferrerRef(Reference referrerRef) { } /** - * Sets the referrer reference if available using an URI string. Note that when + * Sets the referrer reference if available using a URI string. Note that when * used with HTTP connectors, this property maps to the "Referer" header. * * @param referrerUri The referrer URI. @@ -890,7 +890,7 @@ public void setResourceRef(Reference resourceRef) { } /** - * Sets the target resource reference using an URI string. Note that the URI can + * Sets the target resource reference using a URI string. Note that the URI can * be either absolute or relative to the context's base reference. * * @param resourceUri The resource URI. diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/data/Reference.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/data/Reference.java index d1d0132c28..498df4cafd 100644 --- a/org.restlet.java/org.restlet/src/main/java/org/restlet/data/Reference.java +++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/data/Reference.java @@ -19,7 +19,7 @@ /** * Reference to a Uniform Resource Identifier (URI). Contrary to the * java.net.URI class, this interface represents mutable references. It strictly - * conforms to the RFC 3986 specifying URIs and follow its naming + * conforms to the RFC 3986 specifying URIs and follows its naming * conventions.
* *
@@ -49,17 +49,17 @@
  * the character will be automatically encoded.
  * 

*

- * The fundamental point to underline is the difference between an URI - * "reference" and an URI. Contrary to an URI (the target identifier of a REST - * resource), an URI reference can be relative (with or without query and + * The fundamental point to underline is the difference between a URI + * "reference" and a URI. Contrary to a URI (the target identifier of a REST + * resource), a URI reference can be relative (with or without a query and * fragment part). This relative URI reference can then be resolved against a * base reference via the getTargetRef() method which will return a new resolved * Reference instance, an absolute URI reference with no base reference and with * no dot-segments (the path segments "." and ".."). *

*

- * You can also apply the getTargetRef() method on absolute references in order - * to solve the dot-segments. Note that applying the getRelativeRef() method on + * You can also apply the getTargetRef() method on absolute references to solve + * the dot-segments. Note that applying the getRelativeRef() method on * an absolute reference returns the current reference relatively to a base * reference, if any, and solves the dot-segments. *

@@ -71,10 +71,10 @@ *

*

* When you modify a specific component of the URI reference, via the setPath() - * method for example, the internal string is simply regenerated by updating + * method, for example, the internal string is simply regenerated by updating * only the relevant part. We try as much as possible to protect the bytes given * to the Reference class instead of transparently parsing and normalizing the - * URI data. Our idea is to protect encodings and special characters in all case + * URI data. Our idea is to protect encodings and special characters in all cases * and reduce the memory size taken by this class while making Reference * instances mutable. *

@@ -87,11 +87,11 @@ *

*

* The base ref is not automatically resolved or "merged" with the rest of the - * reference information (the path here). For example, this let's you reuse a + * reference information (the path here). For example, this lets you reuse a * single reference as the base of several relative references. If you modify * the base reference, all relative references are still accurate. *

- * Note that the name and value properties are thread safe, stored in volatile + * Note that the name and value properties are thread-safe, stored in volatile * members. * * @author Jerome Louvel @@ -139,7 +139,7 @@ public static String decode(String toDecode, CharacterSet characterSet) { try { result = (characterSet == null) ? toDecode : java.net.URLDecoder.decode(toDecode, characterSet.getName()); } catch (UnsupportedEncodingException uee) { - Context.getCurrentLogger().log(Level.WARNING, "Unable to decode the string with the UTF-8 character set.", + Context.getCurrentLogger().log(Level.WARNING, "Unable to decode the string with the " + characterSet.getName() + " character set.", uee); } @@ -160,11 +160,11 @@ public static String encode(String toEncode) { /** * Encodes a given string using the standard URI encoding mechanism and the * UTF-8 character set. Useful to prevent the usage of '+' to encode spaces (%20 - * instead). The '*' characters are encoded as %2A and %7E are replaced by '~'. + * instead). The '*' characters are encoded as '%2A', and '%7E' are replaced by '~'. * * @param toEncode The string to encode. * @param queryString True if the string to encode is part of a query string - * instead of a HTML form post. + * instead of an HTML form post. * @return The encoded string. */ public static String encode(String toEncode, boolean queryString) { @@ -174,11 +174,11 @@ public static String encode(String toEncode, boolean queryString) { /** * Encodes a given string using the standard URI encoding mechanism and the * UTF-8 character set. Useful to prevent the usage of '+' to encode spaces (%20 - * instead). The '*' characters are encoded as %2A and %7E are replaced by '~'. + * instead). The '*' characters are encoded as '%2A', and '%7E' are replaced by '~'. * * @param toEncode The string to encode. * @param queryString True if the string to encode is part of a query string - * instead of a HTML form post. + * instead of an HTML form post. * @param characterSet The supported character encoding. * @return The encoded string. */ @@ -272,10 +272,10 @@ public static boolean isReserved(int character) { } /** - * Indicates if the given character is an URI subcomponent delimiter character. + * Indicates if the given character is a URI subcomponent delimiter character. * * @param character The character to test. - * @return True if the given character is an URI subcomponent delimiter + * @return True if the given character is a URI subcomponent delimiter * character. */ public static boolean isSubDelimiter(int character) { @@ -491,7 +491,7 @@ public Reference(Reference ref) { } /** - * Constructor from an URI reference (most likely relative). + * Constructor from a URI reference (most likely relative). * * @param baseRef The base reference. * @param uriReference The URI reference, either absolute or relative. @@ -508,11 +508,93 @@ public Reference(Reference baseRef, Reference uriReference) { */ public Reference(Reference baseRef, String uriRef) { uriRef = encodeInvalidCharacters(uriRef); + validateInternalRef(uriRef); this.baseRef = baseRef; this.internalRef = uriRef; updateIndexes(); + } + public void validateInternalRef(String url) { + java.util.Map components = new java.util.HashMap<>(); + + if (url == null || url.isEmpty()) { + throw new IllegalArgumentException("URL cannot be null or empty"); + } + + String remaining = url; + + // Parse scheme + int schemeEnd = remaining.indexOf(':'); + if (schemeEnd > 0) { + components.put("scheme", remaining.substring(0, schemeEnd).toLowerCase()); + remaining = remaining.substring(schemeEnd + 1); + } else { + components.put("scheme", null); + } + + if (remaining.startsWith("//")) { + remaining = remaining.substring(2); + int authorityEnd = remaining.indexOf('/'); + if (authorityEnd == -1) { + authorityEnd = remaining.indexOf('?'); + } + if (authorityEnd == -1) { + authorityEnd = remaining.indexOf('#'); + } + if (authorityEnd == -1) { + authorityEnd = remaining.length(); + } + + String authority = remaining.substring(0, authorityEnd); + + // Parse host and port + // Handle IPv6 addresses [::1] + if (authority.startsWith("[")) { + int ipv6End = authority.indexOf(']'); + if (ipv6End != -1) { + components.put("hostName", authority.substring(0, ipv6End + 1)); + if () { + + } + if (ipv6End + 1 < authority.length() && authority.charAt(ipv6End + 1) == ':') { + components.put("hostPort", authority.substring(ipv6End + 2)); + } else if (ipv6End + 1 < authority.length()) { + throw new IllegalArgumentException("Invalid authority format"); + } else { + components.put("hostPort", null); + } + } else { + throw new IllegalArgumentException("Invalid IPv6 address format"); + } + } else if (authority.indexOf('[') != -1) { + throw new IllegalArgumentException("Invalid IPv6 address format"); + } else { + int atIndex = authority.indexOf('@'); + if (atIndex != -1) { + authority = authority.substring(atIndex + 1); + } + + int portIndex = authority.indexOf(':'); + if (portIndex != -1) { + components.put("hostName", authority.substring(0, portIndex)); + components.put("hostPort", authority.substring(portIndex + 1)); + } else { + components.put("hostName", authority); + components.put("hostPort", null); + } + } + } + + if (components.get("hostPort") != null) { + try { + Integer.parseInt(components.get("hostPort")); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid port number format"); + } + } + } + /** * Constructor of relative reference from its parts. * @@ -751,7 +833,6 @@ public String getAuthority() { } return part.substring(2); - } return null; @@ -783,7 +864,7 @@ public Reference getBaseRef() { * part starts after the first '.' character of the last path segment and ends * with either the end of the segment of with the first ';' character (matrix * start). It is a token similar to file extensions separated by '.' characters. - * The value can be ommited.
+ * The value can be omitted.
* Note that no URI decoding is done by this method. * * @return The extensions or null. @@ -856,11 +937,11 @@ public String getFragment(boolean decode) { } /** - * Returns the hierarchical part which is equivalent to the scheme specific part + * Returns the hierarchical part which is equivalent to the scheme-specific part * less the query component.
* Note that no URI decoding is done by this method. * - * @return The hierarchical part . + * @return The hierarchical part. */ public String getHierarchicalPart() { if (hasScheme()) { @@ -907,12 +988,12 @@ public String getHierarchicalPart(boolean decode) { } /** - * Returns the host domain name component for server based hierarchical + * Returns the host domain name component for server-based hierarchical * identifiers. It can also be replaced by an IP address when no domain name was * registered.
* Note that no URI decoding is done by this method. * - * @return The host domain name component for server based hierarchical + * @return The host domain name component for server-based hierarchical * identifiers. */ public String getHostDomain() { @@ -970,9 +1051,7 @@ public String getHostDomain(boolean decode) { * @return The host identifier. */ public String getHostIdentifier() { - final StringBuilder result = new StringBuilder(); - result.append(getScheme()).append("://").append(getAuthority()); - return result.toString(); + return getScheme() + "://" + getAuthority(); } /** @@ -988,9 +1067,9 @@ public String getHostIdentifier(boolean decode) { } /** - * Returns the optional port number for server based hierarchical identifiers. + * Returns the optional port number for server-based hierarchical identifiers. * - * @return The optional port number for server based hierarchical identifiers or + * @return The optional port number for server-based hierarchical identifiers or * -1 if the port number does not exist. */ public int getHostPort() { @@ -1047,7 +1126,7 @@ public String getIdentifier(boolean decode) { /** * Returns the last segment of a hierarchical path.
- * For example the "/a/b/c" and "/a/b/c/" paths have the same segments: "a", + * For example, the "/a/b/c" and "/a/b/c/" paths have the same segments: "a", * "b", "c.
* Note that no URI decoding is done by this method. * @@ -1112,7 +1191,7 @@ public String getLastSegment(boolean decode, boolean excludeMatrix) { * Returns the optional matrix for hierarchical identifiers. A matrix part * starts after the first ';' character of the last path segment. It is a * sequence of 'name=value' parameters separated by ';' characters. The value - * can be ommitted.
+ * can be omitted.
* Note that no URI decoding is done by this method. * * @return The matrix or null. @@ -1176,7 +1255,7 @@ public Reference getParentRef() { String parentRef = null; String path = getPath(); - if (!path.equals("/") && !path.equals("")) { + if (!path.equals("/") && !path.isEmpty()) { if (path.endsWith("/")) { path = path.substring(0, path.length() - 1); } @@ -1197,8 +1276,8 @@ public Reference getParentRef() { } /** - * Returns the path component for hierarchical identifiers. If not path is - * available it returns null.
+ * Returns the path component for hierarchical identifiers. If no path is + * available, it returns null.
* Note that no URI decoding is done by this method. * * @return The path component for hierarchical identifiers. @@ -1244,7 +1323,7 @@ public String getPath() { } /** - * Returns the optionally decoded path component. If not path is available it + * Returns the optionally decoded path component. If no path is available, it * returns null. * * @param decode Indicates if the result should be decoded using the @@ -1267,7 +1346,7 @@ public String getQuery() { // Query found if (hasFragment()) { if (this.queryIndex < this.fragmentIndex) { - // Fragment found and query sign not inside fragment + // Fragment found and query sign not inside the fragment return this.internalRef.substring(this.queryIndex + 1, this.fragmentIndex); } @@ -1366,7 +1445,7 @@ public Reference getRelativeRef() { * IllegalArgumentException will be raised. * * @param base The base reference to use. - * @throws IllegalArgumentException If the relative reference is computed + * @throws IllegalArgumentException If the relative reference is computed, * although the reference or the base reference * are not absolute or not hierarchical. * @return The current reference relatively to a base reference. @@ -1413,7 +1492,7 @@ public Reference getRelativeRef(Reference base) { // Both paths are strictly equivalent relativePath = "."; } else if (i == localPath.length()) { - // End of local path reached + // End of a local path reached if (basePath.charAt(i) == '/') { if ((i + 1) == basePath.length()) { // Both paths are strictly equivalent @@ -1441,9 +1520,8 @@ public Reference getRelativeRef(Reference base) { } } else { // The base path has a segment that starts like - // the last local path segment - // But that is longer. Situation similar to a - // junction + // the last local path segment, But that is longer. + // Situation similar to a junction final StringBuilder sb = new StringBuilder(); // Count segments @@ -1460,7 +1538,7 @@ public Reference getRelativeRef(Reference base) { relativePath = sb.toString(); - if (relativePath.equals("")) { + if (relativePath.isEmpty()) { relativePath = "."; } } @@ -1625,10 +1703,10 @@ public Protocol getSchemeProtocol() { } /** - * Returns the scheme specific part.
+ * Returns the scheme-specific part.
* Note that no URI decoding is done by this method. * - * @return The scheme specific part. + * @return The scheme-specific part. */ public String getSchemeSpecificPart() { String result = null; @@ -1648,11 +1726,11 @@ public String getSchemeSpecificPart() { } /** - * Returns the optionally decoded scheme specific part. + * Returns the optionally decoded scheme-specific part. * * @param decode Indicates if the result should be decoded using the * {@link #decode(String)} method. - * @return The optionally decoded scheme specific part. + * @return The optionally decoded scheme-specific part. * @see #getSchemeSpecificPart() */ public String getSchemeSpecificPart(boolean decode) { @@ -1667,7 +1745,7 @@ public String getSchemeSpecificPart(boolean decode) { * @return The segments of a hierarchical path. */ public List getSegments() { - final List result = new ArrayList(); + final List result = new ArrayList<>(); final String path = getPath(); int start = -2; // The index of the slash starting the segment char current; @@ -1717,9 +1795,7 @@ public List getSegments(boolean decode) { final List result = getSegments(); if (decode) { - for (int i = 0; i < result.size(); i++) { - result.set(i, decode(result.get(i))); - } + result.replaceAll(Reference::decode); } return result; @@ -1727,7 +1803,7 @@ public List getSegments(boolean decode) { /** * Returns the target reference. This method resolves relative references - * against the base reference then normalize them. + * against the base reference, then normalizes them. * * @throws IllegalArgumentException If the base reference (after resolution) is * not absolute. @@ -1771,7 +1847,7 @@ public Reference getTargetRef() { } else { result.setAuthority(baseReference.getAuthority()); - if ((path == null) || (path.equals(""))) { + if ((path == null) || (path.isEmpty())) { result.setPath(baseReference.getPath()); if (query != null) { @@ -1786,7 +1862,7 @@ public Reference getTargetRef() { final String basePath = baseReference.getPath(); String mergedPath = null; - if ((baseReference.getAuthority() != null) && ((basePath == null) || (basePath.equals("")))) { + if ((baseReference.getAuthority() != null) && ((basePath == null) || (basePath.isEmpty()))) { mergedPath = "/" + path; } else { // Remove the last segment which may be empty if @@ -1822,11 +1898,11 @@ public Reference getTargetRef() { } /** - * Returns the user info component for server based hierarchical + * Returns the user info component for server-based hierarchical * identifiers.
* Note that no URI decoding is done by this method. * - * @return The user info component for server based hierarchical identifiers. + * @return The user info component for server-based hierarchical identifiers. */ public String getUserInfo() { String result = null; @@ -1859,13 +1935,13 @@ public String getUserInfo(boolean decode) { * Indicates if this reference has file-like extensions on its last path * segment. * - * @return True if there is are extensions. + * @return True if there are extensions. * @see #getExtensions() */ public boolean hasExtensions() { boolean result = false; - // If these reference ends with a "/", it cannot be a file. + // If these references end with a "/", it cannot be a file. final String path = getPath(); if (!((path != null) && path.endsWith("/"))) { final String lastSegment = getLastSegment(); @@ -1937,11 +2013,11 @@ public boolean isAbsolute() { } /** - * Returns true if both reference are equivalent, meaning that they resolve to + * Returns true if both references are equivalent, meaning that they resolve to * the same target reference. * * @param ref The reference to compare. - * @return True if both reference are equivalent. + * @return True if both references are equivalent. */ public boolean isEquivalentTo(Reference ref) { return getTargetRef().equals(ref.getTargetRef()); @@ -1993,13 +2069,13 @@ public boolean isRelative() { /** * Normalizes the reference. Useful before comparison between references or when - * building a target reference from a base and a relative references. + * building a target reference from a base reference and a relative reference. * * @return The current reference. */ public Reference normalize() { // 1. The input buffer is initialized with the now-appended path - // components and the output buffer is initialized to the empty string. + // components, and the output buffer is initialized to the empty string. StringBuilder output = new StringBuilder(); StringBuilder input = new StringBuilder(); String path = getPath(); @@ -2008,10 +2084,10 @@ public Reference normalize() { input.append(path); } - // 2. While the input buffer is not empty, loop as follows: + // 2. While the input buffer is not empty, the loop is as follows: while (input.length() > 0) { // A. If the input buffer begins with a prefix of "../" or "./", - // then remove that prefix from the input buffer; otherwise, + // then remove that prefix from the input buffer; otherwise. if ((input.length() >= 3) && input.substring(0, 3).equals("../")) { input.delete(0, 3); } else if ((input.length() >= 2) && input.substring(0, 2).equals("./")) { @@ -2061,7 +2137,7 @@ else if ((input.length() == 1) && input.substring(0, 1).equals(".")) { if (max != -1) { // We found the next "/" character. - output.append(input.substring(0, max)); + output.append(input, 0, max); input.delete(0, max); } else { // End of input buffer reached @@ -2074,7 +2150,7 @@ else if ((input.length() == 1) && input.substring(0, 1).equals(".")) { // Finally, the output buffer is returned as the result setPath(output.toString()); - // Ensure that the scheme and host names are reset in lower case + // Ensure that the scheme and host names are reset in the lower case setScheme(getScheme()); setHostDomain(getHostDomain()); @@ -2092,7 +2168,7 @@ else if ((input.length() == 1) && input.substring(0, 1).equals(".")) { } /** - * Removes the last segement from the output builder. + * Removes the last segment from the output builder. * * @param output The output builder to update. */ @@ -2192,9 +2268,9 @@ public void setExtensions(String extensions) { if (extensionIndex != -1) { // Extensions found - sb.append(lastSegment.substring(0, extensionIndex)); + sb.append(lastSegment, 0, extensionIndex); - if ((extensions != null) && (extensions.length() > 0)) { + if ((extensions != null) && (!extensions.isEmpty())) { sb.append('.').append(extensions); } @@ -2203,11 +2279,13 @@ public void setExtensions(String extensions) { } } else { // Extensions not found - if ((extensions != null) && (extensions.length() > 0)) { + if ((extensions != null) && (!extensions.isEmpty())) { if (matrixIndex != -1) { // Matrix found, make sure we append it // after the extensions - sb.append(lastSegment.substring(0, matrixIndex)).append('.').append(extensions) + sb.append(lastSegment, 0, matrixIndex) + .append('.') + .append(extensions) .append(lastSegment.substring(matrixIndex)); } else { // No matrix found, just append the extensions @@ -2219,7 +2297,7 @@ public void setExtensions(String extensions) { } } - // Finally update the last segment + // Finally, update the last segment setLastSegment(sb.toString()); } else { setLastSegment('.' + extensions); @@ -2292,9 +2370,9 @@ public void setFragment(String fragment) { } /** - * Sets the host domain component for server based hierarchical identifiers. + * Sets the host domain component for server-based hierarchical identifiers. * - * @param domain The host component for server based hierarchical identifiers. + * @param domain The host component for server-based hierarchical identifiers. */ public void setHostDomain(String domain) { final String authority = getAuthority(); @@ -2306,7 +2384,7 @@ public void setHostDomain(String domain) { domain = ""; } else { // URI specification indicates that host names should be - // produced in lower case + // produced in the lower case domain = domain.toLowerCase(); } @@ -2339,11 +2417,11 @@ public void setHostDomain(String domain) { } /** - * Sets the optional port number for server based hierarchical identifiers. + * Sets the optional port number for server-based hierarchical identifiers. * - * @param port The optional port number for server based hierarchical + * @param port The optional port number for server-based hierarchical * identifiers. - * @throws IllegalArgumentException If the autority has not been defined. + * @throws IllegalArgumentException If the authority has not been defined. */ public void setHostPort(Integer port) { final String authority = getAuthority(); @@ -2397,7 +2475,7 @@ public void setIdentifier(String identifier) { /** * Sets the last segment of the path. If no path is available, then it creates - * one and adds a slash in front of the given last segmetn.
+ * one and adds a slash in front of the given last segment.
* Note that no URI decoding is done by this method. * * @param lastSegment The last segment of a hierarchical path. @@ -2584,7 +2662,7 @@ public void setScheme(String scheme) { if (scheme != null) { // URI specification indicates that scheme names should be - // produced in lower case + // produced in the lower case scheme = scheme.toLowerCase(); } @@ -2610,9 +2688,9 @@ public void setScheme(String scheme) { } /** - * Sets the scheme specific part. + * Sets the scheme-specific part. * - * @param schemeSpecificPart The scheme specific part. + * @param schemeSpecificPart The scheme-specific part. */ public void setSchemeSpecificPart(String schemeSpecificPart) { schemeSpecificPart = encodeInvalidCharacters(schemeSpecificPart); @@ -2662,11 +2740,11 @@ public void setSegments(List segments) { } /** - * Sets the user info component for server based hierarchical identifiers. + * Sets the user info component for server-based hierarchical identifiers. * - * @param userInfo The user info component for server based hierarchical + * @param userInfo The user info component for server-based hierarchical * identifiers. - * @throws IllegalArgumentException If the autority part has not been defined. + * @throws IllegalArgumentException If the authority part has not been defined. */ public void setUserInfo(String userInfo) { final String authority = getAuthority(); @@ -2686,9 +2764,9 @@ public void setUserInfo(String userInfo) { } /** - * Returns the reference as an URI string. + * Returns the reference as a URI string. * - * @return The reference as an URI string. + * @return The reference as a URI string. */ @Override public String toString() { @@ -2779,27 +2857,29 @@ private void updateIndexes() { if (this.internalRef != null) { // Compute the indexes final int firstSlashIndex = this.internalRef.indexOf('/'); - this.schemeIndex = this.internalRef.indexOf(':'); + final int firstColonIndex = this.internalRef.indexOf(':'); - if ((firstSlashIndex != -1) && (this.schemeIndex > firstSlashIndex)) { + if ((firstSlashIndex != -1) && (firstColonIndex > firstSlashIndex)) { // We are in the rare case of a relative reference where one of // the path segments contains a colon character. In this case, // we ignore the colon as a valid scheme index. // Note that this colon can't be in the first segment as it is // forbidden by the URI RFC. this.schemeIndex = -1; - } + } else { + this.schemeIndex = firstColonIndex; + } this.queryIndex = this.internalRef.indexOf('?'); this.fragmentIndex = this.internalRef.indexOf('#'); if (hasQuery() && hasFragment() && (this.queryIndex > this.fragmentIndex)) { - // Query sign inside fragment + // Query sign inside a fragment this.queryIndex = -1; } if (hasQuery() && this.schemeIndex > this.queryIndex) { - // Colon sign inside query + // Colon sign inside a query this.schemeIndex = -1; } diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/data/ReferenceTest.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/data/ReferenceTest.java new file mode 100644 index 0000000000..df8479ffda --- /dev/null +++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/data/ReferenceTest.java @@ -0,0 +1,41 @@ +/** + * Copyright 2005-2024 Qlik + * + * The contents of this file is subject to the terms of the Apache 2.0 open + * source license available at http://www.opensource.org/licenses/apache-2.0 + * + * Restlet is a registered trademark of QlikTech International AB. + */ + +package org.restlet.data; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class ReferenceTest { + + @Test + public void shouldFailWhenParsingMultiPortReference() { + Assertions.assertThrows(IllegalArgumentException.class, () -> new Reference("http://192.168.1.1:1111:2222/")); + } + + @ParameterizedTest + @ValueSource(strings = { + "http://[192.168.0.1]127.0.0.1/", + "http://[192.168.0.1]vulndetector.com/", + "http://[normal.com@]vulndetector.com/", + "http://normal.com[user@vulndetector].com/", + "http://normal.com[@]vulndetector.com/"}) + public void shouldFailWhenParsingIncorrectHosts(String reference) { + Assertions.assertThrows(IllegalArgumentException.class, () -> new Reference(reference)); + } + + @ParameterizedTest + @ValueSource(strings = {"http://[0:0::vulndetector.com]:80", "http://[2001:db8::vulndetector.com]"}) + public void shouldFailWhenParsingIncorrectIPv6Hosts(String reference) { + Assertions.assertThrows(IllegalArgumentException.class, () -> new Reference(reference)); + } + +} diff --git a/pom.xml b/pom.xml index 136d1bbdb4..4c2a811157 100644 --- a/pom.xml +++ b/pom.xml @@ -106,6 +106,18 @@ 4.4.1 + + + + org.junit + junit-bom + ${lib-junit-version} + pom + import + + + + github @@ -273,16 +285,6 @@ org.restlet.gwt - - - - org.junit.jupiter - junit-jupiter-api - ${lib-junit-version} - - - - From 7b01d27540e6d1a681f6afb70ecbfb4789e6a2d3 Mon Sep 17 00:00:00 2001 From: Thierry Boileau Date: Fri, 23 Jan 2026 16:27:05 +0100 Subject: [PATCH 2/2] added control --- .../restlet/test/data/ReferenceTestCase.java | 744 ------------------ .../main/java/org/restlet/data/Reference.java | 154 ++-- .../java/org/restlet/data/ReferenceTest.java | 731 ++++++++++++++++- 3 files changed, 823 insertions(+), 806 deletions(-) delete mode 100644 org.restlet.java/org.restlet.test/src/main/java/org/restlet/test/data/ReferenceTestCase.java diff --git a/org.restlet.java/org.restlet.test/src/main/java/org/restlet/test/data/ReferenceTestCase.java b/org.restlet.java/org.restlet.test/src/main/java/org/restlet/test/data/ReferenceTestCase.java deleted file mode 100644 index 8b99446e9f..0000000000 --- a/org.restlet.java/org.restlet.test/src/main/java/org/restlet/test/data/ReferenceTestCase.java +++ /dev/null @@ -1,744 +0,0 @@ -/** - * Copyright 2005-2024 Qlik - * - * The contents of this file is subject to the terms of the Apache 2.0 open - * source license available at http://www.opensource.org/licenses/apache-2.0 - * - * Restlet is a registered trademark of QlikTech International AB. - */ - -package org.restlet.test.data; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.jupiter.api.Test; -import org.restlet.data.Form; -import org.restlet.data.Header; -import org.restlet.data.Protocol; -import org.restlet.data.Reference; -import org.restlet.engine.header.HeaderConstants; -import org.restlet.engine.util.ReferenceUtils; -import org.restlet.test.RestletTestCase; -import org.restlet.util.Series; - -/** - * Test {@link org.restlet.data.Reference}. - * - * @author Jerome Louvel - * @author Lars Heuer (heuer[at]semagia.com) Semagia - */ -public class ReferenceTestCase extends RestletTestCase { - protected final static String DEFAULT_SCHEME = "http"; - - protected final static String DEFAULT_SCHEMEPART = "//"; - - /** - * Returns a reference initialized with http://restlet.org. - * - * @return Reference instance. - */ - protected Reference getDefaultReference() { - final Reference ref = getReference(); - ref.setHostDomain("restlet.org"); - return ref; - } - - /** - * Returns a reference with uri == http:// - * - * @return Reference instance. - */ - protected Reference getReference() { - final Reference ref = new Reference(); - ref.setScheme(DEFAULT_SCHEME); - ref.setSchemeSpecificPart(DEFAULT_SCHEMEPART); - return ref; - } - - /** - * Test addition methods. - */ - @Test - public void testAdditions() throws Exception { - final Reference ref = new Reference("http://restlet.org"); - ref.addQueryParameter("abc", "123"); - assertEquals("http://restlet.org?abc=123", ref.toString()); - ref.addQueryParameter("def", null); - assertEquals("http://restlet.org?abc=123&def", ref.toString()); - ref.addSegment("root"); - assertEquals("http://restlet.org/root?abc=123&def", ref.toString()); - ref.addSegment("dir"); - assertEquals("http://restlet.org/root/dir?abc=123&def", ref.toString()); - } - - @Test - public void testEmptyRef() { - Reference reference = new Reference(); - reference.setAuthority("testAuthority"); // must not produce NPE - - reference = new Reference(); - reference.setBaseRef("http://localhost"); // must not produce NPE - - reference = new Reference(); - reference.setFragment("fragment"); // must not produce NPE - - reference = new Reference(); - reference.setHostDomain("localhost"); // must not produce NPE - assertEquals("localhost", reference.getAuthority()); - reference.setHostPort(Integer.valueOf(4711)); // must not produce NPE - assertEquals("localhost:4711", reference.getAuthority()); - reference.setUserInfo("sdgj:skdfj"); // must not produce NPE - assertEquals("sdgj:skdfj@localhost:4711", reference.getAuthority()); - - reference = new Reference(); - reference.setIdentifier("http://host/abc/wkj"); // must not produce NPE - - reference = new Reference(); - reference.setPath("loc/alhost"); // must not produce NPE - - reference = new Reference(); - reference.setProtocol(Protocol.HTTPS); // must not produce NPE - - reference = new Reference(); - reference.setQuery("a=b&c=&g=1"); // must not produce NPE - - reference = new Reference(); - reference.setRelativePart("http://localhost"); // must not produce NPE - - reference = new Reference(); - reference.setScheme("skjf"); // must not produce NPE - - reference = new Reference(); - reference.setSchemeSpecificPart("host/afjhsd"); // must not produce NPE - - reference = new Reference(); - final List segments = new ArrayList(); - segments.add("skhf"); - segments.add("sgdfg"); - segments.add("xiz"); - reference.setSegments(segments); // must not produce NPE - } - - /** - * Equality tests. - */ - @Test - public void testEquals() { - final Reference ref1 = getDefaultReference(); - final Reference ref2 = getDefaultReference(); - assertEquals(ref1, ref2); - assertTrue(ref1.equals(ref2)); - } - - @Test - public void testGetLastSegment() { - Reference reference = new Reference("http://hostname"); - assertNull(reference.getLastSegment()); - - reference = new Reference("http://hostname/"); - assertNull(reference.getLastSegment()); - - reference = new Reference("http://hostname/abc"); - assertEquals("abc", reference.getLastSegment()); - - reference = new Reference("http://hostname/abc/"); - assertEquals("abc", reference.getLastSegment()); - - reference = new Reference("http://hostname/123/abc/"); - assertEquals("abc", reference.getLastSegment()); - - reference = new Reference("http://hostname/123/abc"); - assertEquals("abc", reference.getLastSegment()); - } - - /** - * Test hostname getting/setting. - */ - @Test - public void testHostName() { - final Reference ref = getReference(); - String host = "restlet.org"; - ref.setHostDomain(host); - assertEquals(host, ref.getHostDomain()); - host = "restlet.org"; - ref.setHostDomain(host); - assertEquals(host, ref.getHostDomain()); - Reference ref2 = new Reference("http://[::1]:8182"); - assertEquals("[::1]", ref2.getHostDomain()); - } - - @Test - public void testMatrix() { - final Reference ref1 = new Reference( - "http://domain.tld/whatever/a=1;b=2;c=4?x=a&y=b"); - final Reference ref2 = new Reference( - "http://domain.tld/whatever/a=1/foo;b=2;c=4;d?x=a&y=b"); - final Reference ref3 = new Reference( - "http://domain.tld/whatever/a=1;b=2;c=4/foo?x=a&y=b"); - - assertTrue(ref1.hasMatrix()); - assertTrue(ref2.hasMatrix()); - assertFalse(ref3.hasMatrix()); - - assertEquals("b=2;c=4", ref1.getMatrix()); - assertEquals("b=2;c=4;d", ref2.getMatrix()); - - final Form form1 = ref1.getMatrixAsForm(); - assertEquals("2", form1.getFirstValue("b")); - assertEquals("4", form1.getFirstValue("c")); - - final Form form2 = ref1.getMatrixAsForm(); - assertEquals("2", form2.getFirstValue("b")); - assertEquals("4", form2.getFirstValue("c")); - assertNull(form2.getFirstValue("d")); - - final Form newForm = new Form(); - newForm.add("a", "1"); - newForm.add("b", "2"); - newForm.add("c", "4"); - assertEquals("a=1;b=2;c=4", newForm.getMatrixString()); - } - - @Test - public void testOriginalRef() { - Reference ref = new Reference("http://localhost/test"); - Series
headers = new Series<>(Header.class); - headers.add(HeaderConstants.HEADER_X_FORWARDED_PROTO, "HTTPS"); - headers.add(HeaderConstants.HEADER_X_FORWARDED_PORT, "123"); - - Reference originalRef = ReferenceUtils.getOriginalRef(ref, headers); - assertEquals(originalRef.getSchemeProtocol(), Protocol.HTTPS); - assertEquals(originalRef.getHostPort(), 123); - } - - /** - * Test the computation of parent references, for absolute and relative - * URIs. - */ - @Test - public void testParentRef() { - Reference baseRef = new Reference("http://test.com/foo/bar"); - Reference parentRef = baseRef.getParentRef(); - assertEquals("http://test.com/foo/", parentRef.toString()); - - baseRef = new Reference("/foo/bar"); - parentRef = baseRef.getParentRef(); - assertEquals("/foo/", parentRef.toString()); - } - - /** - * Tests the URI parsing. - */ - @Test - public void testParsing() { - final String base = "http://a/b/c/d;p?q"; - - final String uri01 = "g:h"; - final String uri02 = "g"; - final String uri03 = "./g"; - final String uri04 = "g/"; - final String uri05 = "/g"; - final String uri06 = "//g"; - final String uri07 = "?y"; - final String uri08 = "g?y"; - final String uri09 = "#s"; - final String uri10 = "g#s"; - final String uri11 = "g?y#s"; - final String uri12 = ";x"; - final String uri13 = "g;x"; - final String uri14 = "g;x?y#s"; - final String uri15 = ""; - final String uri16 = "."; - final String uri17 = "./"; - final String uri18 = ".."; - final String uri19 = "../"; - final String uri20 = "../g"; - final String uri21 = "../.."; - final String uri22 = "../../"; - final String uri23 = "../../g"; - final String uri24 = "../../../g"; - final String uri25 = "../../../../g"; - final String uri26 = "/./g"; - final String uri27 = "/../g"; - final String uri28 = "g."; - final String uri29 = ".g"; - final String uri30 = "g.."; - final String uri31 = "..g"; - final String uri32 = "./../g"; - final String uri33 = "./g/."; - final String uri34 = "g/./h"; - final String uri35 = "g/../h"; - final String uri36 = "g;x=1/./y"; - final String uri37 = "g;x=1/../y"; - - final String uri101 = "g:h"; - final String uri102 = "http://a/b/c/g"; - final String uri103 = "http://a/b/c/g"; - final String uri104 = "http://a/b/c/g/"; - final String uri105 = "http://a/g"; - final String uri106 = "http://g"; - final String uri107 = "http://a/b/c/d;p?y"; - final String uri108 = "http://a/b/c/g?y"; - final String uri109 = "http://a/b/c/d;p?q#s"; - final String uri110 = "http://a/b/c/g#s"; - final String uri111 = "http://a/b/c/g?y#s"; - final String uri112 = "http://a/b/c/;x"; - final String uri113 = "http://a/b/c/g;x"; - final String uri114 = "http://a/b/c/g;x?y#s"; - final String uri115 = "http://a/b/c/d;p?q"; - final String uri116 = "http://a/b/c/"; - final String uri117 = "http://a/b/c/"; - final String uri118 = "http://a/b/"; - final String uri119 = "http://a/b/"; - final String uri120 = "http://a/b/g"; - final String uri121 = "http://a/"; - final String uri122 = "http://a/"; - final String uri123 = "http://a/g"; - final String uri124 = "http://a/g"; - final String uri125 = "http://a/g"; - final String uri126 = "http://a/g"; - final String uri127 = "http://a/g"; - final String uri128 = "http://a/b/c/g."; - final String uri129 = "http://a/b/c/.g"; - final String uri130 = "http://a/b/c/g.."; - final String uri131 = "http://a/b/c/..g"; - final String uri132 = "http://a/b/g"; - final String uri133 = "http://a/b/c/g/"; - final String uri134 = "http://a/b/c/g/h"; - final String uri135 = "http://a/b/c/h"; - final String uri136 = "http://a/b/c/g;x=1/y"; - final String uri137 = "http://a/b/c/y"; - - final Reference host = new Reference("http://host.com"); - final Reference slashdir = new Reference(host, "/dir"); - final Reference dir = new Reference(host, "dir"); - final Reference dirslash = new Reference(host, "dir/"); - final Reference fulldir = new Reference("http://host.com/dir"); - final Reference fulldirsub = new Reference(fulldir, "sub"); - final Reference fulldirslashsub = new Reference(fulldir, "/sub"); - final Reference slashdirsub = new Reference(slashdir, "sub"); - final Reference slashdirslashsub = new Reference(slashdir, "/sub"); - final Reference dirslashsub = new Reference(dirslash, "sub"); - final Reference fullsub = new Reference("http://host.com/dir/sub"); - - // Test the parsing of references into its components - testRef0("foo://example.com:8042/over/there?name=ferret#nose", "foo", - "example.com:8042", "/over/there", "name=ferret", "nose"); - testRef0("urn:example:animal:ferret:nose", "urn", null, - "example:animal:ferret:nose", null, null); - testRef0("mailto:fred@example.com", "mailto", null, "fred@example.com", - null, null); - testRef0("foo://info.example.com?fred", "foo", "info.example.com", - null, "fred", null); - testRef0("*", null, null, "*", null, null); - testRef0("http://localhost?query", "http", "localhost", null, "query", - null); - testRef0("http://localhost#?query", "http", "localhost", null, null, - "?query"); - testRef0("http://localhost/?query", "http", "localhost", "/", "query", - null); - testRef0("http://localhost/#?query", "http", "localhost", "/", null, - "?query"); - testRef0("http://localhost/path#frag/ment", "http", "localhost", - "/path", null, "frag/ment"); - testRef0("http://localhost/path?qu/ery", "http", "localhost", "/path", - "qu/ery", null); - - // Test the resolution of relative references - testRef1(base, uri01, uri101); - testRef1(base, uri02, uri102); - testRef1(base, uri03, uri103); - testRef1(base, uri04, uri104); - testRef1(base, uri05, uri105); - testRef1(base, uri06, uri106); - testRef1(base, uri07, uri107); - testRef1(base, uri08, uri108); - testRef1(base, uri09, uri109); - testRef1(base, uri10, uri110); - testRef1(base, uri11, uri111); - testRef1(base, uri12, uri112); - testRef1(base, uri13, uri113); - testRef1(base, uri14, uri114); - testRef1(base, uri15, uri115); - testRef1(base, uri16, uri116); - testRef1(base, uri17, uri117); - testRef1(base, uri18, uri118); - testRef1(base, uri19, uri119); - testRef1(base, uri20, uri120); - testRef1(base, uri21, uri121); - testRef1(base, uri22, uri122); - testRef1(base, uri23, uri123); - testRef1(base, uri24, uri124); - testRef1(base, uri25, uri125); - testRef1(base, uri26, uri126); - testRef1(base, uri27, uri127); - testRef1(base, uri28, uri128); - testRef1(base, uri29, uri129); - testRef1(base, uri30, uri130); - testRef1(base, uri31, uri131); - testRef1(base, uri32, uri132); - testRef1(base, uri33, uri133); - testRef1(base, uri34, uri134); - testRef1(base, uri35, uri135); - testRef1(base, uri36, uri136); - testRef1(base, uri37, uri137); - - // Test the relativization of absolute references - testRef2(base, uri102, uri02); - testRef2(base, uri104, uri04); - testRef2(base, uri107, uri07); - testRef2(base, uri108, uri08); - testRef2(base, uri109, uri09); - testRef2(base, uri110, uri10); - testRef2(base, uri111, uri11); - testRef2(base, uri112, uri12); - testRef2(base, uri113, uri13); - testRef2(base, uri114, uri14); - testRef2(base, uri116, uri16); - testRef2(base, uri118, uri18); - testRef2(base, uri120, uri20); - testRef2(base, uri121, uri21); - testRef2(base, uri123, uri23); - testRef2(uri104, uri116, uri18); - testRef2(uri104, uri118, uri21); - - // Test the toString method with or without query/fragment - testRef3("http://localhost/path#fragment", true, true, - "http://localhost/path#fragment"); - testRef3("http://localhost/path#fragment", true, false, - "http://localhost/path"); - testRef3("http://localhost/path#fragment", false, true, - "http://localhost/path#fragment"); - testRef3("http://localhost/path#fragment", false, false, - "http://localhost/path"); - - testRef3("http://localhost/path?query", true, true, - "http://localhost/path?query"); - testRef3("http://localhost/path?query", true, false, - "http://localhost/path?query"); - testRef3("http://localhost/path?query", false, true, - "http://localhost/path"); - testRef3("http://localhost/path?query", false, false, - "http://localhost/path"); - - testRef3("http://localhost/path?query#fragment", true, true, - "http://localhost/path?query#fragment"); - testRef3("http://localhost/path?query#fragment", true, false, - "http://localhost/path?query"); - testRef3("http://localhost/path?query#fragment", false, true, - "http://localhost/path#fragment"); - testRef3("http://localhost/path?query#fragment", false, false, - "http://localhost/path"); - - testRef3("http://localhost/path#fragment?query", true, true, - "http://localhost/path#fragment?query"); - testRef3("http://localhost/path#fragment?query", true, false, - "http://localhost/path"); - testRef3("http://localhost/path#fragment?query", false, true, - "http://localhost/path#fragment?query"); - testRef3("http://localhost/path#fragment?query", false, false, - "http://localhost/path"); - - testRef4(host, "http", "host.com", null, "http://host.com", - "http://host.com", "http://host.com", null, null); - testRef4(slashdir, null, null, "/dir", null, "/dir", - "http://host.com/dir", null, "/dir"); - testRef4(dir, null, null, "dir", null, "dir", "http://host.com/dir", - null, "dir"); - testRef4(dirslash, null, null, "dir/", null, "dir/", - "http://host.com/dir/", null, "dir/"); - testRef4(fulldir, "http", "host.com", "/dir", "http://host.com/dir", - "http://host.com/dir", "http://host.com/dir", null, null); - - testRef4(fulldirsub, null, null, "sub", null, "sub", - "http://host.com/sub", null, "sub"); - testRef4(fulldirslashsub, null, null, "/sub", null, "/sub", - "http://host.com/sub", null, "/sub"); - testRef4(slashdirsub, null, null, "sub", null, "sub", - "http://host.com/sub", null, "sub"); - testRef4(slashdirslashsub, null, null, "/sub", null, "/sub", - "http://host.com/sub", null, "/sub"); - testRef4(dirslashsub, null, null, "sub", null, "sub", - "http://host.com/dir/sub", null, "sub"); - testRef4(fullsub, "http", "host.com", "/dir/sub", - "http://host.com/dir/sub", "http://host.com/dir/sub", - "http://host.com/dir/sub", null, null); - } - - /** - * Test port getting/setting. - */ - @Test - public void testPort() { - Reference ref = getDefaultReference(); - int port = 8080; - ref.setHostPort(port); - assertEquals(port, ref.getHostPort()); - port = 9090; - ref.setHostPort(port); - assertEquals(port, ref.getHostPort()); - ref = new Reference("http://[::1]:8182"); - assertEquals(8182, ref.getHostPort()); - } - - @Test - public void testProtocolConstructors() { - assertEquals("http://restlet.org", new Reference(Protocol.HTTP, - "restlet.org").toString()); - assertEquals("https://restlet.org:8443", new Reference(Protocol.HTTPS, - "restlet.org", 8443).toString()); - - final Reference ref = new Reference(Protocol.HTTP, "restlet.org"); - ref.addQueryParameter("abc", "123"); - assertEquals("http://restlet.org?abc=123", ref.toString()); - } - - @Test - public void testQuery() { - - Reference ref1 = new Reference( - "http://localhost/search?q=anythingelse%"); - String query = ref1.getQuery(); - assertEquals("q=anythingelse%25", query); - - Form queryForm = ref1.getQueryAsForm(); - assertEquals("anythingelse%", queryForm.getFirstValue("q")); - - Form extJsQuery = new Form( - "&_dc=1244741620627&callback=stcCallback1001"); - assertEquals("1244741620627", extJsQuery.getFirstValue("_dc")); - assertEquals("stcCallback1001", extJsQuery.getFirstValue("callback")); - - Reference ref = new Reference("http://localhost/v1/projects/13404"); - ref.addQueryParameter("dyn", "true"); - assertEquals("http://localhost/v1/projects/13404?dyn=true", - ref.toString()); - } - - @Test - public void testQueryWithUri() { - Reference ref = new Reference(new Reference("http://localhost:8111/"), - "http://localhost:8111/contrats/123?srvgwt=localhost:9997"); - assertEquals("contrats/123?srvgwt=localhost:9997", ref.getRelativeRef() - .toString()); - } - - /** - * Tests the parsing of a reference into its components - * - * @param reference - * @param scheme - * @param authority - * @param path - * @param query - * @param fragment - */ - private void testRef0(String reference, String scheme, String authority, - String path, String query, String fragment) { - final Reference ref = new Reference(reference); - assertEquals(scheme, ref.getScheme()); - assertEquals(authority, ref.getAuthority()); - assertEquals(path, ref.getPath()); - assertEquals(query, ref.getQuery()); - assertEquals(fragment, ref.getFragment()); - } - - /** - * Test the resolution of relative references. - * - * @param baseUri - * @param relativeUri - * @param expectedAbsoluteUri - */ - private void testRef1(String baseUri, String relativeUri, - String expectedAbsoluteUri) { - final Reference baseRef = new Reference(baseUri); - final Reference relativeRef = new Reference(baseRef, relativeUri); - final Reference absoluteRef = relativeRef.getTargetRef(); - assertEquals(expectedAbsoluteUri, absoluteRef.toString()); - } - - /** - * Test the relativization of absolute references - * - * @param baseUri - * @param absoluteUri - * @param expectedRelativeUri - */ - private void testRef2(String baseUri, String absoluteUri, - String expectedRelativeUri) { - final Reference baseRef = new Reference(baseUri); - final Reference absoluteRef = new Reference(absoluteUri); - final Reference relativeRef = absoluteRef.getRelativeRef(baseRef); - assertEquals(expectedRelativeUri, relativeRef.toString()); - } - - /** - * Test the toString method with or without query/fragment - * - * @param reference - * @param query - * @param fragment - * @param toString - */ - private void testRef3(String reference, boolean query, boolean fragment, - String toString) { - final Reference ref = new Reference(reference); - assertEquals(ref.toString(query, fragment), toString); - } - - /** - * Test the behaviour of several getters upon a Reference object. - */ - private void testRef4(Reference reference, String scheme, String authority, - String path, String remainingPart, String toString, - String targetRef, String query, String relativePart) { - assertEquals(reference.getScheme(), scheme); - assertEquals(reference.getAuthority(), authority); - assertEquals(reference.getPath(), path); - assertEquals(reference.getRemainingPart(), remainingPart); - assertEquals(reference.toString(), toString); - assertEquals(reference.getTargetRef().toString(), targetRef); - assertEquals(reference.getQuery(), query); - assertEquals(reference.getRelativePart(), relativePart); - } - - @Test - public void testRiap() { - Reference baseRef = new Reference("riap://component/exist/db/"); - Reference ref = new Reference(baseRef, "something.xq"); - assertEquals("riap://component/exist/db/something.xq", ref - .getTargetRef().toString()); - } - - /** - * Test scheme getting/setting. - */ - @Test - public void testScheme() { - final Reference ref = getDefaultReference(); - assertEquals(DEFAULT_SCHEME, ref.getScheme()); - final String scheme = "https"; - ref.setScheme(scheme); - assertEquals(scheme, ref.getScheme()); - ref.setScheme(DEFAULT_SCHEME); - assertEquals(DEFAULT_SCHEME, ref.getScheme()); - } - - /** - * Test scheme specific part getting/setting. - */ - @Test - public void testSchemeSpecificPart() { - final Reference ref = getDefaultReference(); - String part = "//restlet.org"; - assertEquals(part, ref.getSchemeSpecificPart()); - part = "//restlet.net"; - ref.setSchemeSpecificPart(part); - assertEquals(part, ref.getSchemeSpecificPart()); - } - - /** - * Test setting of the last segment. - */ - @Test - public void testSetLastSegment() { - Reference ref = new Reference("http://localhost:1234"); - ref.addSegment("test"); - assertEquals("http://localhost:1234/test", ref.toString()); - - ref.setLastSegment("last"); - assertEquals("http://localhost:1234/last", ref.toString()); - - ref = new Reference("http://localhost:1234"); - ref.setLastSegment("last"); - assertEquals("http://localhost:1234/last", ref.toString()); - - ref.setLastSegment("test"); - assertEquals("http://localhost:1234/test", ref.toString()); - - ref.addSegment("last"); - assertEquals("http://localhost:1234/test/last", ref.toString()); - } - - @Test - public void testTargetRef() { - Reference ref = new Reference( - "http://twitter.com?status=RT @gamasutra: Devil May Cry : Born Again http://www.gamasutra.com/view/feature/177267/"); - Reference targetRef = new Reference( - new Reference( - "http://www.gamasutra.com/view/feature/177267/devil_may_cry_born_again.php"), - ref).getTargetRef(); - assertEquals( - "http://twitter.com?status=RT%20@gamasutra:%20%20Devil%20May%20Cry%20:%20Born%20Again%20http:?status=RT%20@gamasutra:%20%20Devil%20May%20Cry%20:%20Born%20Again%20http://www.gamasutra.com/view/feature/177267/", - targetRef.toString()); - } - - /** - * Test references that are unequal. - */ - @Test - public void testUnEquals() throws Exception { - final String uri1 = "http://restlet.org/"; - final String uri2 = "http://restlet.net/"; - final Reference ref1 = new Reference(uri1); - final Reference ref2 = new Reference(uri2); - assertFalse(ref1.equals(ref2)); - assertFalse(ref1.equals(null)); - } - - @Test - public void testUserinfo() { - final Reference reference = new Reference("http://localhost:81"); - // This format is depre. however we may prevent failures. - reference.setUserInfo("login:password"); - assertEquals("login:password@localhost:81", reference.getAuthority()); - assertEquals("localhost", reference.getHostDomain()); - assertEquals(81, reference.getHostPort()); - assertEquals("login:password", reference.getUserInfo()); - - reference.setHostDomain("[::1]"); - assertEquals("login:password@[::1]:81", reference.getAuthority()); - assertEquals("[::1]", reference.getHostDomain()); - assertEquals(81, reference.getHostPort()); - assertEquals("login:password", reference.getUserInfo()); - - reference.setHostDomain("www.example.com"); - assertEquals("login:password@www.example.com:81", - reference.getAuthority()); - assertEquals("www.example.com", reference.getHostDomain()); - assertEquals(81, reference.getHostPort()); - assertEquals("login:password", reference.getUserInfo()); - - reference.setHostPort(82); - assertEquals("login:password@www.example.com:82", - reference.getAuthority()); - assertEquals("www.example.com", reference.getHostDomain()); - assertEquals(82, reference.getHostPort()); - assertEquals("login:password", reference.getUserInfo()); - - reference.setUserInfo("login"); - assertEquals("login@www.example.com:82", reference.getAuthority()); - assertEquals("www.example.com", reference.getHostDomain()); - assertEquals(82, reference.getHostPort()); - assertEquals("login", reference.getUserInfo()); - } - - @Test - public void testValidity() { - String uri = "http ://domain.tld/whatever/"; - Reference ref = new Reference(uri); - assertEquals("http%20://domain.tld/whatever/", ref.toString()); - - uri = "file:///C|/wherever\\whatever.swf"; - ref = new Reference(uri); - assertEquals("file:///C%7C/wherever%5Cwhatever.swf", ref.toString()); - } -} diff --git a/org.restlet.java/org.restlet/src/main/java/org/restlet/data/Reference.java b/org.restlet.java/org.restlet/src/main/java/org/restlet/data/Reference.java index 498df4cafd..8aa525f330 100644 --- a/org.restlet.java/org.restlet/src/main/java/org/restlet/data/Reference.java +++ b/org.restlet.java/org.restlet/src/main/java/org/restlet/data/Reference.java @@ -516,85 +516,119 @@ public Reference(Reference baseRef, String uriRef) { } public void validateInternalRef(String url) { - java.util.Map components = new java.util.HashMap<>(); - if (url == null || url.isEmpty()) { - throw new IllegalArgumentException("URL cannot be null or empty"); + return; } - String remaining = url; - - // Parse scheme - int schemeEnd = remaining.indexOf(':'); - if (schemeEnd > 0) { - components.put("scheme", remaining.substring(0, schemeEnd).toLowerCase()); - remaining = remaining.substring(schemeEnd + 1); - } else { - components.put("scheme", null); - } + String authority = getAuthority(url); - if (remaining.startsWith("//")) { - remaining = remaining.substring(2); - int authorityEnd = remaining.indexOf('/'); - if (authorityEnd == -1) { - authorityEnd = remaining.indexOf('?'); - } - if (authorityEnd == -1) { - authorityEnd = remaining.indexOf('#'); + if (authority.startsWith("[")) { + int ipv6End = authority.indexOf(']'); + if (ipv6End != -1) { + validateIPv6(authority.substring(0, ipv6End + 1)); + if (ipv6End + 1 < authority.length() && authority.charAt(ipv6End + 1) == ':') { + validateHostPort(authority.substring(ipv6End + 2)); + } else if (ipv6End + 1 < authority.length()) { + throw new IllegalArgumentException("Invalid authority format"); + } + } else { + throw new IllegalArgumentException("Invalid IPv6 address format"); } - if (authorityEnd == -1) { - authorityEnd = remaining.length(); + } else if (authority.indexOf('[') != -1) { + throw new IllegalArgumentException("Invalid IPv6 address format"); + } else { + int atIndex = authority.indexOf('@'); + if (atIndex != -1) { + authority = authority.substring(atIndex + 1); } - String authority = remaining.substring(0, authorityEnd); + int portIndex = authority.indexOf(':'); + if (portIndex != -1) { + validateHostPort(authority.substring(portIndex + 1)); + } + } + } - // Parse host and port - // Handle IPv6 addresses [::1] - if (authority.startsWith("[")) { - int ipv6End = authority.indexOf(']'); - if (ipv6End != -1) { - components.put("hostName", authority.substring(0, ipv6End + 1)); - if () { + private static String getAuthority(final String url) { + String remaining = url; - } - if (ipv6End + 1 < authority.length() && authority.charAt(ipv6End + 1) == ':') { - components.put("hostPort", authority.substring(ipv6End + 2)); - } else if (ipv6End + 1 < authority.length()) { - throw new IllegalArgumentException("Invalid authority format"); - } else { - components.put("hostPort", null); - } - } else { - throw new IllegalArgumentException("Invalid IPv6 address format"); - } - } else if (authority.indexOf('[') != -1) { - throw new IllegalArgumentException("Invalid IPv6 address format"); - } else { - int atIndex = authority.indexOf('@'); - if (atIndex != -1) { - authority = authority.substring(atIndex + 1); - } + // Parse scheme + int schemeEnd = remaining.indexOf("://"); + if (schemeEnd > 0) { + remaining = remaining.substring(schemeEnd + 3); + } - int portIndex = authority.indexOf(':'); - if (portIndex != -1) { - components.put("hostName", authority.substring(0, portIndex)); - components.put("hostPort", authority.substring(portIndex + 1)); - } else { - components.put("hostName", authority); - components.put("hostPort", null); - } - } + int authorityEnd = remaining.indexOf('/'); + if (authorityEnd == -1) { + authorityEnd = remaining.indexOf('?'); + } + if (authorityEnd == -1) { + authorityEnd = remaining.indexOf('#'); + } + if (authorityEnd == -1) { + authorityEnd = remaining.length(); } - if (components.get("hostPort") != null) { + return remaining.substring(0, authorityEnd); + } + + private static void validateHostPort(final String hostPort) { + if (hostPort != null) { try { - Integer.parseInt(components.get("hostPort")); + Integer.parseInt(hostPort); } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid port number format"); } } } + // RFC 2373 + private static void validateIPv6(String ipv6) { + if (ipv6 == null || ipv6.isEmpty()) { + throw new IllegalArgumentException("Invalid IPv6 address"); + } + + if (ipv6.startsWith("[")) { + ipv6 = ipv6.substring(1); + } + if (ipv6.endsWith("]")) { + ipv6 = ipv6.substring(0, ipv6.length() - 1); + } + + // Check for double colon compression (only one allowed) + int doubleColonCount = 0; + int idx = ipv6.indexOf("::"); + while (idx != -1) { + doubleColonCount++; + idx = ipv6.indexOf("::", idx + 2); + } + if (doubleColonCount > 1) { + throw new IllegalArgumentException("Invalid IPv6 address format"); + } + + String[] parts = ipv6.split(":", -1); + int maxParts = 8; + + if (parts.length > maxParts) { + throw new IllegalArgumentException("Invalid IPv6 address format"); + } + + for (String part : parts) { + if (part.isEmpty() && doubleColonCount == 0) { + throw new IllegalArgumentException("Invalid IPv6 address format"); + } + if (!part.isEmpty()) { + if (part.length() > 4) { + throw new IllegalArgumentException("Invalid IPv6 address format"); + } + for (char c : part.toCharArray()) { + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + throw new IllegalArgumentException("Invalid IPv6 address format"); + } + } + } + } + } /** * Constructor of relative reference from its parts. * diff --git a/org.restlet.java/org.restlet/src/test/java/org/restlet/data/ReferenceTest.java b/org.restlet.java/org.restlet/src/test/java/org/restlet/data/ReferenceTest.java index df8479ffda..d080b7c66c 100644 --- a/org.restlet.java/org.restlet/src/test/java/org/restlet/data/ReferenceTest.java +++ b/org.restlet.java/org.restlet/src/test/java/org/restlet/data/ReferenceTest.java @@ -9,13 +9,734 @@ package org.restlet.data; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.List; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.restlet.engine.header.HeaderConstants; +import org.restlet.engine.util.ReferenceUtils; +import org.restlet.util.Series; public class ReferenceTest { + protected final static String DEFAULT_SCHEME = "http"; + + protected final static String DEFAULT_SCHEMEPART = "//"; + + /** + * Returns a reference initialized with http://restlet.org. + * + * @return Reference instance. + */ + protected Reference getDefaultReference() { + final Reference ref = getReference(); + ref.setHostDomain("restlet.org"); + return ref; + } + + /** + * Returns a reference with uri == http:// + * + * @return Reference instance. + */ + protected Reference getReference() { + final Reference ref = new Reference(); + ref.setScheme(DEFAULT_SCHEME); + ref.setSchemeSpecificPart(DEFAULT_SCHEMEPART); + return ref; + } + + /** + * Test addition methods. + */ + @Test + public void testAdditions() throws Exception { + final Reference ref = new Reference("http://restlet.org"); + ref.addQueryParameter("abc", "123"); + assertEquals("http://restlet.org?abc=123", ref.toString()); + ref.addQueryParameter("def", null); + assertEquals("http://restlet.org?abc=123&def", ref.toString()); + ref.addSegment("root"); + assertEquals("http://restlet.org/root?abc=123&def", ref.toString()); + ref.addSegment("dir"); + assertEquals("http://restlet.org/root/dir?abc=123&def", ref.toString()); + } + + @Test + public void testEmptyRef() { + Reference reference = new Reference(); + reference.setAuthority("testAuthority"); // must not produce NPE + + reference = new Reference(); + reference.setBaseRef("http://localhost"); // must not produce NPE + + reference = new Reference(); + reference.setFragment("fragment"); // must not produce NPE + + reference = new Reference(); + reference.setHostDomain("localhost"); // must not produce NPE + assertEquals("localhost", reference.getAuthority()); + reference.setHostPort(Integer.valueOf(4711)); // must not produce NPE + assertEquals("localhost:4711", reference.getAuthority()); + reference.setUserInfo("sdgj:skdfj"); // must not produce NPE + assertEquals("sdgj:skdfj@localhost:4711", reference.getAuthority()); + + reference = new Reference(); + reference.setIdentifier("http://host/abc/wkj"); // must not produce NPE + + reference = new Reference(); + reference.setPath("loc/alhost"); // must not produce NPE + + reference = new Reference(); + reference.setProtocol(Protocol.HTTPS); // must not produce NPE + + reference = new Reference(); + reference.setQuery("a=b&c=&g=1"); // must not produce NPE + + reference = new Reference(); + reference.setRelativePart("http://localhost"); // must not produce NPE + + reference = new Reference(); + reference.setScheme("skjf"); // must not produce NPE + + reference = new Reference(); + reference.setSchemeSpecificPart("host/afjhsd"); // must not produce NPE + + reference = new Reference(); + final List segments = new ArrayList(); + segments.add("skhf"); + segments.add("sgdfg"); + segments.add("xiz"); + reference.setSegments(segments); // must not produce NPE + } + + /** + * Equality tests. + */ + @Test + public void testEquals() { + final Reference ref1 = getDefaultReference(); + final Reference ref2 = getDefaultReference(); + assertEquals(ref1, ref2); + assertTrue(ref1.equals(ref2)); + } + + @Test + public void testGetLastSegment() { + Reference reference = new Reference("http://hostname"); + assertNull(reference.getLastSegment()); + + reference = new Reference("http://hostname/"); + assertNull(reference.getLastSegment()); + + reference = new Reference("http://hostname/abc"); + assertEquals("abc", reference.getLastSegment()); + + reference = new Reference("http://hostname/abc/"); + assertEquals("abc", reference.getLastSegment()); + + reference = new Reference("http://hostname/123/abc/"); + assertEquals("abc", reference.getLastSegment()); + + reference = new Reference("http://hostname/123/abc"); + assertEquals("abc", reference.getLastSegment()); + } + + /** + * Test hostname getting/setting. + */ + @Test + public void testHostName() { + final Reference ref = getReference(); + String host = "restlet.org"; + ref.setHostDomain(host); + assertEquals(host, ref.getHostDomain()); + host = "restlet.org"; + ref.setHostDomain(host); + assertEquals(host, ref.getHostDomain()); + Reference ref2 = new Reference("http://[::1]:8182"); + assertEquals("[::1]", ref2.getHostDomain()); + } + + @Test + public void testMatrix() { + final Reference ref1 = new Reference( + "http://domain.tld/whatever/a=1;b=2;c=4?x=a&y=b"); + final Reference ref2 = new Reference( + "http://domain.tld/whatever/a=1/foo;b=2;c=4;d?x=a&y=b"); + final Reference ref3 = new Reference( + "http://domain.tld/whatever/a=1;b=2;c=4/foo?x=a&y=b"); + + assertTrue(ref1.hasMatrix()); + assertTrue(ref2.hasMatrix()); + assertFalse(ref3.hasMatrix()); + + assertEquals("b=2;c=4", ref1.getMatrix()); + assertEquals("b=2;c=4;d", ref2.getMatrix()); + + final Form form1 = ref1.getMatrixAsForm(); + assertEquals("2", form1.getFirstValue("b")); + assertEquals("4", form1.getFirstValue("c")); + + final Form form2 = ref1.getMatrixAsForm(); + assertEquals("2", form2.getFirstValue("b")); + assertEquals("4", form2.getFirstValue("c")); + assertNull(form2.getFirstValue("d")); + + final Form newForm = new Form(); + newForm.add("a", "1"); + newForm.add("b", "2"); + newForm.add("c", "4"); + assertEquals("a=1;b=2;c=4", newForm.getMatrixString()); + } + + @Test + public void testOriginalRef() { + Reference ref = new Reference("http://localhost/test"); + Series
headers = new Series<>(Header.class); + headers.add(HeaderConstants.HEADER_X_FORWARDED_PROTO, "HTTPS"); + headers.add(HeaderConstants.HEADER_X_FORWARDED_PORT, "123"); + + Reference originalRef = ReferenceUtils.getOriginalRef(ref, headers); + assertEquals(originalRef.getSchemeProtocol(), Protocol.HTTPS); + assertEquals(originalRef.getHostPort(), 123); + } + + /** + * Test the computation of parent references, for absolute and relative + * URIs. + */ + @Test + public void testParentRef() { + Reference baseRef = new Reference("http://test.com/foo/bar"); + Reference parentRef = baseRef.getParentRef(); + assertEquals("http://test.com/foo/", parentRef.toString()); + + baseRef = new Reference("/foo/bar"); + parentRef = baseRef.getParentRef(); + assertEquals("/foo/", parentRef.toString()); + } + + /** + * Tests the URI parsing. + */ + @Test + public void testParsing() { + final String base = "http://a/b/c/d;p?q"; + + final String uri01 = "g:h"; + final String uri02 = "g"; + final String uri03 = "./g"; + final String uri04 = "g/"; + final String uri05 = "/g"; + final String uri06 = "//g"; + final String uri07 = "?y"; + final String uri08 = "g?y"; + final String uri09 = "#s"; + final String uri10 = "g#s"; + final String uri11 = "g?y#s"; + final String uri12 = ";x"; + final String uri13 = "g;x"; + final String uri14 = "g;x?y#s"; + final String uri15 = ""; + final String uri16 = "."; + final String uri17 = "./"; + final String uri18 = ".."; + final String uri19 = "../"; + final String uri20 = "../g"; + final String uri21 = "../.."; + final String uri22 = "../../"; + final String uri23 = "../../g"; + final String uri24 = "../../../g"; + final String uri25 = "../../../../g"; + final String uri26 = "/./g"; + final String uri27 = "/../g"; + final String uri28 = "g."; + final String uri29 = ".g"; + final String uri30 = "g.."; + final String uri31 = "..g"; + final String uri32 = "./../g"; + final String uri33 = "./g/."; + final String uri34 = "g/./h"; + final String uri35 = "g/../h"; + final String uri36 = "g;x=1/./y"; + final String uri37 = "g;x=1/../y"; + + final String uri101 = "g:h"; + final String uri102 = "http://a/b/c/g"; + final String uri103 = "http://a/b/c/g"; + final String uri104 = "http://a/b/c/g/"; + final String uri105 = "http://a/g"; + final String uri106 = "http://g"; + final String uri107 = "http://a/b/c/d;p?y"; + final String uri108 = "http://a/b/c/g?y"; + final String uri109 = "http://a/b/c/d;p?q#s"; + final String uri110 = "http://a/b/c/g#s"; + final String uri111 = "http://a/b/c/g?y#s"; + final String uri112 = "http://a/b/c/;x"; + final String uri113 = "http://a/b/c/g;x"; + final String uri114 = "http://a/b/c/g;x?y#s"; + final String uri115 = "http://a/b/c/d;p?q"; + final String uri116 = "http://a/b/c/"; + final String uri117 = "http://a/b/c/"; + final String uri118 = "http://a/b/"; + final String uri119 = "http://a/b/"; + final String uri120 = "http://a/b/g"; + final String uri121 = "http://a/"; + final String uri122 = "http://a/"; + final String uri123 = "http://a/g"; + final String uri124 = "http://a/g"; + final String uri125 = "http://a/g"; + final String uri126 = "http://a/g"; + final String uri127 = "http://a/g"; + final String uri128 = "http://a/b/c/g."; + final String uri129 = "http://a/b/c/.g"; + final String uri130 = "http://a/b/c/g.."; + final String uri131 = "http://a/b/c/..g"; + final String uri132 = "http://a/b/g"; + final String uri133 = "http://a/b/c/g/"; + final String uri134 = "http://a/b/c/g/h"; + final String uri135 = "http://a/b/c/h"; + final String uri136 = "http://a/b/c/g;x=1/y"; + final String uri137 = "http://a/b/c/y"; + + final Reference host = new Reference("http://host.com"); + final Reference slashdir = new Reference(host, "/dir"); + final Reference dir = new Reference(host, "dir"); + final Reference dirslash = new Reference(host, "dir/"); + final Reference fulldir = new Reference("http://host.com/dir"); + final Reference fulldirsub = new Reference(fulldir, "sub"); + final Reference fulldirslashsub = new Reference(fulldir, "/sub"); + final Reference slashdirsub = new Reference(slashdir, "sub"); + final Reference slashdirslashsub = new Reference(slashdir, "/sub"); + final Reference dirslashsub = new Reference(dirslash, "sub"); + final Reference fullsub = new Reference("http://host.com/dir/sub"); + + // Test the parsing of references into its components + testRef0("foo://example.com:8042/over/there?name=ferret#nose", "foo", + "example.com:8042", "/over/there", "name=ferret", "nose"); + + // TODO support URN + //testRef0("urn:example:animal:ferret:nose", "urn", null, + // "example:animal:ferret:nose", null, null); + testRef0("mailto:fred@example.com", "mailto", null, "fred@example.com", + null, null); + testRef0("foo://info.example.com?fred", "foo", "info.example.com", + null, "fred", null); + testRef0("*", null, null, "*", null, null); + testRef0("http://localhost?query", "http", "localhost", null, "query", + null); + testRef0("http://localhost#?query", "http", "localhost", null, null, + "?query"); + testRef0("http://localhost/?query", "http", "localhost", "/", "query", + null); + testRef0("http://localhost/#?query", "http", "localhost", "/", null, + "?query"); + testRef0("http://localhost/path#frag/ment", "http", "localhost", + "/path", null, "frag/ment"); + testRef0("http://localhost/path?qu/ery", "http", "localhost", "/path", + "qu/ery", null); + + // Test the resolution of relative references + // TODO testRef1(base, uri01, uri101); + testRef1(base, uri02, uri102); + testRef1(base, uri03, uri103); + testRef1(base, uri04, uri104); + testRef1(base, uri05, uri105); + testRef1(base, uri06, uri106); + testRef1(base, uri07, uri107); + testRef1(base, uri08, uri108); + testRef1(base, uri09, uri109); + testRef1(base, uri10, uri110); + testRef1(base, uri11, uri111); + testRef1(base, uri12, uri112); + testRef1(base, uri13, uri113); + testRef1(base, uri14, uri114); + testRef1(base, uri15, uri115); + testRef1(base, uri16, uri116); + testRef1(base, uri17, uri117); + testRef1(base, uri18, uri118); + testRef1(base, uri19, uri119); + testRef1(base, uri20, uri120); + testRef1(base, uri21, uri121); + testRef1(base, uri22, uri122); + testRef1(base, uri23, uri123); + testRef1(base, uri24, uri124); + testRef1(base, uri25, uri125); + testRef1(base, uri26, uri126); + testRef1(base, uri27, uri127); + testRef1(base, uri28, uri128); + testRef1(base, uri29, uri129); + testRef1(base, uri30, uri130); + testRef1(base, uri31, uri131); + testRef1(base, uri32, uri132); + testRef1(base, uri33, uri133); + testRef1(base, uri34, uri134); + testRef1(base, uri35, uri135); + testRef1(base, uri36, uri136); + testRef1(base, uri37, uri137); + + // Test the relativization of absolute references + testRef2(base, uri102, uri02); + testRef2(base, uri104, uri04); + testRef2(base, uri107, uri07); + testRef2(base, uri108, uri08); + testRef2(base, uri109, uri09); + testRef2(base, uri110, uri10); + testRef2(base, uri111, uri11); + testRef2(base, uri112, uri12); + testRef2(base, uri113, uri13); + testRef2(base, uri114, uri14); + testRef2(base, uri116, uri16); + testRef2(base, uri118, uri18); + testRef2(base, uri120, uri20); + testRef2(base, uri121, uri21); + testRef2(base, uri123, uri23); + testRef2(uri104, uri116, uri18); + testRef2(uri104, uri118, uri21); + + // Test the toString method with or without query/fragment + testRef3("http://localhost/path#fragment", true, true, + "http://localhost/path#fragment"); + testRef3("http://localhost/path#fragment", true, false, + "http://localhost/path"); + testRef3("http://localhost/path#fragment", false, true, + "http://localhost/path#fragment"); + testRef3("http://localhost/path#fragment", false, false, + "http://localhost/path"); + + testRef3("http://localhost/path?query", true, true, + "http://localhost/path?query"); + testRef3("http://localhost/path?query", true, false, + "http://localhost/path?query"); + testRef3("http://localhost/path?query", false, true, + "http://localhost/path"); + testRef3("http://localhost/path?query", false, false, + "http://localhost/path"); + + testRef3("http://localhost/path?query#fragment", true, true, + "http://localhost/path?query#fragment"); + testRef3("http://localhost/path?query#fragment", true, false, + "http://localhost/path?query"); + testRef3("http://localhost/path?query#fragment", false, true, + "http://localhost/path#fragment"); + testRef3("http://localhost/path?query#fragment", false, false, + "http://localhost/path"); + + testRef3("http://localhost/path#fragment?query", true, true, + "http://localhost/path#fragment?query"); + testRef3("http://localhost/path#fragment?query", true, false, + "http://localhost/path"); + testRef3("http://localhost/path#fragment?query", false, true, + "http://localhost/path#fragment?query"); + testRef3("http://localhost/path#fragment?query", false, false, + "http://localhost/path"); + + testRef4(host, "http", "host.com", null, "http://host.com", + "http://host.com", "http://host.com", null, null); + testRef4(slashdir, null, null, "/dir", null, "/dir", + "http://host.com/dir", null, "/dir"); + testRef4(dir, null, null, "dir", null, "dir", "http://host.com/dir", + null, "dir"); + testRef4(dirslash, null, null, "dir/", null, "dir/", + "http://host.com/dir/", null, "dir/"); + testRef4(fulldir, "http", "host.com", "/dir", "http://host.com/dir", + "http://host.com/dir", "http://host.com/dir", null, null); + + testRef4(fulldirsub, null, null, "sub", null, "sub", + "http://host.com/sub", null, "sub"); + testRef4(fulldirslashsub, null, null, "/sub", null, "/sub", + "http://host.com/sub", null, "/sub"); + testRef4(slashdirsub, null, null, "sub", null, "sub", + "http://host.com/sub", null, "sub"); + testRef4(slashdirslashsub, null, null, "/sub", null, "/sub", + "http://host.com/sub", null, "/sub"); + testRef4(dirslashsub, null, null, "sub", null, "sub", + "http://host.com/dir/sub", null, "sub"); + testRef4(fullsub, "http", "host.com", "/dir/sub", + "http://host.com/dir/sub", "http://host.com/dir/sub", + "http://host.com/dir/sub", null, null); + } + + /** + * Test port getting/setting. + */ + @ParameterizedTest + @ValueSource(ints = { 8080, 9090 }) + public void testPort(int port) { + Reference ref = getDefaultReference(); + ref.setHostPort(port); + assertEquals(port, ref.getHostPort()); + } + + @Test + public void testPortIPv6() { + Reference ref = new Reference("http://[::1]:8182"); + assertEquals(8182, ref.getHostPort()); + } + + @Test + public void testProtocolConstructors() { + assertEquals("http://restlet.org", new Reference(Protocol.HTTP, + "restlet.org").toString()); + assertEquals("https://restlet.org:8443", new Reference(Protocol.HTTPS, + "restlet.org", 8443).toString()); + + final Reference ref = new Reference(Protocol.HTTP, "restlet.org"); + ref.addQueryParameter("abc", "123"); + assertEquals("http://restlet.org?abc=123", ref.toString()); + } + + @Test + public void testQuery() { + + Reference ref1 = new Reference( + "http://localhost/search?q=anythingelse%"); + String query = ref1.getQuery(); + assertEquals("q=anythingelse%25", query); + + Form queryForm = ref1.getQueryAsForm(); + assertEquals("anythingelse%", queryForm.getFirstValue("q")); + + Form extJsQuery = new Form( + "&_dc=1244741620627&callback=stcCallback1001"); + assertEquals("1244741620627", extJsQuery.getFirstValue("_dc")); + assertEquals("stcCallback1001", extJsQuery.getFirstValue("callback")); + + Reference ref = new Reference("http://localhost/v1/projects/13404"); + ref.addQueryParameter("dyn", "true"); + assertEquals("http://localhost/v1/projects/13404?dyn=true", + ref.toString()); + } + + @Test + public void testQueryWithUri() { + Reference ref = new Reference(new Reference("http://localhost:8111/"), + "http://localhost:8111/contrats/123?srvgwt=localhost:9997"); + assertEquals("contrats/123?srvgwt=localhost:9997", ref.getRelativeRef() + .toString()); + } + + /** + * Tests the parsing of a reference into its components + * + * @param reference + * @param scheme + * @param authority + * @param path + * @param query + * @param fragment + */ + private void testRef0(String reference, String scheme, String authority, + String path, String query, String fragment) { + final Reference ref = new Reference(reference); + assertEquals(scheme, ref.getScheme()); + assertEquals(authority, ref.getAuthority()); + assertEquals(path, ref.getPath()); + assertEquals(query, ref.getQuery()); + assertEquals(fragment, ref.getFragment()); + } + + /** + * Test the resolution of relative references. + * + * @param baseUri + * @param relativeUri + * @param expectedAbsoluteUri + */ + private void testRef1(String baseUri, String relativeUri, + String expectedAbsoluteUri) { + final Reference baseRef = new Reference(baseUri); + final Reference relativeRef = new Reference(baseRef, relativeUri); + final Reference absoluteRef = relativeRef.getTargetRef(); + assertEquals(expectedAbsoluteUri, absoluteRef.toString()); + } + + /** + * Test the relativization of absolute references + * + * @param baseUri + * @param absoluteUri + * @param expectedRelativeUri + */ + private void testRef2(String baseUri, String absoluteUri, + String expectedRelativeUri) { + final Reference baseRef = new Reference(baseUri); + final Reference absoluteRef = new Reference(absoluteUri); + final Reference relativeRef = absoluteRef.getRelativeRef(baseRef); + assertEquals(expectedRelativeUri, relativeRef.toString()); + } + + /** + * Test the toString method with or without query/fragment + * + * @param reference + * @param query + * @param fragment + * @param toString + */ + private void testRef3(String reference, boolean query, boolean fragment, + String toString) { + final Reference ref = new Reference(reference); + assertEquals(ref.toString(query, fragment), toString); + } + + /** + * Test the behaviour of several getters upon a Reference object. + */ + private void testRef4(Reference reference, String scheme, String authority, + String path, String remainingPart, String toString, + String targetRef, String query, String relativePart) { + assertEquals(reference.getScheme(), scheme); + assertEquals(reference.getAuthority(), authority); + assertEquals(reference.getPath(), path); + assertEquals(reference.getRemainingPart(), remainingPart); + assertEquals(reference.toString(), toString); + assertEquals(reference.getTargetRef().toString(), targetRef); + assertEquals(reference.getQuery(), query); + assertEquals(reference.getRelativePart(), relativePart); + } + + @Test + public void testRiap() { + Reference baseRef = new Reference("riap://component/exist/db/"); + Reference ref = new Reference(baseRef, "something.xq"); + assertEquals("riap://component/exist/db/something.xq", ref + .getTargetRef().toString()); + } + + /** + * Test scheme getting/setting. + */ + @Test + public void testScheme() { + final Reference ref = getDefaultReference(); + assertEquals(DEFAULT_SCHEME, ref.getScheme()); + final String scheme = "https"; + ref.setScheme(scheme); + assertEquals(scheme, ref.getScheme()); + ref.setScheme(DEFAULT_SCHEME); + assertEquals(DEFAULT_SCHEME, ref.getScheme()); + } + + /** + * Test scheme specific part getting/setting. + */ + @Test + public void testSchemeSpecificPart() { + final Reference ref = getDefaultReference(); + String part = "//restlet.org"; + assertEquals(part, ref.getSchemeSpecificPart()); + part = "//restlet.net"; + ref.setSchemeSpecificPart(part); + assertEquals(part, ref.getSchemeSpecificPart()); + } + + /** + * Test setting of the last segment. + */ + @Test + public void testSetLastSegment() { + Reference ref = new Reference("http://localhost:1234"); + ref.addSegment("test"); + assertEquals("http://localhost:1234/test", ref.toString()); + + ref.setLastSegment("last"); + assertEquals("http://localhost:1234/last", ref.toString()); + + ref = new Reference("http://localhost:1234"); + ref.setLastSegment("last"); + assertEquals("http://localhost:1234/last", ref.toString()); + + ref.setLastSegment("test"); + assertEquals("http://localhost:1234/test", ref.toString()); + + ref.addSegment("last"); + assertEquals("http://localhost:1234/test/last", ref.toString()); + } + + // TODO @Test + public void testTargetRef() { + Reference ref = new Reference( + "http://twitter.com?status=RT @gamasutra: Devil May Cry : Born Again http://www.gamasutra.com/view/feature/177267/"); + Reference targetRef = new Reference( + new Reference( + "http://www.gamasutra.com/view/feature/177267/devil_may_cry_born_again.php"), + ref).getTargetRef(); + assertEquals( + "http://twitter.com?status=RT%20@gamasutra:%20%20Devil%20May%20Cry%20:%20Born%20Again%20http:?status=RT%20@gamasutra:%20%20Devil%20May%20Cry%20:%20Born%20Again%20http://www.gamasutra.com/view/feature/177267/", + targetRef.toString()); + } + + /** + * Test references that are unequal. + */ + @Test + public void testUnEquals() throws Exception { + final String uri1 = "http://restlet.org/"; + final String uri2 = "http://restlet.net/"; + final Reference ref1 = new Reference(uri1); + final Reference ref2 = new Reference(uri2); + assertFalse(ref1.equals(ref2)); + assertFalse(ref1.equals(null)); + } + + @Test + public void testUserinfo() { + final Reference reference = new Reference("http://localhost:81"); + // This format is depre. however we may prevent failures. + reference.setUserInfo("login:password"); + assertEquals("login:password@localhost:81", reference.getAuthority()); + assertEquals("localhost", reference.getHostDomain()); + assertEquals(81, reference.getHostPort()); + assertEquals("login:password", reference.getUserInfo()); + + reference.setHostDomain("[::1]"); + assertEquals("login:password@[::1]:81", reference.getAuthority()); + assertEquals("[::1]", reference.getHostDomain()); + assertEquals(81, reference.getHostPort()); + assertEquals("login:password", reference.getUserInfo()); + + reference.setHostDomain("www.example.com"); + assertEquals("login:password@www.example.com:81", + reference.getAuthority()); + assertEquals("www.example.com", reference.getHostDomain()); + assertEquals(81, reference.getHostPort()); + assertEquals("login:password", reference.getUserInfo()); + + reference.setHostPort(82); + assertEquals("login:password@www.example.com:82", + reference.getAuthority()); + assertEquals("www.example.com", reference.getHostDomain()); + assertEquals(82, reference.getHostPort()); + assertEquals("login:password", reference.getUserInfo()); + + reference.setUserInfo("login"); + assertEquals("login@www.example.com:82", reference.getAuthority()); + assertEquals("www.example.com", reference.getHostDomain()); + assertEquals(82, reference.getHostPort()); + assertEquals("login", reference.getUserInfo()); + } + + @Test + public void testValidity() { + String uri = "http ://domain.tld/whatever/"; + Reference ref = new Reference(uri); + assertEquals("http%20://domain.tld/whatever/", ref.toString()); + + uri = "file:///C|/wherever\\whatever.swf"; + ref = new Reference(uri); + assertEquals("file:///C%7C/wherever%5Cwhatever.swf", ref.toString()); + } + @Test public void shouldFailWhenParsingMultiPortReference() { Assertions.assertThrows(IllegalArgumentException.class, () -> new Reference("http://192.168.1.1:1111:2222/")); @@ -27,13 +748,19 @@ public void shouldFailWhenParsingMultiPortReference() { "http://[192.168.0.1]vulndetector.com/", "http://[normal.com@]vulndetector.com/", "http://normal.com[user@vulndetector].com/", - "http://normal.com[@]vulndetector.com/"}) + "http://normal.com[@]vulndetector.com/" + }) public void shouldFailWhenParsingIncorrectHosts(String reference) { Assertions.assertThrows(IllegalArgumentException.class, () -> new Reference(reference)); } @ParameterizedTest - @ValueSource(strings = {"http://[0:0::vulndetector.com]:80", "http://[2001:db8::vulndetector.com]"}) + @ValueSource(strings = { + "https://[1:2:3:4:5:6:7:8:9]", + "https://[1::1::1]", + "https://[1:2:3:]", + "https://[ffff::127.0.0.4000]", + "http://[0:0::vulndetector.com]:80", "http://[2001:db8::vulndetector.com]"}) public void shouldFailWhenParsingIncorrectIPv6Hosts(String reference) { Assertions.assertThrows(IllegalArgumentException.class, () -> new Reference(reference)); }