From 412b6eaeb5074d5e67c385e4b39dad1fe1da75b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rg=20Prante?= Date: Mon, 22 Oct 2018 23:13:42 +0200 Subject: [PATCH] add better user/password handling in userInfo, for instance passwords with colon --- gradle.properties | 2 +- net-url/src/main/java/org/xbib/net/URL.java | 67 +++++++++++----- .../java/org/xbib/net/URLBuilderTest.java | 76 +++++++++++-------- .../test/java/org/xbib/net/URLParserTest.java | 12 +++ .../java/org/xbib/net/URLResolverTest.java | 5 +- 5 files changed, 111 insertions(+), 51 deletions(-) diff --git a/gradle.properties b/gradle.properties index 7e861e7..aaf1cec 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ group = org.xbib name = net -version = 1.1.1 +version = 1.1.3 jackson.version = 2.8.11 junit.version = 4.12 diff --git a/net-url/src/main/java/org/xbib/net/URL.java b/net-url/src/main/java/org/xbib/net/URL.java index cf3ab64..3985b6f 100755 --- a/net-url/src/main/java/org/xbib/net/URL.java +++ b/net-url/src/main/java/org/xbib/net/URL.java @@ -248,7 +248,7 @@ public class URL implements Comparable { public static URL from(String input, boolean resolve) { try { return parser().parse(input, resolve); - } catch (URLSyntaxException e) { + } catch (URLSyntaxException | MalformedInputException | UnmappableCharacterException e) { throw new IllegalArgumentException(e); } } @@ -260,7 +260,7 @@ public class URL implements Comparable { public static URL from(URL base, String spec) { try { return new Resolver(base).resolve(spec); - } catch (URLSyntaxException e) { + } catch (URLSyntaxException | MalformedInputException | UnmappableCharacterException e) { throw new IllegalArgumentException(e); } } @@ -330,6 +330,22 @@ public class URL implements Comparable { return builder.userInfo; } + public String getUser() { + if (builder.userInfo == null) { + return null; + } + Pair p = indexOf(COLON_CHAR, builder.userInfo); + return decode(p.first); + } + + public String getPassword() { + if (builder.userInfo == null) { + return null; + } + Pair p = indexOf(COLON_CHAR, builder.userInfo); + return decode(p.second); + } + /** * Get the host name ('www.example.com' or '192.168.0.1:8080' or '[fde2:d7de:302::]') of the {@code URL}. * @return the host name @@ -668,6 +684,13 @@ public class URL implements Comparable { return str == null || str.isEmpty(); } + private static Pair indexOf(char ch, String input) { + int i = input.indexOf(ch); + String k = i >= 0 ? input.substring(0, i) : input; + String v = i >= 0 ? input.substring(i + 1) : null; + return new Pair<>(k, v); + } + @Override public int hashCode() { return toString().hashCode(); @@ -675,7 +698,7 @@ public class URL implements Comparable { @Override public boolean equals(Object other) { - return other != null && other instanceof URL && toString().equals(other.toString()); + return other instanceof URL && toString().equals(other.toString()); } @Override @@ -751,6 +774,16 @@ public class URL implements Comparable { return this; } + public Builder userInfo(String user, String pass) { + try { + this.userInfo = PercentEncoders.getRegNameEncoder(charset).encode(user) + ':' + + PercentEncoders.getRegNameEncoder(charset).encode(pass); + } catch (MalformedInputException | UnmappableCharacterException e) { + throw new IllegalArgumentException(e); + } + return this; + } + public Builder host(String host) { this.host = host; this.protocolVersion = ProtocolVersion.NONE; @@ -935,11 +968,13 @@ public class URL implements Comparable { builder = new Builder(); } - public URL parse(String input) throws URLSyntaxException { + public URL parse(String input) + throws URLSyntaxException, MalformedInputException, UnmappableCharacterException { return parse(input, true); } - public URL parse(String input, boolean resolve) throws URLSyntaxException { + public URL parse(String input, boolean resolve) + throws URLSyntaxException, MalformedInputException, UnmappableCharacterException { if (isNullOrEmpty(input)) { return INVALID; } @@ -992,17 +1027,20 @@ public class URL implements Comparable { return p.getSecond(); } - private String parseUserInfo(Builder builder, String input) { + private String parseUserInfo(Builder builder, String input) + throws MalformedInputException, UnmappableCharacterException { String remaining = input; int i = input.lastIndexOf(AT_CHAR); if (i > 0) { remaining = input.substring(i + 1); - builder.userInfo(input.substring(0, i)); + String userInfo = input.substring(0, i); + builder.userInfo(builder.percentDecoder.decode(userInfo)); } return remaining; } - private void parseHostAndPort(Builder builder, String host, boolean resolve) throws URLSyntaxException { + private void parseHostAndPort(Builder builder, String host, boolean resolve) + throws URLSyntaxException { if (host.indexOf('[') == 0) { int i = host.lastIndexOf(']'); if (i >= 0) { @@ -1126,13 +1164,6 @@ public class URL implements Comparable { builder.query(query); } } - - private Pair indexOf(char ch, String input) { - int i = input.indexOf(ch); - String k = i >= 0 ? input.substring(0, i) : input; - String v = i >= 0 ? input.substring(i + 1) : null; - return new Pair<>(k, v); - } } /** @@ -1147,7 +1178,8 @@ public class URL implements Comparable { this.base = base; } - public URL resolve(String relative) throws URLSyntaxException { + public URL resolve(String relative) + throws URLSyntaxException, MalformedInputException, UnmappableCharacterException { if (relative == null) { return null; } @@ -1158,7 +1190,8 @@ public class URL implements Comparable { return resolve(url); } - public URL resolve(URL relative) throws URLSyntaxException { + public URL resolve(URL relative) + throws URLSyntaxException { if (relative == null || relative == INVALID) { throw new URLSyntaxException("relative URL is invalid"); } diff --git a/net-url/src/test/java/org/xbib/net/URLBuilderTest.java b/net-url/src/test/java/org/xbib/net/URLBuilderTest.java index 45e073c..9cda7cb 100644 --- a/net-url/src/test/java/org/xbib/net/URLBuilderTest.java +++ b/net-url/src/test/java/org/xbib/net/URLBuilderTest.java @@ -10,17 +10,17 @@ import static org.junit.Assert.assertEquals; public class URLBuilderTest { @Test - public void testNoUrlParts() throws Exception { + public void testNoUrlParts() { assertUrl(URL.http().resolveFromHost("foo.com").toUrlString(), "http://foo.com"); } @Test - public void testWithPort() throws Exception { + public void testWithPort() { assertUrl(URL.http().resolveFromHost("foo.com").port(33).toUrlString(), "http://foo.com:33"); } @Test - public void testSimplePath() throws Exception { + public void testSimplePath() { assertUrl(URL.http().resolveFromHost("foo.com") .pathSegment("seg1") .pathSegment("seg2") @@ -29,7 +29,7 @@ public class URLBuilderTest { } @Test - public void testPathWithReserved() throws Exception { + public void testPathWithReserved() { // RFC 1738 S3.3 assertUrl(URL.http().resolveFromHost("foo.com") .pathSegment("seg/;?ment") @@ -38,14 +38,14 @@ public class URLBuilderTest { } @Test - public void testPathSegments() throws Exception { + public void testPathSegments() { assertUrl(URL.http().resolveFromHost("foo.com") .pathSegments("seg1", "seg2", "seg3") .toUrlString(), "http://foo.com/seg1/seg2/seg3"); } @Test - public void testMatrixWithReserved() throws Exception { + public void testMatrixWithReserved() { assertUrl(URL.http().resolveFromHost("foo.com") .pathSegment("foo") .matrixParam("foo", "bar") @@ -55,28 +55,28 @@ public class URLBuilderTest { } @Test - public void testUrlEncodedPathSegmentUtf8() throws Exception { + public void testUrlEncodedPathSegmentUtf8() { assertUrl(URL.http().resolveFromHost("foo.com") .pathSegment("snowman").pathSegment("\u2603") .toUrlString(), "http://foo.com/snowman/%E2%98%83"); } @Test - public void testUrlEncodedPathSegmentUtf8SurrogatePair() throws Exception { + public void testUrlEncodedPathSegmentUtf8SurrogatePair() { assertUrl(URL.http().resolveFromHost("foo.com") .pathSegment("clef").pathSegment("\ud834\udd1e") .toUrlString(), "http://foo.com/clef/%F0%9D%84%9E"); } @Test - public void testQueryParamNoPath() throws Exception { + public void testQueryParamNoPath() { assertUrl(URL.http().resolveFromHost("foo.com") .queryParam("foo", "bar") .toUrlString(), "http://foo.com?foo=bar"); } @Test - public void testQueryParamsDuplicated() throws Exception { + public void testQueryParamsDuplicated() { assertUrl(URL.http().resolveFromHost("foo.com") .queryParam("foo", "bar") .queryParam("foo", "bar2") @@ -86,7 +86,7 @@ public class URLBuilderTest { } @Test - public void testEncodeQueryParams() throws Exception { + public void testEncodeQueryParams() { assertUrl(URL.http().resolveFromHost("foo.com") .queryParam("foo", "bar&=#baz") .queryParam("foo", "bar?/2") @@ -94,7 +94,7 @@ public class URLBuilderTest { } @Test - public void testEncodeQueryParamWithSpaceAndPlus() throws Exception { + public void testEncodeQueryParamWithSpaceAndPlus() { assertUrl(URL.http().resolveFromHost("foo.com") .queryParam("foo", "spa ce") .queryParam("fo+o", "plus+") @@ -102,7 +102,7 @@ public class URLBuilderTest { } @Test - public void testPlusInVariousParts() throws Exception { + public void testPlusInVariousParts() { assertUrl(URL.http().resolveFromHost("foo.com") .pathSegment("has+plus") .matrixParam("plusMtx", "pl+us") @@ -112,7 +112,7 @@ public class URLBuilderTest { } @Test - public void testFragment() throws Exception { + public void testFragment() { assertUrl(URL.http().resolveFromHost("foo.com") .queryParam("foo", "bar") .fragment("#frag/?") @@ -120,7 +120,7 @@ public class URLBuilderTest { } @Test - public void testAllParts() throws Exception { + public void testAllParts() { assertUrl(URL.https().resolveFromHost("foo.bar.com").port(3333) .pathSegment("foo") .pathSegment("bar") @@ -134,24 +134,24 @@ public class URLBuilderTest { } @Test - public void testSlashInHost() throws Exception { + public void testSlashInHost() { URL.http().resolveFromHost("/").toUrlString(); } @Test - public void testGoogle() throws Exception { + public void testGoogle() { URL url = URL.https().resolveFromHost("google.com").build(); assertEquals("https://google.com", url.toString()); } @Test - public void testBadIPv4LiteralDoesntChoke() throws Exception { + public void testBadIPv4LiteralDoesntChoke() { assertUrl(URL.http().resolveFromHost("300.100.50.1") .toUrlString(), "http://300.100.50.1"); } @Test - public void testIPv4Literal() throws Exception { + public void testIPv4Literal() { if ("false".equals(System.getProperty("java.net.preferIPv6Addresses"))) { assertUrl(URL.http().resolveFromHost("127.0.0.1") .toUrlString(), "http://localhost"); @@ -161,7 +161,7 @@ public class URLBuilderTest { } @Test - public void testIPv6LiteralLocalhost() throws Exception { + public void testIPv6LiteralLocalhost() { String s = URL.http().resolveFromHost("[::1]").toUrlString(); if ("true".equals(System.getProperty("java.net.preferIPv6Addresses"))) { assertEquals("http://[0:0:0:0:0:0:0:1]", s); @@ -171,7 +171,7 @@ public class URLBuilderTest { } @Test - public void testIPv6Literal() throws Exception { + public void testIPv6Literal() { if ("true".equals(System.getProperty("java.net.preferIPv6Addresses"))) { String s = URL.http().resolveFromHost("[2001:db8:85a3::8a2e:370:7334]") .toUrlString(); @@ -180,21 +180,21 @@ public class URLBuilderTest { } @Test - public void testEncodedRegNameSingleByte() throws Exception { + public void testEncodedRegNameSingleByte() { String s = URL.http().resolveFromHost("host?name;") .toUrlString(); assertEquals("http://host%3Fname;", s); } @Test - public void testEncodedRegNameMultiByte() throws Exception { + public void testEncodedRegNameMultiByte() { String s = URL.http().host("snow\u2603man") .toUrlString(); assertEquals("http://snow%E2%98%83man", s); } @Test - public void testThreePathSegments() throws Exception { + public void testThreePathSegments() { String s = URL.https().resolveFromHost("foo.com") .pathSegments("a", "b", "c") .toUrlString(); @@ -202,7 +202,7 @@ public class URLBuilderTest { } @Test - public void testThreePathSegmentsWithQueryParams() throws Exception { + public void testThreePathSegmentsWithQueryParams() { String s = URL.https().resolveFromHost("foo.com") .pathSegments("a", "b", "c") .queryParam("foo", "bar") @@ -211,7 +211,7 @@ public class URLBuilderTest { } @Test - public void testIntermingledMatrixParamsAndPathSegments() throws Exception { + public void testIntermingledMatrixParamsAndPathSegments() { String s = URL.http().resolveFromHost("foo.com") .pathSegments("seg1", "seg2") .matrixParam("m1", "v1") @@ -222,7 +222,7 @@ public class URLBuilderTest { } @Test - public void testUseQueryParamAfterQuery() throws Exception { + public void testUseQueryParamAfterQuery() { String s = URL.http().resolveFromHost("foo.com") .query("q") .queryParam("foo", "bar") @@ -231,7 +231,7 @@ public class URLBuilderTest { } @Test - public void testUseQueryAfterQueryParam() throws Exception { + public void testUseQueryAfterQueryParam() { String s = URL.http().resolveFromHost("foo.com") .queryParam("foo", "bar") .query("q") @@ -240,7 +240,7 @@ public class URLBuilderTest { } @Test - public void testQueryWithNoSpecialChars() throws Exception { + public void testQueryWithNoSpecialChars() { String s = URL.http().resolveFromHost("foo.com") .query("q") .toUrlString(); @@ -248,14 +248,14 @@ public class URLBuilderTest { } @Test - public void testQueryWithOkSpecialChars() throws Exception { + public void testQueryWithOkSpecialChars() { String s = URL.http().resolveFromHost("foo.com") .query("q?/&=").toUrlString(); assertEquals("http://foo.com?q?/&=", s); } @Test - public void testQueryWithEscapedSpecialChars() throws Exception { + public void testQueryWithEscapedSpecialChars() { String s = URL.http().resolveFromHost("foo.com") .query("q#+").toUrlString(); assertEquals("http://foo.com?q%23%2B", s); @@ -268,7 +268,19 @@ public class URLBuilderTest { assertEquals("https://google.com:8008/foobar", builder.build().toString()); } - private void assertUrl(String urlString, String expected) throws Exception { + @Test + public void testUserInfo(){ + String s = URL.http().userInfo("foo:bar").host("foo.com").toUrlString(); + assertEquals("http://foo:bar@foo.com", s); + s = URL.http().userInfo("foo:foo:bar").host("foo.com").toUrlString(); + assertEquals("http://foo:foo:bar@foo.com", s); + s = URL.http().userInfo("foo:foo%3Abar").host("foo.com").toUrlString(); + assertEquals("http://foo:foo%3Abar@foo.com", s); + s = URL.http().userInfo("foo", "foo:bar").host("foo.com").toUrlString(); + assertEquals("http://foo:foo%3Abar@foo.com", s); + } + + private void assertUrl(String urlString, String expected) { assertEquals(expected, urlString); assertEquals(expected, URL.from(urlString).toExternalForm()); } diff --git a/net-url/src/test/java/org/xbib/net/URLParserTest.java b/net-url/src/test/java/org/xbib/net/URLParserTest.java index f677651..6522fc2 100644 --- a/net-url/src/test/java/org/xbib/net/URLParserTest.java +++ b/net-url/src/test/java/org/xbib/net/URLParserTest.java @@ -369,6 +369,18 @@ public class URLParserTest { assertEquals("plus+frag", url.getFragment()); } + @Test + public void testUserInfo() throws Exception { + URL url = URL.parser().parse("http://foo:bar@foo.com/"); + assertEquals("foo:bar", url.getUserInfo()); + url = URL.parser().parse("http://foo:foo:bar@foo.com/"); + assertEquals("foo:foo:bar", url.getUserInfo()); + url = URL.parser().parse("http://foo:foo%3Abar@foo.com/"); + assertEquals("foo:foo:bar", url.getUserInfo()); + assertEquals("foo", url.getUser()); + assertEquals("foo:bar", url.getPassword()); + } + private void assertUrlCompatibility(String url) throws Exception { String s = URL.from(url).toExternalForm(); assertEquals(s, URL.from(s).toExternalForm()); diff --git a/net-url/src/test/java/org/xbib/net/URLResolverTest.java b/net-url/src/test/java/org/xbib/net/URLResolverTest.java index 4a9e76b..385bfdc 100644 --- a/net-url/src/test/java/org/xbib/net/URLResolverTest.java +++ b/net-url/src/test/java/org/xbib/net/URLResolverTest.java @@ -5,6 +5,8 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; import java.net.URI; +import java.nio.charset.MalformedInputException; +import java.nio.charset.UnmappableCharacterException; /** */ @@ -98,7 +100,8 @@ public class URLResolverTest { resolve("http://a/b/c/d;p?q", "http://e/f/g/h", "http://e/f/g/h"); } - private void resolve(String inputBase, String spec, String expected) throws URLSyntaxException { + private void resolve(String inputBase, String spec, String expected) + throws URLSyntaxException, MalformedInputException, UnmappableCharacterException { assertEquals(expected, URL.base(inputBase).resolve(spec).toExternalForm()); } }