diff --git a/crux-uri/src/main/java/com/fizzed/crux/uri/MutableUri.java b/crux-uri/src/main/java/com/fizzed/crux/uri/MutableUri.java index 0b0f9a9..272dd12 100644 --- a/crux-uri/src/main/java/com/fizzed/crux/uri/MutableUri.java +++ b/crux-uri/src/main/java/com/fizzed/crux/uri/MutableUri.java @@ -27,6 +27,9 @@ * Helps to build a URI. Why another one? Unlike Java's URI this one allows * modification after it's been created. This one has a simple fluent style * to help build uris. + * + * https://en.wikipedia.org/wiki/Uniform_Resource_Identifier + * URI = scheme ":" ["//" authority] path ["?" query] ["#" fragment] */ public class MutableUri extends Uri { @@ -35,17 +38,18 @@ public MutableUri() { } public MutableUri(String uri) { - this(URI.create(uri)); + // use our new string parser + this.parse(uri); } public MutableUri(URI uri) { - this.apply(uri); + this.parse(uri); } @SuppressWarnings("OverridableMethodCallInConstructor") public MutableUri(Uri uri) { this.scheme = uri.scheme; - this.schemeSpecificPart = uri.schemeSpecificPart; + this.hasAuthority = uri.hasAuthority; this.userInfo = uri.userInfo; this.host = uri.host; this.port = uri.port; @@ -60,7 +64,7 @@ public Uri toImmutable() { } public Uri immutable() { - return new Uri(this.scheme, this.schemeSpecificPart, this.userInfo, this.host, this.port, this.rels, this.query, this.fragment); + return new Uri(this.scheme, this.hasAuthority, this.userInfo, this.host, this.port, this.rels, this.query, this.fragment); } public Uri toUri() { @@ -98,8 +102,7 @@ public MutableUri port(Integer port) { * path is "/a/b" and you call this method with "c" then the final * path would be "/a/b/c". * @return This instance - * @see #relPath(java.lang.String) - * @see #rel(java.lang.String[]) + * @see #rel(java.lang.String[]) */ public MutableUri path(String path) { // clear it? @@ -317,74 +320,181 @@ public MutableUri fragment(String fragment) { this.fragment = fragment; return this; } - - private MutableUri apply(URI uri) { - if (uri.getScheme() != null) { - this.scheme = uri.getScheme(); - } - - if (uri.getSchemeSpecificPart() != null - && uri.getSchemeSpecificPart().length() > 0 - && !uri.getSchemeSpecificPart().startsWith("//")) { - this.schemeSpecificPart = uri.getSchemeSpecificPart(); - } - - if (uri.getRawUserInfo() != null) { - this.userInfo = urlDecode(uri.getRawUserInfo()); - } - - if (uri.getHost() != null) { - this.host = uri.getHost(); - } - - if (uri.getPort() >= 0) { - this.port = uri.getPort(); + + private MutableUri parse(URI uri) { + // parsing a URI as a string is WAY more reliable + return this.parse(uri.toString()); + } + + private MutableUri parse(String uri) { + // https://en.wikipedia.org/wiki/Uniform_Resource_Identifier + // URI = scheme ":" ["//" authority] path ["?" query] ["#" fragment] + // authority = [userinfo "@"] host [":" port] + + // we will implement our own parsing routine + int pos = 0; + int len = uri.length(); + + // scheme is everything to start up to the "://" such as ldap://[2001:db8::7]/c=GB?objectClass?one + boolean maybeHasAuthority = false; + + int schemeEndPos = uri.indexOf("://", pos); + if (schemeEndPos > 0) { + this.scheme = uri.substring(pos, schemeEndPos); + pos = schemeEndPos + 3; // skip over :// on next part to parse + maybeHasAuthority = true; + this.hasAuthority = true; + } else { + // this could be a scheme:path scenario such as tel:+1-816-555-1212 + schemeEndPos = uri.indexOf(":", pos); + if (schemeEndPos > 0) { + this.scheme = uri.substring(pos, schemeEndPos); + pos = schemeEndPos + 1; // skip over : on next part to parse + // the rest is apparently always a path + } else { + // there isn't any scheme, the rest must be a path + // we will simply continue on parsing it as a path + } } - // if the uri contains reserved, punctuation, and other chars in the - // host section of the uri, then those are actually set as the authority - // we're going to make a design decision to try and parse it as the host - // and potentially the port - if (uri.getHost() == null && uri.getUserInfo() == null && uri.getAuthority() != null) { - String authority = uri.getAuthority(); - int portIndex = authority.indexOf(':'); - if (portIndex >= 0) { - this.host = authority.substring(0, portIndex); - this.port = Integer.valueOf(authority.substring(portIndex+1)); + boolean maybeHasPath = true; + boolean maybeHasQuery = true; + + if (maybeHasAuthority) { + int authorityEndPos = uri.indexOf("/", pos); + if (authorityEndPos < 0) { + // definitely no path + maybeHasPath = false; + // was it instead a ? + authorityEndPos = uri.indexOf("?", pos); + if (authorityEndPos < 0) { + maybeHasQuery = false; + // was it instead a # + authorityEndPos = uri.indexOf("#", pos); + if (authorityEndPos < 0) { + // the rest of the uri must be the authority + // e.g. https://john.doe@www.example.com:123 + authorityEndPos = len; + } + } + } + + // any userinfo? + int userInfoPos = uri.indexOf("@", pos); + if (userInfoPos > 0 && userInfoPos <= authorityEndPos) { // in case an @ symbol is AFTER the authority + this.userInfo = urlDecode(uri.substring(pos, userInfoPos)); + pos = userInfoPos + 1; // skip @ char + } + + // we're left with a potential host[:port] + int hostEndPos = uri.indexOf(":", pos); + + // does the host start with an "[" indicating its an ipv6 address? + if (pos < len && uri.charAt(pos) == '[') { + // find the next matching ] + int closingSquarePos = uri.indexOf(']', pos); + if (closingSquarePos < 0) { + throw new IllegalArgumentException("Invalid IPv6 host (no matching ] char)"); + } + hostEndPos = uri.indexOf(":", closingSquarePos); + } + + // was no port found? + if (hostEndPos < 0 || hostEndPos >= authorityEndPos) { + // host makes up rest of authority + hostEndPos = authorityEndPos; + this.host = trimAndBlankToNull(uri.substring(pos, hostEndPos)); } else { - this.host = authority; + // a port was found and we should parse it and the host + this.host = trimAndBlankToNull(uri.substring(pos, hostEndPos)); + pos = hostEndPos + 1; // skip over : char + String portStr = uri.substring(pos, authorityEndPos); + try { + this.port = Integer.parseInt(portStr); + } catch (Exception e) { + throw new IllegalArgumentException("port " + portStr + " was not an integer in uri " + uri); + } } + + pos = authorityEndPos; } - - String rawPath = uri.getRawPath(); - if (rawPath != null && rawPath.length() > 0) { - this.path(uri.getRawPath()); + + // we may be done at this point + if (pos >= len) { + return this; } - - if (uri.getRawQuery() != null) { - // get rid of map to rebuild - this.query = null; - - // split on ampersand... - String[] pairs = uri.getRawQuery().split("&"); - for (String pair : pairs) { - String[] nv = pair.split("="); - switch (nv.length) { - case 1: - this.query(urlDecode(nv[0]), null); - break; - case 2: - this.query(urlDecode(nv[0]), urlDecode(nv[1])); - break; - default: - throw new IllegalArgumentException("Name value pair [" + pair + "] in query [" + uri.getRawQuery() + "] missing = char"); + + if (maybeHasPath) { + // we're left with the path, query, fragment + int pathEndPos = uri.indexOf("?", pos); + if (pathEndPos < 0) { + // we know there is no query + maybeHasQuery = false; + // we may need to read to the fragment + pathEndPos = uri.indexOf("#", pos); + if (pathEndPos < 0) { + // the rest of the uri is path + pathEndPos = len; } } + + final String rawPath = uri.substring(pos, pathEndPos); + this.path(rawPath); + pos = pathEndPos; + + // we may be done at this point + if (pos >= len) { + return this; + } } - - if (uri.getRawFragment() != null) { - this.fragment = urlDecode(uri.getRawFragment()); + + if (maybeHasQuery) { + // there must have been a ?, we'll skip over it + pos++; + + int queryEndPos = uri.indexOf("#", pos); + if (queryEndPos < 0) { + // the rest of the uri is query + queryEndPos = len; + } + + final String rawQuery = uri.substring(pos, queryEndPos); + // parse raw query now + { + // get rid of map to rebuild + this.query = null; + + // split on ampersand... + String[] pairs = rawQuery.split("&"); + for (String pair : pairs) { + String[] nv = pair.split("="); + switch (nv.length) { + case 1: + this.query(urlDecode(nv[0]), null); + break; + case 2: + this.query(urlDecode(nv[0]), urlDecode(nv[1])); + break; + default: + throw new IllegalArgumentException("Name value pair [" + pair + "] in query [" + rawQuery + "] missing = char"); + } + } + } + + pos = queryEndPos; + + // we may be done at this point + if (pos >= len) { + return this; + } } + + // there must have been a #, we'll skip over it + pos++; + + // remaining part is now fragment + String rawFragment = uri.substring(pos); + this.fragment = urlDecode(rawFragment); return this; } @@ -453,4 +563,15 @@ static void splitPath(String path, List paths, boolean decode) { paths.add(""); } } -} + + static String trimAndBlankToNull(String s) { + if (s != null) { + s = s.trim(); + if (s.length() == 0) { + return null; + } + } + return s; + } + +} \ No newline at end of file diff --git a/crux-uri/src/main/java/com/fizzed/crux/uri/Uri.java b/crux-uri/src/main/java/com/fizzed/crux/uri/Uri.java index 92b360b..50d01af 100644 --- a/crux-uri/src/main/java/com/fizzed/crux/uri/Uri.java +++ b/crux-uri/src/main/java/com/fizzed/crux/uri/Uri.java @@ -49,7 +49,7 @@ public class Uri { protected String scheme; - protected String schemeSpecificPart; + protected boolean hasAuthority; // if the uri had a :// after the scheme protected String userInfo; protected String host; protected Integer port; @@ -71,12 +71,12 @@ public Uri(URI uri) { } public Uri(Uri uri) { - this(uri.scheme, uri.schemeSpecificPart, uri.userInfo, uri.host, uri.port, uri.rels, uri.query, uri.fragment); + this(uri.scheme, uri.hasAuthority, uri.userInfo, uri.host, uri.port, uri.rels, uri.query, uri.fragment); } - protected Uri(String scheme, String schemeSpecificPart, String userInfo, String host, Integer port, List rels, Map> query, String fragment) { + protected Uri(String scheme, boolean hasAuthority, String userInfo, String host, Integer port, List rels, Map> query, String fragment) { this.scheme = scheme; - this.schemeSpecificPart = schemeSpecificPart; + this.hasAuthority = hasAuthority; this.userInfo = userInfo; this.host = host; this.port = port; @@ -101,9 +101,9 @@ public URI toURI() { public String getScheme() { return this.scheme; } - - public String getSchemeSpecificPart() { - return this.schemeSpecificPart; + + public boolean hasAuthority() { + return this.hasAuthority; } public String getUserInfo() { @@ -298,8 +298,7 @@ static public Uri navigate(String maybeUrl) { // with a schemeSpecificPart -- we'll detect this unique case by // determining if the scheme contains a period and assume the user // really meant it as a domain name - if (maybeUri.getScheme() != null && maybeUri.getSchemeSpecificPart() != null - && maybeUri.getHost() == null) { + if (maybeUri.getScheme() != null && maybeUri.getHost() == null) { maybeUri = new MutableUri("http://" + maybeUrl); } @@ -345,8 +344,11 @@ protected String encodedPath() { for (int i = 0; i < this.rels.size(); i++) { String path = this.rels.get(i); - s.append('/'); - s.append(MutableUri.urlEncode(path)); + // special case: if this is the first rel and only if there was authority + if (i > 0 || this.hasAuthority || this.host != null || this.scheme == null || this.rels.size() > 1 || path.equals("")) { + s.append('/'); + } + s.append(MutableUri.urlEncodePath(path)); } return s.toString(); @@ -377,68 +379,63 @@ protected String encodedQueryString() { @Override public String toString() { - // Note this code is essentially a copy of 'java.net.URI.defineString', - // which is private. We cannot use the 'new URI( scheme, userInfo, ... )' or - // 'new URI( scheme, authority, ... )' constructors because they double - // urlEncode the query string using 'java.net.URI.quote' - StringBuilder uri = new StringBuilder(); - boolean afterScheme = false; + final StringBuilder sb = new StringBuilder(); if (this.scheme != null) { - uri.append(this.scheme); - if (this.schemeSpecificPart != null) { - uri.append(':'); - uri.append(this.schemeSpecificPart); - } - } else { - afterScheme = true; + sb.append(this.scheme); + } + + // we make a lot of decisions on the authority, we keep it local so we dont' change it's value + boolean _hasAuthority = this.hasAuthority; + + // do we need to append the "://" now? + if (_hasAuthority) { + sb.append("://"); } if (this.host != null) { - if (!afterScheme) { - uri.append(':'); - afterScheme = true; + if (!_hasAuthority) { + // the authority must have been wrong? + sb.append("://"); + _hasAuthority = true; } - - uri.append("//"); - + if (this.userInfo != null) { - uri.append(MutableUri.urlEncode(this.userInfo)); - uri.append('@'); + sb.append(MutableUri.urlEncode(this.userInfo)); + sb.append('@'); } - uri.append(this.host); + sb.append(this.host); if (this.port != null) { - uri.append(':'); - uri.append(this.port); + sb.append(':'); + sb.append(this.port); } - } if (this.rels != null && !this.rels.isEmpty()) { - if (!afterScheme) { - uri.append(':'); - afterScheme = true; + if (!_hasAuthority && this.scheme != null) { + sb.append(':'); + _hasAuthority = true; } - uri.append(this.encodedPath()); + sb.append(this.encodedPath()); } if (this.query != null && !this.query.isEmpty()) { - if (!afterScheme) { - uri.append(':'); - afterScheme = true; + if (!_hasAuthority && this.scheme != null && this.rels == null) { + sb.append(':'); + _hasAuthority = true; } - uri.append('?'); - uri.append(this.encodedQueryString()); + sb.append('?'); + sb.append(this.encodedQueryString()); } if (this.fragment != null) { - uri.append('#'); - uri.append(MutableUri.urlEncode(this.fragment)); + sb.append('#'); + sb.append(MutableUri.urlEncode(this.fragment)); } - return uri.toString(); + return sb.toString(); } @Override @@ -473,6 +470,18 @@ static String urlEncode(String value) { throw new IllegalArgumentException(e.getMessage()); } } + + static String urlEncodePath(String value) { + try { + String encoded = java.net.URLEncoder.encode(value, "UTF-8"); + // hack: we don't want to encode @, : symbols + encoded = encoded.replace("%40", "@"); + encoded = encoded.replace("%3A", ":"); + return encoded; + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException(e.getMessage()); + } + } static String urlDecode(String value) { try { diff --git a/crux-uri/src/test/java/com/fizzed/crux/uri/MutableUriTest.java b/crux-uri/src/test/java/com/fizzed/crux/uri/MutableUriTest.java index 5bef793..5166d48 100644 --- a/crux-uri/src/test/java/com/fizzed/crux/uri/MutableUriTest.java +++ b/crux-uri/src/test/java/com/fizzed/crux/uri/MutableUriTest.java @@ -23,9 +23,12 @@ import java.util.List; import java.util.Map; import java.util.Optional; + +import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.junit.Assert.fail; @@ -38,66 +41,64 @@ public void splitPath() { List paths; paths = MutableUri.splitPath("", true); - assertThat(paths, is(Arrays.asList(""))); + assertThat(paths, is(asList(""))); paths = MutableUri.splitPath("/", true); - assertThat(paths, is(Arrays.asList("", ""))); + assertThat(paths, is(asList("", ""))); paths = MutableUri.splitPath("test", true); - assertThat(paths, is(Arrays.asList("test"))); + assertThat(paths, is(asList("test"))); paths = MutableUri.splitPath("/test", true); - assertThat(paths, is(Arrays.asList("", "test"))); + assertThat(paths, is(asList("", "test"))); paths = MutableUri.splitPath("/test/", true); - assertThat(paths, is(Arrays.asList("", "test", ""))); + assertThat(paths, is(asList("", "test", ""))); paths = MutableUri.splitPath("/test/t", true); - assertThat(paths, is(Arrays.asList("", "test", "t"))); + assertThat(paths, is(asList("", "test", "t"))); } @Test public void normalizeRels() { List rels; - rels = MutableUri.normalizeRels(Arrays.asList()); - assertThat(rels, is(Arrays.asList())); + rels = MutableUri.normalizeRels(asList()); + assertThat(rels, is(asList())); - rels = MutableUri.normalizeRels(Arrays.asList("a")); - assertThat(rels, is(Arrays.asList("a"))); + rels = MutableUri.normalizeRels(asList("a")); + assertThat(rels, is(asList("a"))); - rels = MutableUri.normalizeRels(Arrays.asList("a", "b")); - assertThat(rels, is(Arrays.asList("a", "b"))); + rels = MutableUri.normalizeRels(asList("a", "b")); + assertThat(rels, is(asList("a", "b"))); - rels = MutableUri.normalizeRels(Arrays.asList("a", ".", "b")); - assertThat(rels, is(Arrays.asList("a", "b"))); + rels = MutableUri.normalizeRels(asList("a", ".", "b")); + assertThat(rels, is(asList("a", "b"))); - rels = MutableUri.normalizeRels(Arrays.asList("a", "..", "b")); - assertThat(rels, is(Arrays.asList("b"))); + rels = MutableUri.normalizeRels(asList("a", "..", "b")); + assertThat(rels, is(asList("b"))); - rels = MutableUri.normalizeRels(Arrays.asList("..", "..", "b")); - assertThat(rels, is(Arrays.asList("b"))); + rels = MutableUri.normalizeRels(asList("..", "..", "b")); + assertThat(rels, is(asList("b"))); } @Test public void parse() { MutableUri uri; - + uri = new MutableUri("http://localhost"); - + assertThat(uri.getScheme(), is("http")); - assertThat(uri.getSchemeSpecificPart(), is(nullValue())); assertThat(uri.getUserInfo(), is(nullValue())); assertThat(uri.getHost(), is("localhost")); assertThat(uri.getPort(), is(nullValue())); assertThat(uri.getPath(), is(nullValue())); assertThat(uri.getQuery(), is(nullValue())); assertThat(uri.getFragment(), is(nullValue())); - + uri = new MutableUri("http://localhost:8080?a=1&b=2"); - + assertThat(uri.getScheme(), is("http")); - assertThat(uri.getSchemeSpecificPart(), is(nullValue())); assertThat(uri.getUserInfo(), is(nullValue())); assertThat(uri.getHost(), is("localhost")); assertThat(uri.getPort(), is(8080)); @@ -105,11 +106,10 @@ public void parse() { assertThat(uri.getQueryFirst("a"), is("1")); assertThat(uri.getQueryFirst("b"), is("2")); assertThat(uri.getFragment(), is(nullValue())); - + uri = new MutableUri("http://localhost:8080/?a=1&b=2"); - + assertThat(uri.getScheme(), is("http")); - assertThat(uri.getSchemeSpecificPart(), is(nullValue())); assertThat(uri.getUserInfo(), is(nullValue())); assertThat(uri.getHost(), is("localhost")); assertThat(uri.getPort(), is(8080)); @@ -117,11 +117,10 @@ public void parse() { assertThat(uri.getQueryFirst("a"), is("1")); assertThat(uri.getQueryFirst("b"), is("2")); assertThat(uri.getFragment(), is(nullValue())); - + uri = new MutableUri("http://localhost:8080/?a=1&b=2#frag"); - + assertThat(uri.getScheme(), is("http")); - assertThat(uri.getSchemeSpecificPart(), is(nullValue())); assertThat(uri.getUserInfo(), is(nullValue())); assertThat(uri.getHost(), is("localhost")); assertThat(uri.getPort(), is(8080)); @@ -129,11 +128,10 @@ public void parse() { assertThat(uri.getQueryFirst("a"), is("1")); assertThat(uri.getQueryFirst("b"), is("2")); assertThat(uri.getFragment(), is("frag")); - + uri = new MutableUri("http://user1@localhost:8080/this/is/a/path?a=1&b=2#frag"); - + assertThat(uri.getScheme(), is("http")); - assertThat(uri.getSchemeSpecificPart(), is(nullValue())); assertThat(uri.getUserInfo(), is("user1")); assertThat(uri.getHost(), is("localhost")); assertThat(uri.getPort(), is(8080)); @@ -141,20 +139,195 @@ public void parse() { assertThat(uri.getQueryFirst("a"), is("1")); assertThat(uri.getQueryFirst("b"), is("2")); assertThat(uri.getFragment(), is("frag")); - + uri = new MutableUri("http://user1@localhost:8080/this/is/a/path?a=1&a=2&b=2&c#frag"); - + assertThat(uri.getScheme(), is("http")); - assertThat(uri.getSchemeSpecificPart(), is(nullValue())); assertThat(uri.getUserInfo(), is("user1")); assertThat(uri.getHost(), is("localhost")); assertThat(uri.getPort(), is(8080)); assertThat(uri.getPath(), is("/this/is/a/path")); assertThat(uri.getQueryFirst("a"), is("1")); - assertThat(uri.getQueryAll("a"), is(Arrays.asList("1", "2"))); + assertThat(uri.getQueryAll("a"), is(asList("1", "2"))); assertThat(uri.getQueryFirst("b"), is("2")); assertThat(uri.getQuery(), hasKey("c")); assertThat(uri.getFragment(), is("frag")); + + // only a fragment, no query + uri = new MutableUri("http://user1@localhost:8080#frag"); + + assertThat(uri.getScheme(), is("http")); + assertThat(uri.getUserInfo(), is("user1")); + assertThat(uri.getHost(), is("localhost")); + assertThat(uri.getPort(), is(8080)); + assertThat(uri.getPath(), is(nullValue())); + assertThat(uri.getQuery(), is(nullValue())); + assertThat(uri.getFragment(), is("frag")); + } + + @Test + public void parseIpAddress() { + MutableUri uri; + + uri = new MutableUri("ws://127.0.0.1"); + + assertThat(uri.getScheme(), is("ws")); + assertThat(uri.getUserInfo(), is(nullValue())); + assertThat(uri.getHost(), is("127.0.0.1")); + assertThat(uri.getPort(), is(nullValue())); + assertThat(uri.getPath(), is(nullValue())); + assertThat(uri.getQuery(), is(nullValue())); + assertThat(uri.getFragment(), is(nullValue())); + assertThat(uri.toString(), is("ws://127.0.0.1")); + + uri = new MutableUri("ws://127.0.0.1:8080"); + + assertThat(uri.getScheme(), is("ws")); + assertThat(uri.getUserInfo(), is(nullValue())); + assertThat(uri.getHost(), is("127.0.0.1")); + assertThat(uri.getPort(), is(8080)); + assertThat(uri.getPath(), is(nullValue())); + assertThat(uri.getQuery(), is(nullValue())); + assertThat(uri.getFragment(), is(nullValue())); + assertThat(uri.toString(), is("ws://127.0.0.1:8080")); + + // ipv6 + uri = new MutableUri("ws://[2001:db8::7]"); + + assertThat(uri.getScheme(), is("ws")); + assertThat(uri.getUserInfo(), is(nullValue())); + assertThat(uri.getHost(), is("[2001:db8::7]")); + assertThat(uri.getPort(), is(nullValue())); + assertThat(uri.getPath(), is(nullValue())); + assertThat(uri.getQuery(), is(nullValue())); + assertThat(uri.getFragment(), is(nullValue())); + assertThat(uri.toString(), is("ws://[2001:db8::7]")); + + // ipv6 + uri = new MutableUri("ws://[2001:db8::7]:8080"); + + assertThat(uri.getScheme(), is("ws")); + assertThat(uri.getUserInfo(), is(nullValue())); + assertThat(uri.getHost(), is("[2001:db8::7]")); + assertThat(uri.getPort(), is(8080)); + assertThat(uri.getPath(), is(nullValue())); + assertThat(uri.getQuery(), is(nullValue())); + assertThat(uri.getFragment(), is(nullValue())); + assertThat(uri.toString(), is("ws://[2001:db8::7]:8080")); + } + + @Test + public void parsePath() { + MutableUri uri; + + uri = new MutableUri("/"); + + assertThat(uri.getScheme(), is(nullValue())); + assertThat(uri.getUserInfo(), is(nullValue())); + assertThat(uri.getHost(), is(nullValue())); + assertThat(uri.getPort(), is(nullValue())); + assertThat(uri.getPath(), is("/")); + assertThat(uri.getQuery(), is(nullValue())); + assertThat(uri.getFragment(), is(nullValue())); + assertThat(uri.toString(), is("/")); + + uri = new MutableUri("/?a=1"); + + assertThat(uri.getScheme(), is(nullValue())); + assertThat(uri.getUserInfo(), is(nullValue())); + assertThat(uri.getHost(), is(nullValue())); + assertThat(uri.getPort(), is(nullValue())); + assertThat(uri.getPath(), is("/")); + assertThat(uri.getQuery(), hasKey("a")); + assertThat(uri.getFragment(), is(nullValue())); + assertThat(uri.toString(), is("/?a=1")); + + uri = new MutableUri("/#frag"); + + assertThat(uri.getScheme(), is(nullValue())); + assertThat(uri.getUserInfo(), is(nullValue())); + assertThat(uri.getHost(), is(nullValue())); + assertThat(uri.getPort(), is(nullValue())); + assertThat(uri.getPath(), is("/")); + assertThat(uri.getQuery(), is(nullValue())); + assertThat(uri.getFragment(), is("frag")); + assertThat(uri.toString(), is("/#frag")); + } + + @Test + public void parseOther() { + MutableUri uri; + + uri = new MutableUri("mailto:John.Doe@example.com"); + + assertThat(uri.getScheme(), is("mailto")); + assertThat(uri.getUserInfo(), is(nullValue())); + assertThat(uri.getHost(), is(nullValue())); + assertThat(uri.getPort(), is(nullValue())); + assertThat(uri.getPath(), is("John.Doe@example.com")); + assertThat(uri.getQuery(), is(nullValue())); + assertThat(uri.getFragment(), is(nullValue())); + assertThat(uri.toString(), is("mailto:John.Doe@example.com")); + + uri = new MutableUri("news:comp.infosystems.www.servers.unix"); + + assertThat(uri.getScheme(), is("news")); + assertThat(uri.getUserInfo(), is(nullValue())); + assertThat(uri.getHost(), is(nullValue())); + assertThat(uri.getPort(), is(nullValue())); + assertThat(uri.getPath(), is("comp.infosystems.www.servers.unix")); + assertThat(uri.getQuery(), is(nullValue())); + assertThat(uri.getFragment(), is(nullValue())); + assertThat(uri.toString(), is("news:comp.infosystems.www.servers.unix")); + + uri = new MutableUri("tel:+1-816-555-1212"); + + assertThat(uri.getScheme(), is("tel")); + assertThat(uri.getUserInfo(), is(nullValue())); + assertThat(uri.getHost(), is(nullValue())); + assertThat(uri.getPort(), is(nullValue())); + assertThat(uri.getPath(), is("+1-816-555-1212")); + assertThat(uri.getQuery(), is(nullValue())); + assertThat(uri.getFragment(), is(nullValue())); + assertThat(uri.toString(), is("tel:+1-816-555-1212")); + } + + @Test + public void parseJdbcUrl() { + MutableUri uri; + + uri = new MutableUri("jdbc:mysql://localhost"); + + assertThat(uri.getScheme(), is("jdbc:mysql")); + assertThat(uri.getUserInfo(), is(nullValue())); + assertThat(uri.getHost(), is("localhost")); + assertThat(uri.getPort(), is(nullValue())); + assertThat(uri.getPath(), is(nullValue())); + assertThat(uri.getQuery(), is(nullValue())); + assertThat(uri.getFragment(), is(nullValue())); + assertThat(uri.toString(), is("jdbc:mysql://localhost")); + + uri = new MutableUri("jdbc:mysql://localhost:8080"); + + assertThat(uri.getScheme(), is("jdbc:mysql")); + assertThat(uri.getUserInfo(), is(nullValue())); + assertThat(uri.getHost(), is("localhost")); + assertThat(uri.getPort(), is(8080)); + assertThat(uri.getPath(), is(nullValue())); + assertThat(uri.getQuery(), is(nullValue())); + assertThat(uri.getFragment(), is(nullValue())); + assertThat(uri.toString(), is("jdbc:mysql://localhost:8080")); + + uri = new MutableUri("jdbc:mysql://localhost:8080/this/is/a/path?a=1&b=2"); + + assertThat(uri.getScheme(), is("jdbc:mysql")); + assertThat(uri.getUserInfo(), is(nullValue())); + assertThat(uri.getHost(), is("localhost")); + assertThat(uri.getPort(), is(8080)); + assertThat(uri.getQuery(), hasEntry("a", asList("1"))); + assertThat(uri.getQuery(), hasEntry("b", asList("2"))); + assertThat(uri.getFragment(), is(nullValue())); + assertThat(uri.toString(), is("jdbc:mysql://localhost:8080/this/is/a/path?a=1&b=2")); } @Test @@ -164,13 +337,13 @@ public void parseFile() { uri = new MutableUri("file:///path/to/file"); assertThat(uri.getScheme(), is("file")); - assertThat(uri.getSchemeSpecificPart(), is(nullValue())); assertThat(uri.getUserInfo(), is(nullValue())); assertThat(uri.getHost(), is(nullValue())); assertThat(uri.getPort(), is(nullValue())); assertThat(uri.getPath(), is("/path/to/file")); assertThat(uri.getQuery(), is(nullValue())); assertThat(uri.getFragment(), is(nullValue())); + assertThat(uri.toString(), is("file:///path/to/file")); } @Test @@ -211,8 +384,8 @@ public void parseUrisWithAuthority() { uri = new MutableUri("local://joe@c$wd:80/path/to/file"); assertThat(uri.getScheme(), is("local")); - assertThat(uri.getUserInfo(), is(nullValue())); - assertThat(uri.getHost(), is("joe@c$wd")); + assertThat(uri.getUserInfo(), is("joe")); + assertThat(uri.getHost(), is("c$wd")); assertThat(uri.getPort(), is(80)); assertThat(uri.getPath(), is("/path/to/file")); assertThat(uri.getQuery(), is(nullValue())); @@ -221,32 +394,29 @@ public void parseUrisWithAuthority() { @Test public void stringify() { - String uri; + MutableUri uri; uri = new MutableUri() .scheme("http") - .host("localhost") - .toString(); + .host("localhost"); - assertThat(uri, is("http://localhost")); + assertThat(uri.toString(), is("http://localhost")); uri = new MutableUri() .scheme("http") .host("localhost") - .port(8080) - .toString(); + .port(8080); - assertThat(uri, is("http://localhost:8080")); + assertThat(uri.toString(), is("http://localhost:8080")); uri = new MutableUri() .scheme("http") .host("localhost") .port(8080) .query("a", "1") - .query("b", "2") - .toString(); + .query("b", "2"); - assertThat(uri, is("http://localhost:8080?a=1&b=2")); + assertThat(uri.toString(), is("http://localhost:8080?a=1&b=2")); uri = new MutableUri() .scheme("http") @@ -254,10 +424,9 @@ public void stringify() { .port(8080) .path("/") .query("a", "1") - .query("b", "2") - .toString(); + .query("b", "2"); - assertThat(uri, is("http://localhost:8080/?a=1&b=2")); + assertThat(uri.toString(), is("http://localhost:8080/?a=1&b=2")); uri = new MutableUri() .scheme("http") @@ -266,10 +435,9 @@ public void stringify() { .path("/") .query("a", "1") .query("b", "2") - .fragment("frag") - .toString(); + .fragment("frag"); - assertThat(uri, is("http://localhost:8080/?a=1&b=2#frag")); + assertThat(uri.toString(), is("http://localhost:8080/?a=1&b=2#frag")); uri = new MutableUri() .scheme("http") @@ -279,10 +447,9 @@ public void stringify() { .path("/") .query("a", "1") .query("b", "2") - .fragment("frag") - .toString(); + .fragment("frag"); - assertThat(uri, is("http://user1@localhost:8080/?a=1&b=2#frag")); + assertThat(uri.toString(), is("http://user1@localhost:8080/?a=1&b=2#frag")); uri = new MutableUri() .scheme("http") @@ -292,10 +459,9 @@ public void stringify() { .path("/this/is/a/path") .query("a", "1") .query("b", "2") - .fragment("frag") - .toString(); + .fragment("frag"); - assertThat(uri, is("http://user1@localhost:8080/this/is/a/path?a=1&b=2#frag")); + assertThat(uri.toString(), is("http://user1@localhost:8080/this/is/a/path?a=1&b=2#frag")); // multiple parameters uri = new MutableUri() @@ -308,10 +474,9 @@ public void stringify() { .query("a", "2") .query("b", "2") .query("c", null) - .fragment("frag") - .toString(); + .fragment("frag"); - assertThat(uri, is("http://user1@localhost:8080/this/is/a/path?a=1&a=2&b=2&c#frag")); + assertThat(uri.toString(), is("http://user1@localhost:8080/this/is/a/path?a=1&a=2&b=2&c#frag")); // multiple parameters & requiring encoding uri = new MutableUri() @@ -324,10 +489,9 @@ public void stringify() { .query("a", "2") .query("b", 2) .query("c", null) - .fragment("fr@g") - .toString(); + .fragment("fr@g"); - assertThat(uri, is("http://user%401@localhost:8080/this/is/a/path?a=%40&a=2&b=2&c#fr%40g")); + assertThat(uri.toString(), is("http://user%401@localhost:8080/this/is/a/path?a=%40&a=2&b=2&c#fr%40g")); } @Test @@ -401,7 +565,7 @@ public void queryWithMap() { // either single value OR iterable :-) Map query = new LinkedHashMap<>(); query.put("a", "2"); - query.put("b", Arrays.asList("3")); + query.put("b", asList("3")); uri.query(query); @@ -417,7 +581,7 @@ public void setQueryWithMap() { Map query = new LinkedHashMap<>(); query.put("a", "2"); - query.put("b", Arrays.asList("3")); + query.put("b", asList("3")); uri.setQuery(query); @@ -468,7 +632,7 @@ public void pathAsRelative() { .path("test"); assertThat(uri.toString(), is("t://l/api/v1/test")); - assertThat(uri.getRels(), is(Arrays.asList("api", "v1", "test"))); + assertThat(uri.getRels(), is(asList("api", "v1", "test"))); assertThat(uri.getRel(0), is("api")); assertThat(uri.getRel(4), is(nullValue())); @@ -518,18 +682,18 @@ public void rel() { uri = new MutableUri("t://l") .rel("a@b"); - assertThat(uri.toString(), is("t://l/a%40b")); + assertThat(uri.toString(), is("t://l/a@b")); assertThat(uri.getRel(0), is("a@b")); uri = new MutableUri("t://l") .rel("a@b", "c"); - assertThat(uri.toString(), is("t://l/a%40b/c")); + assertThat(uri.toString(), is("t://l/a@b/c")); uri = new MutableUri("t://l") .rel("a@b", 80); - assertThat(uri.toString(), is("t://l/a%40b/80")); + assertThat(uri.toString(), is("t://l/a@b/80")); } @Test diff --git a/crux-uri/src/test/java/com/fizzed/crux/uri/UriTest.java b/crux-uri/src/test/java/com/fizzed/crux/uri/UriTest.java index 86cc5bb..72d1782 100644 --- a/crux-uri/src/test/java/com/fizzed/crux/uri/UriTest.java +++ b/crux-uri/src/test/java/com/fizzed/crux/uri/UriTest.java @@ -207,41 +207,22 @@ public void schemeSpecificPart() { uri = new Uri("mailto:info@example.com"); assertThat(uri.getScheme(), is("mailto")); - assertThat(uri.getSchemeSpecificPart(), is("info@example.com")); assertThat(uri.toString(), is("mailto:info@example.com")); uri = new Uri("tel:+1-816-555-1212"); assertThat(uri.getScheme(), is("tel")); - assertThat(uri.getSchemeSpecificPart(), is("+1-816-555-1212")); assertThat(uri.toString(), is("tel:+1-816-555-1212")); uri = new Uri("urn:oasis:names:specification:docbook:dtd:xml:4.1.2"); assertThat(uri.getScheme(), is("urn")); - assertThat(uri.getSchemeSpecificPart(), is("oasis:names:specification:docbook:dtd:xml:4.1.2")); assertThat(uri.toString(), is("urn:oasis:names:specification:docbook:dtd:xml:4.1.2")); uri = new Uri("urn:oasis:names:specification:docbook:dtd:xml:4.1.2"); assertThat(uri.getScheme(), is("urn")); - assertThat(uri.getSchemeSpecificPart(), is("oasis:names:specification:docbook:dtd:xml:4.1.2")); assertThat(uri.toString(), is("urn:oasis:names:specification:docbook:dtd:xml:4.1.2")); - -// //uri = new Uri("ldap://[2001:db8::7]/c=GB?objectClass?one"); -// URI uri2 = URI.create("ldap://[2001:db8::7]/c=GB?objectClass?one"); -// -// assertThat(uri2.getScheme(), is("ldap")); -// //assertThat(uri2.getSchemeSpecificPart(), is(nullValue())); -// assertThat(uri2.toString(), is("ldap://[2001:db8::7]/c=GB?objectClass?one")); - - - // real-world links that chrome handles :-( - // https://fonts.googleapis.com/css?family=Roboto:100,300,300italic,400,500italic|Open+Sans:400&subset=latin - - //Uri uri = new Uri("https://fonts.googleapis.com/css?family=Roboto:100,300,300italic,400,500italic|Open+Sans:400&subset=latin"); - - //assertThat(uri, is(not(nullValue()))); } @Test