修复因RFC3986理解有误导致的UrlPath处理冒号转义问题

This commit is contained in:
Looly 2024-07-04 12:27:22 +08:00
parent 3125c23477
commit 0af4f1cf21
2 changed files with 91 additions and 84 deletions

View File

@ -170,14 +170,9 @@ public class UrlPath {
final char[] safeChars = encodePercent ? null : new char[]{'%'}; final char[] safeChars = encodePercent ? null : new char[]{'%'};
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
for (final String segment : segments) { for (final String segment : segments) {
if (builder.length() == 0) { // https://www.ietf.org/rfc/rfc3986.html#section-3.3
// 根据https://www.ietf.org/rfc/rfc3986.html#section-3.3定义 // 此处Path中是允许有`:`之前理解有误应该是相对URI的第一个segment中不允许有`:`
// path的第一部分不允许有":"其余部分允许 builder.append(CharUtil.SLASH).append(RFC3986.SEGMENT.encode(segment, charset, safeChars));
// 在此处的Path部分特指host之后的部分即不包含第一部分
builder.append(CharUtil.SLASH).append(RFC3986.SEGMENT_NZ_NC.encode(segment, charset, safeChars));
} else {
builder.append(CharUtil.SLASH).append(RFC3986.SEGMENT.encode(segment, charset, safeChars));
}
} }
if (withEngTag) { if (withEngTag) {

View File

@ -14,6 +14,7 @@ package org.dromara.hutool.core.net;
import org.dromara.hutool.core.date.DateUtil; import org.dromara.hutool.core.date.DateUtil;
import org.dromara.hutool.core.net.url.UrlBuilder; import org.dromara.hutool.core.net.url.UrlBuilder;
import org.dromara.hutool.core.net.url.UrlPath;
import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.core.util.CharsetUtil;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -24,6 +25,8 @@ import java.nio.charset.StandardCharsets;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class UrlBuilderTest { public class UrlBuilderTest {
@Test @Test
@ -31,28 +34,28 @@ public class UrlBuilderTest {
final UrlBuilder builder = UrlBuilder.of(); final UrlBuilder builder = UrlBuilder.of();
final String buildUrl = builder.setHost("www.hutool.cn").build(); final String buildUrl = builder.setHost("www.hutool.cn").build();
Assertions.assertEquals("http://www.hutool.cn/", buildUrl); assertEquals("http://www.hutool.cn/", buildUrl);
Assertions.assertEquals(80, builder.getPortWithDefault()); assertEquals(80, builder.getPortWithDefault());
} }
@Test @Test
public void buildWithoutSlashTest() { public void buildWithoutSlashTest() {
// https://github.com/dromara/hutool/issues/2459 // https://github.com/dromara/hutool/issues/2459
String buildUrl = UrlBuilder.of().setScheme("http").setHost("192.168.1.1").setPort(8080).setWithEndTag(false).build(); String buildUrl = UrlBuilder.of().setScheme("http").setHost("192.168.1.1").setPort(8080).setWithEndTag(false).build();
Assertions.assertEquals("http://192.168.1.1:8080", buildUrl); assertEquals("http://192.168.1.1:8080", buildUrl);
final UrlBuilder urlBuilder = UrlBuilder.of(); final UrlBuilder urlBuilder = UrlBuilder.of();
buildUrl = urlBuilder.setScheme("http").setHost("192.168.1.1").setPort(8080).addQuery("url", "http://192.168.1.1/test/1") buildUrl = urlBuilder.setScheme("http").setHost("192.168.1.1").setPort(8080).addQuery("url", "http://192.168.1.1/test/1")
.setWithEndTag(false).build(); .setWithEndTag(false).build();
Assertions.assertEquals("http://192.168.1.1:8080?url=http://192.168.1.1/test/1", buildUrl); assertEquals("http://192.168.1.1:8080?url=http://192.168.1.1/test/1", buildUrl);
Assertions.assertEquals(8080, urlBuilder.getPortWithDefault()); assertEquals(8080, urlBuilder.getPortWithDefault());
} }
@Test @Test
public void buildTest2() { public void buildTest2() {
// path中的+不做处理 // path中的+不做处理
final String buildUrl = UrlBuilder.ofHttp("http://www.hutool.cn/+8618888888888", CharsetUtil.UTF_8).build(); final String buildUrl = UrlBuilder.ofHttp("http://www.hutool.cn/+8618888888888", CharsetUtil.UTF_8).build();
Assertions.assertEquals("http://www.hutool.cn/+8618888888888", buildUrl); assertEquals("http://www.hutool.cn/+8618888888888", buildUrl);
} }
@Test @Test
@ -60,7 +63,7 @@ public class UrlBuilderTest {
final String buildUrl = UrlBuilder.of() final String buildUrl = UrlBuilder.of()
.setScheme("https") .setScheme("https")
.setHost("www.hutool.cn").build(); .setHost("www.hutool.cn").build();
Assertions.assertEquals("https://www.hutool.cn/", buildUrl); assertEquals("https://www.hutool.cn/", buildUrl);
} }
@Test @Test
@ -70,7 +73,7 @@ public class UrlBuilderTest {
.setHost("www.hutool.cn") .setHost("www.hutool.cn")
.setPort(8080) .setPort(8080)
.build(); .build();
Assertions.assertEquals("https://www.hutool.cn:8080/", buildUrl); assertEquals("https://www.hutool.cn:8080/", buildUrl);
} }
@Test @Test
@ -83,7 +86,7 @@ public class UrlBuilderTest {
.addQuery("wd", "test") .addQuery("wd", "test")
.build(); .build();
Assertions.assertEquals("https://www.hutool.cn/aaa/bbb?ie=UTF-8&wd=test", buildUrl); assertEquals("https://www.hutool.cn/aaa/bbb?ie=UTF-8&wd=test", buildUrl);
} }
@Test @Test
@ -96,7 +99,7 @@ public class UrlBuilderTest {
.addQuery("wd", "测试") .addQuery("wd", "测试")
.build(); .build();
Assertions.assertEquals("https://www.hutool.cn/aaa/bbb?ie=UTF-8&wd=%E6%B5%8B%E8%AF%95", buildUrl); assertEquals("https://www.hutool.cn/aaa/bbb?ie=UTF-8&wd=%E6%B5%8B%E8%AF%95", buildUrl);
} }
@Test @Test
@ -110,7 +113,7 @@ public class UrlBuilderTest {
.addQuery("wd", "测试") .addQuery("wd", "测试")
.build(); .build();
Assertions.assertEquals("https://www.hutool.cn/s?ie=UTF-8&ie=GBK&wd=%E6%B5%8B%E8%AF%95", buildUrl); assertEquals("https://www.hutool.cn/s?ie=UTF-8&ie=GBK&wd=%E6%B5%8B%E8%AF%95", buildUrl);
} }
@Test @Test
@ -119,7 +122,7 @@ public class UrlBuilderTest {
.setScheme("https") .setScheme("https")
.setHost("www.hutool.cn") .setHost("www.hutool.cn")
.setFragment("abc").build(); .setFragment("abc").build();
Assertions.assertEquals("https://www.hutool.cn/#abc", buildUrl); assertEquals("https://www.hutool.cn/#abc", buildUrl);
} }
@Test @Test
@ -128,7 +131,7 @@ public class UrlBuilderTest {
.setScheme("https") .setScheme("https")
.setHost("www.hutool.cn") .setHost("www.hutool.cn")
.setFragment("测试").build(); .setFragment("测试").build();
Assertions.assertEquals("https://www.hutool.cn/#%E6%B5%8B%E8%AF%95", buildUrl); assertEquals("https://www.hutool.cn/#%E6%B5%8B%E8%AF%95", buildUrl);
} }
@Test @Test
@ -138,7 +141,7 @@ public class UrlBuilderTest {
.setHost("www.hutool.cn") .setHost("www.hutool.cn")
.addPath("/s") .addPath("/s")
.setFragment("测试").build(); .setFragment("测试").build();
Assertions.assertEquals("https://www.hutool.cn/s#%E6%B5%8B%E8%AF%95", buildUrl); assertEquals("https://www.hutool.cn/s#%E6%B5%8B%E8%AF%95", buildUrl);
} }
@Test @Test
@ -149,22 +152,22 @@ public class UrlBuilderTest {
.addPath("/s") .addPath("/s")
.addQuery("wd", "test") .addQuery("wd", "test")
.setFragment("测试").build(); .setFragment("测试").build();
Assertions.assertEquals("https://www.hutool.cn/s?wd=test#%E6%B5%8B%E8%AF%95", buildUrl); assertEquals("https://www.hutool.cn/s?wd=test#%E6%B5%8B%E8%AF%95", buildUrl);
} }
@Test @Test
public void ofTest() { public void ofTest() {
final UrlBuilder builder = UrlBuilder.of("http://www.hutool.cn/aaa/bbb/?a=1&b=2#frag1", CharsetUtil.UTF_8); final UrlBuilder builder = UrlBuilder.of("http://www.hutool.cn/aaa/bbb/?a=1&b=2#frag1", CharsetUtil.UTF_8);
Assertions.assertEquals("http", builder.getScheme()); assertEquals("http", builder.getScheme());
Assertions.assertEquals("www.hutool.cn", builder.getHost()); assertEquals("www.hutool.cn", builder.getHost());
Assertions.assertEquals("aaa", builder.getPath().getSegment(0)); assertEquals("aaa", builder.getPath().getSegment(0));
Assertions.assertEquals("bbb", builder.getPath().getSegment(1)); assertEquals("bbb", builder.getPath().getSegment(1));
Assertions.assertEquals("1", builder.getQuery().get("a")); assertEquals("1", builder.getQuery().get("a"));
Assertions.assertEquals("2", builder.getQuery().get("b")); assertEquals("2", builder.getQuery().get("b"));
Assertions.assertEquals("frag1", builder.getFragment()); assertEquals("frag1", builder.getFragment());
} }
@Test @Test
@ -177,47 +180,47 @@ public class UrlBuilderTest {
@Test @Test
public void ofWithChineseTest() { public void ofWithChineseTest() {
final UrlBuilder builder = UrlBuilder.ofHttp("www.hutool.cn/aaa/bbb/?a=张三&b=%e6%9d%8e%e5%9b%9b#frag1", CharsetUtil.UTF_8); final UrlBuilder builder = UrlBuilder.ofHttp("www.hutool.cn/aaa/bbb/?a=张三&b=%e6%9d%8e%e5%9b%9b#frag1", CharsetUtil.UTF_8);
Assertions.assertEquals("http", builder.getScheme()); assertEquals("http", builder.getScheme());
Assertions.assertEquals("www.hutool.cn", builder.getHost()); assertEquals("www.hutool.cn", builder.getHost());
Assertions.assertEquals("aaa", builder.getPath().getSegment(0)); assertEquals("aaa", builder.getPath().getSegment(0));
Assertions.assertEquals("bbb", builder.getPath().getSegment(1)); assertEquals("bbb", builder.getPath().getSegment(1));
Assertions.assertEquals("张三", builder.getQuery().get("a")); assertEquals("张三", builder.getQuery().get("a"));
Assertions.assertEquals("李四", builder.getQuery().get("b")); assertEquals("李四", builder.getQuery().get("b"));
Assertions.assertEquals("frag1", builder.getFragment()); assertEquals("frag1", builder.getFragment());
} }
@Test @Test
public void ofWithBlankTest() { public void ofWithBlankTest() {
final UrlBuilder builder = UrlBuilder.ofHttp(" www.hutool.cn/aaa/bbb/?a=张三&b=%e6%9d%8e%e5%9b%9b#frag1", CharsetUtil.UTF_8); final UrlBuilder builder = UrlBuilder.ofHttp(" www.hutool.cn/aaa/bbb/?a=张三&b=%e6%9d%8e%e5%9b%9b#frag1", CharsetUtil.UTF_8);
Assertions.assertEquals("http", builder.getScheme()); assertEquals("http", builder.getScheme());
Assertions.assertEquals("www.hutool.cn", builder.getHost()); assertEquals("www.hutool.cn", builder.getHost());
Assertions.assertEquals("aaa", builder.getPath().getSegment(0)); assertEquals("aaa", builder.getPath().getSegment(0));
Assertions.assertEquals("bbb", builder.getPath().getSegment(1)); assertEquals("bbb", builder.getPath().getSegment(1));
Assertions.assertEquals("张三", builder.getQuery().get("a")); assertEquals("张三", builder.getQuery().get("a"));
Assertions.assertEquals("李四", builder.getQuery().get("b")); assertEquals("李四", builder.getQuery().get("b"));
Assertions.assertEquals("frag1", builder.getFragment()); assertEquals("frag1", builder.getFragment());
} }
@Test @Test
public void ofSpecialTest() { public void ofSpecialTest() {
//测试不规范的或者无需解码的字符串是否成功解码 //测试不规范的或者无需解码的字符串是否成功解码
final UrlBuilder builder = UrlBuilder.ofHttp(" www.hutool.cn/aaa/bbb/?a=张三&b=%%e5%9b%9b#frag1", CharsetUtil.UTF_8); final UrlBuilder builder = UrlBuilder.ofHttp(" www.hutool.cn/aaa/bbb/?a=张三&b=%%e5%9b%9b#frag1", CharsetUtil.UTF_8);
Assertions.assertEquals("http", builder.getScheme()); assertEquals("http", builder.getScheme());
Assertions.assertEquals("www.hutool.cn", builder.getHost()); assertEquals("www.hutool.cn", builder.getHost());
Assertions.assertEquals("aaa", builder.getPath().getSegment(0)); assertEquals("aaa", builder.getPath().getSegment(0));
Assertions.assertEquals("bbb", builder.getPath().getSegment(1)); assertEquals("bbb", builder.getPath().getSegment(1));
Assertions.assertEquals("张三", builder.getQuery().get("a")); assertEquals("张三", builder.getQuery().get("a"));
Assertions.assertEquals("%四", builder.getQuery().get("b")); assertEquals("%四", builder.getQuery().get("b"));
Assertions.assertEquals("frag1", builder.getFragment()); assertEquals("frag1", builder.getFragment());
} }
@Test @Test
@ -230,7 +233,7 @@ public class UrlBuilderTest {
"&chksm=6cbda3a25bca2ab4516410db6ce6e125badaac2f8c5548ea6e18eab6dc3c5422cb8cbe1095f7"; "&chksm=6cbda3a25bca2ab4516410db6ce6e125badaac2f8c5548ea6e18eab6dc3c5422cb8cbe1095f7";
final UrlBuilder builder = UrlBuilder.ofHttp(urlStr, CharsetUtil.UTF_8); final UrlBuilder builder = UrlBuilder.ofHttp(urlStr, CharsetUtil.UTF_8);
// 原URL中的&替换为& // 原URL中的&替换为&
Assertions.assertEquals("https://mp.weixin.qq.com/s?" + assertEquals("https://mp.weixin.qq.com/s?" +
"__biz=MzI5NjkyNTIxMg==" + "__biz=MzI5NjkyNTIxMg==" +
"&mid=100000465&idx=1" + "&mid=100000465&idx=1" +
"&sn=1044c0d19723f74f04f4c1da34eefa35" + "&sn=1044c0d19723f74f04f4c1da34eefa35" +
@ -244,40 +247,40 @@ public class UrlBuilderTest {
final String today = DateUtil.now().toString("yyyyMMdd"); final String today = DateUtil.now().toString("yyyyMMdd");
final String getWorkDayUrl = "https://tool.bitefu.net/jiari/?info=1&d=" + today; final String getWorkDayUrl = "https://tool.bitefu.net/jiari/?info=1&d=" + today;
final UrlBuilder builder = UrlBuilder.ofHttp(getWorkDayUrl, CharsetUtil.UTF_8); final UrlBuilder builder = UrlBuilder.ofHttp(getWorkDayUrl, CharsetUtil.UTF_8);
Assertions.assertEquals(getWorkDayUrl, builder.toString()); assertEquals(getWorkDayUrl, builder.toString());
} }
@Test @Test
public void blankEncodeTest() { public void blankEncodeTest() {
final UrlBuilder urlBuilder = UrlBuilder.ofHttp("http://a.com/aaa bbb.html", CharsetUtil.UTF_8); final UrlBuilder urlBuilder = UrlBuilder.ofHttp("http://a.com/aaa bbb.html", CharsetUtil.UTF_8);
Assertions.assertEquals("http://a.com/aaa%20bbb.html", urlBuilder.toString()); assertEquals("http://a.com/aaa%20bbb.html", urlBuilder.toString());
} }
@Test @Test
public void dotEncodeTest() { public void dotEncodeTest() {
final UrlBuilder urlBuilder = UrlBuilder.ofHttp("http://xtbgyy.digitalgd.com.cn/ebus/../../..", CharsetUtil.UTF_8); final UrlBuilder urlBuilder = UrlBuilder.ofHttp("http://xtbgyy.digitalgd.com.cn/ebus/../../..", CharsetUtil.UTF_8);
Assertions.assertEquals("http://xtbgyy.digitalgd.com.cn/ebus/../../..", urlBuilder.toString()); assertEquals("http://xtbgyy.digitalgd.com.cn/ebus/../../..", urlBuilder.toString());
} }
@Test @Test
public void multiSlashTest() { public void multiSlashTest() {
//issue#I25MZL某些URL中有多个斜杠此为合法路径 //issue#I25MZL某些URL中有多个斜杠此为合法路径
final UrlBuilder urlBuilder = UrlBuilder.ofHttp("https://hutool.cn//file/test.jpg", CharsetUtil.UTF_8); final UrlBuilder urlBuilder = UrlBuilder.ofHttp("https://hutool.cn//file/test.jpg", CharsetUtil.UTF_8);
Assertions.assertEquals("https://hutool.cn//file/test.jpg", urlBuilder.toString()); assertEquals("https://hutool.cn//file/test.jpg", urlBuilder.toString());
} }
@Test @Test
public void toURITest() throws URISyntaxException { public void toURITest() throws URISyntaxException {
final String webUrl = "http://exmple.com/patha/pathb?a=123"; // 报错数据 final String webUrl = "http://exmple.com/patha/pathb?a=123"; // 报错数据
final UrlBuilder urlBuilder = UrlBuilder.of(webUrl, StandardCharsets.UTF_8); final UrlBuilder urlBuilder = UrlBuilder.of(webUrl, StandardCharsets.UTF_8);
Assertions.assertEquals(new URI(webUrl), urlBuilder.toURI()); assertEquals(new URI(webUrl), urlBuilder.toURI());
} }
@Test @Test
public void testEncodeInQuery() { public void testEncodeInQuery() {
final String webUrl = "http://exmple.com/patha/pathb?a=123&b=4?6&c=789"; // b=4?6 参数中有未编码的 final String webUrl = "http://exmple.com/patha/pathb?a=123&b=4?6&c=789"; // b=4?6 参数中有未编码的
final UrlBuilder urlBuilder = UrlBuilder.of(webUrl, StandardCharsets.UTF_8); final UrlBuilder urlBuilder = UrlBuilder.of(webUrl, StandardCharsets.UTF_8);
Assertions.assertEquals("a=123&b=4?6&c=789", urlBuilder.getQueryStr()); assertEquals("a=123&b=4?6&c=789", urlBuilder.getQueryStr());
} }
@Test @Test
@ -285,7 +288,7 @@ public class UrlBuilderTest {
// Path中的某些符号无需转义比如= // Path中的某些符号无需转义比如=
final String urlStr = "http://hq.sinajs.cn/list=sh600519"; final String urlStr = "http://hq.sinajs.cn/list=sh600519";
final UrlBuilder urlBuilder = UrlBuilder.ofHttp(urlStr, CharsetUtil.UTF_8); final UrlBuilder urlBuilder = UrlBuilder.ofHttp(urlStr, CharsetUtil.UTF_8);
Assertions.assertEquals(urlStr, urlBuilder.toString()); assertEquals(urlStr, urlBuilder.toString());
} }
@Test @Test
@ -294,7 +297,7 @@ public class UrlBuilderTest {
// Path中`:`在第一个segment需要转义之后的不需要 // Path中`:`在第一个segment需要转义之后的不需要
final String urlStr = "https://hutool.cn/aa/bb/Pre-K,Kindergarten,First,Second,Third,Fourth,Fifth/Page:3"; final String urlStr = "https://hutool.cn/aa/bb/Pre-K,Kindergarten,First,Second,Third,Fourth,Fifth/Page:3";
final UrlBuilder urlBuilder = UrlBuilder.ofHttp(urlStr, CharsetUtil.UTF_8); final UrlBuilder urlBuilder = UrlBuilder.ofHttp(urlStr, CharsetUtil.UTF_8);
Assertions.assertEquals(urlStr, urlBuilder.toString()); assertEquals(urlStr, urlBuilder.toString());
} }
@Test @Test
@ -304,7 +307,7 @@ public class UrlBuilderTest {
// PATH除了第一个path外:是允许的 // PATH除了第一个path外:是允许的
final String url2 = "https://gimg2.baidu.com/image_search/src=http:%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1114%2F0H320120Z3%2F200H3120Z3-6-1200.jpg&refer=http:%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1621996490&t=8c384c2823ea453da15a1b9cd5183eea"; final String url2 = "https://gimg2.baidu.com/image_search/src=http:%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1114%2F0H320120Z3%2F200H3120Z3-6-1200.jpg&refer=http:%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1621996490&t=8c384c2823ea453da15a1b9cd5183eea";
Assertions.assertEquals(url2, urlBuilder.toString()); assertEquals(url2, urlBuilder.toString());
} }
@Test @Test
@ -313,10 +316,10 @@ public class UrlBuilderTest {
// https://stackoverflow.com/questions/26088849/url-fragment-allowed-characters // https://stackoverflow.com/questions/26088849/url-fragment-allowed-characters
final String url = "https://hutool.cn/docs/#/?id=简介"; final String url = "https://hutool.cn/docs/#/?id=简介";
UrlBuilder urlBuilder = UrlBuilder.ofHttp(url); UrlBuilder urlBuilder = UrlBuilder.ofHttp(url);
Assertions.assertEquals("https://hutool.cn/docs/#/?id=%E7%AE%80%E4%BB%8B", urlBuilder.toString()); assertEquals("https://hutool.cn/docs/#/?id=%E7%AE%80%E4%BB%8B", urlBuilder.toString());
urlBuilder = UrlBuilder.ofHttp(urlBuilder.toString()); urlBuilder = UrlBuilder.ofHttp(urlBuilder.toString());
Assertions.assertEquals(urlBuilder.toString(), urlBuilder.toString()); assertEquals(urlBuilder.toString(), urlBuilder.toString());
} }
@Test @Test
@ -326,7 +329,7 @@ public class UrlBuilderTest {
// https://www.rfc-editor.org/rfc/rfc3986.html#section-3.4 // https://www.rfc-editor.org/rfc/rfc3986.html#section-3.4
final String url = "https://invoice.maycur.com/2b27a802-8423-4d41-86f5-63a6b259f61e.xlsx?download/2b27a802-8423-4d41-86f5-63a6b259f61e.xlsx&e=1630491088"; final String url = "https://invoice.maycur.com/2b27a802-8423-4d41-86f5-63a6b259f61e.xlsx?download/2b27a802-8423-4d41-86f5-63a6b259f61e.xlsx&e=1630491088";
final UrlBuilder urlBuilder = UrlBuilder.ofHttp(url); final UrlBuilder urlBuilder = UrlBuilder.ofHttp(url);
Assertions.assertEquals(url, urlBuilder.toString()); assertEquals(url, urlBuilder.toString());
} }
@Test @Test
@ -339,7 +342,7 @@ public class UrlBuilderTest {
.addPath("bbb") .addPath("bbb")
.build(); .build();
Assertions.assertEquals("https://domain.cn/api/xxx/bbb", url); assertEquals("https://domain.cn/api/xxx/bbb", url);
} }
@Test @Test
@ -351,21 +354,21 @@ public class UrlBuilderTest {
.addPath("/api/xxx/bbb") .addPath("/api/xxx/bbb")
.build(); .build();
Assertions.assertEquals("https://domain.cn/api/xxx/bbb", url); assertEquals("https://domain.cn/api/xxx/bbb", url);
} }
@Test @Test
public void percent2BTest() { public void percent2BTest() {
final String url = "http://xxx.cn/a?Signature=3R013Bj9Uq4YeISzAs2iC%2BTVCL8%3D"; final String url = "http://xxx.cn/a?Signature=3R013Bj9Uq4YeISzAs2iC%2BTVCL8%3D";
final UrlBuilder of = UrlBuilder.ofHttpWithoutEncode(url); final UrlBuilder of = UrlBuilder.ofHttpWithoutEncode(url);
Assertions.assertEquals(url, of.toString()); assertEquals(url, of.toString());
} }
@Test @Test
public void paramTest() { public void paramTest() {
final String url = "http://ci.xiaohongshu.com/spectrum/c136c98aa2047babe25b994a26ffa7b492bd8058?imageMogr2/thumbnail/x800/format/jpg"; final String url = "http://ci.xiaohongshu.com/spectrum/c136c98aa2047babe25b994a26ffa7b492bd8058?imageMogr2/thumbnail/x800/format/jpg";
final UrlBuilder builder = UrlBuilder.ofHttp(url); final UrlBuilder builder = UrlBuilder.ofHttp(url);
Assertions.assertEquals(url, builder.toString()); assertEquals(url, builder.toString());
} }
@Test @Test
@ -374,7 +377,7 @@ public class UrlBuilderTest {
final String url = "https://www.hutool.cn/#/a/b?timestamp=1640391380204"; final String url = "https://www.hutool.cn/#/a/b?timestamp=1640391380204";
final UrlBuilder builder = UrlBuilder.ofHttp(url); final UrlBuilder builder = UrlBuilder.ofHttp(url);
Assertions.assertEquals(url, builder.toString()); assertEquals(url, builder.toString());
} }
@Test @Test
@ -383,7 +386,7 @@ public class UrlBuilderTest {
final String url = "https://www.hutool.cn/#/a/b"; final String url = "https://www.hutool.cn/#/a/b";
final UrlBuilder builder = UrlBuilder.ofHttp(url); final UrlBuilder builder = UrlBuilder.ofHttp(url);
builder.setFragment(builder.getFragment() + "?timestamp=1640391380204"); builder.setFragment(builder.getFragment() + "?timestamp=1640391380204");
Assertions.assertEquals("https://www.hutool.cn/#/a/b?timestamp=1640391380204", builder.toString()); assertEquals("https://www.hutool.cn/#/a/b?timestamp=1640391380204", builder.toString());
} }
@Test @Test
@ -398,7 +401,7 @@ public class UrlBuilderTest {
"/RqAAYRYVCBiyuzAexSiDiJX1VqWljg4jYp1sdyv3HpV3sXVcf6VH6AN9ot5YNTw4JNO0aNpLpLm93rRMrOKIOsve+OmNyZ4HS7qHQKt1qp7HY1A" + "/RqAAYRYVCBiyuzAexSiDiJX1VqWljg4jYp1sdyv3HpV3sXVcf6VH6AN9ot5YNTw4JNO0aNpLpLm93rRMrOKIOsve+OmNyZ4HS7qHQKt1qp7HY1A" +
"/wGhJstkAoGQt+CHSMwVdIx3bVT1+ZYnJdM/oIQ/90afw4EEEQaRE51Z0rQC7z8d"; "/wGhJstkAoGQt+CHSMwVdIx3bVT1+ZYnJdM/oIQ/90afw4EEEQaRE51Z0rQC7z8d";
final String build = UrlBuilder.of(url).build(); final String build = UrlBuilder.of(url).build();
Assertions.assertEquals(url, build); assertEquals(url, build);
} }
@Test @Test
@ -410,21 +413,21 @@ public class UrlBuilderTest {
"&Signature=oYUu26JufAyPY4PdzaOp1x4sr4Q%3D"; "&Signature=oYUu26JufAyPY4PdzaOp1x4sr4Q%3D";
final UrlBuilder urlBuilder = UrlBuilder.ofHttp(url, null); final UrlBuilder urlBuilder = UrlBuilder.ofHttp(url, null);
Assertions.assertEquals(url, urlBuilder.toString()); assertEquals(url, urlBuilder.toString());
} }
@Test @Test
public void issue2215Test() { public void issue2215Test() {
final String url = "https://hutool.cn/v1/104303371/messages:send"; final String url = "https://hutool.cn/v1/104303371/messages:send";
final String build = UrlBuilder.of(url).build(); final String build = UrlBuilder.of(url).build();
Assertions.assertEquals(url, build); assertEquals(url, build);
} }
@Test @Test
public void issuesI4Z2ETTest() { public void issuesI4Z2ETTest() {
final String url = "http://hutool.cn/2022/03/09/123.zip?Expires=1648704684&OSSAccessKeyId=LTAI4FncgaVtwZGBnYHHi8ox&Signature=%2BK%2B%3D"; final String url = "http://hutool.cn/2022/03/09/123.zip?Expires=1648704684&OSSAccessKeyId=LTAI4FncgaVtwZGBnYHHi8ox&Signature=%2BK%2B%3D";
final String build = UrlBuilder.of(url, null).build(); final String build = UrlBuilder.of(url, null).build();
Assertions.assertEquals(url, build); assertEquals(url, build);
} }
@Test @Test
@ -438,7 +441,7 @@ public class UrlBuilderTest {
final UrlBuilder builder = UrlBuilder.of(url); final UrlBuilder builder = UrlBuilder.of(url);
params.forEach(builder::addQuery); params.forEach(builder::addQuery);
Assertions.assertEquals("http://127.0.0.1/devicerecord/list?start=2022-03-31%2000:00:00&end=2022-03-31%2023:59:59&page=1&limit=10", builder.toString()); assertEquals("http://127.0.0.1/devicerecord/list?start=2022-03-31%2000:00:00&end=2022-03-31%2023:59:59&page=1&limit=10", builder.toString());
} }
@Test @Test
@ -447,7 +450,7 @@ public class UrlBuilderTest {
// 如果用户已经做了%编码不应该重复编码 // 如果用户已经做了%编码不应该重复编码
final String url = "https://hutool.cn/v1.0?privateNum=%2B8616512884988"; final String url = "https://hutool.cn/v1.0?privateNum=%2B8616512884988";
final String s = UrlBuilder.of(url, null).setCharset(CharsetUtil.UTF_8).toString(); final String s = UrlBuilder.of(url, null).setCharset(CharsetUtil.UTF_8).toString();
Assertions.assertEquals(url, s); assertEquals(url, s);
} }
@Test @Test
@ -455,7 +458,7 @@ public class UrlBuilderTest {
// &自动转换为& // &自动转换为&
final String url = "https://hutool.cn/a.mp3?Expires=1652423884&key=JMv2rKNc7Pz&sign=12zva00BpVqgZcX1wcb%2BrmN7H3E%3D"; final String url = "https://hutool.cn/a.mp3?Expires=1652423884&key=JMv2rKNc7Pz&sign=12zva00BpVqgZcX1wcb%2BrmN7H3E%3D";
final UrlBuilder of = UrlBuilder.of(url, null); final UrlBuilder of = UrlBuilder.of(url, null);
Assertions.assertEquals(url.replace("&", "&"), of.toString()); assertEquals(url.replace("&", "&"), of.toString());
} }
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
@ -465,14 +468,14 @@ public class UrlBuilderTest {
.addQuery("param[0].field", "编码") .addQuery("param[0].field", "编码")
.toURI() .toURI()
.toString(); .toString();
Assertions.assertEquals("http://127.0.0.1:8080?param%5B0%5D.field=%E7%BC%96%E7%A0%81", duplicate); assertEquals("http://127.0.0.1:8080?param%5B0%5D.field=%E7%BC%96%E7%A0%81", duplicate);
final String normal = UrlBuilder.ofHttp("127.0.0.1:8080") final String normal = UrlBuilder.ofHttp("127.0.0.1:8080")
.addQuery("param[0].field", "编码") .addQuery("param[0].field", "编码")
.toURL() .toURL()
.toURI() .toURI()
.toString(); .toString();
Assertions.assertEquals(duplicate, normal); assertEquals(duplicate, normal);
} }
@Test @Test
@ -480,7 +483,7 @@ public class UrlBuilderTest {
final UrlBuilder builder = UrlBuilder.ofHttp("127.0.0.1:8080") final UrlBuilder builder = UrlBuilder.ofHttp("127.0.0.1:8080")
.addQuery("param[0].field", "编码"); .addQuery("param[0].field", "编码");
Assertions.assertEquals("127.0.0.1:8080", builder.getAuthority()); assertEquals("127.0.0.1:8080", builder.getAuthority());
} }
@Test @Test
@ -495,15 +498,24 @@ public class UrlBuilderTest {
@Test @Test
public void ofHttpTest() { public void ofHttpTest() {
UrlBuilder ofHttp = UrlBuilder.ofHttp("http://hutool.cn"); UrlBuilder ofHttp = UrlBuilder.ofHttp("http://hutool.cn");
Assertions.assertEquals("http://hutool.cn", ofHttp.toString()); assertEquals("http://hutool.cn", ofHttp.toString());
ofHttp = UrlBuilder.ofHttp("https://hutool.cn"); ofHttp = UrlBuilder.ofHttp("https://hutool.cn");
Assertions.assertEquals("https://hutool.cn", ofHttp.toString()); assertEquals("https://hutool.cn", ofHttp.toString());
ofHttp = UrlBuilder.ofHttp("hutool.cn"); ofHttp = UrlBuilder.ofHttp("hutool.cn");
Assertions.assertEquals("http://hutool.cn", ofHttp.toString()); assertEquals("http://hutool.cn", ofHttp.toString());
ofHttp = UrlBuilder.ofHttp("hutool.cn?old=http://aaa"); ofHttp = UrlBuilder.ofHttp("hutool.cn?old=http://aaa");
Assertions.assertEquals("http://hutool.cn?old=http://aaa", ofHttp.toString()); assertEquals("http://hutool.cn?old=http://aaa", ofHttp.toString());
}
@Test
public void pathWithColonTest() {
// https://www.ietf.org/rfc/rfc3986.html#section-3.3
// 此处Path中是允许有`:`之前理解有误应该是相对URI的第一个segment中不允许有`:`
final UrlPath httpUrl = UrlPath.of("/a:1/b:1/c:1/d:1/", CharsetUtil.UTF_8);
final String build = httpUrl.build(CharsetUtil.UTF_8);
assertEquals("/a:1/b:1/c:1/d:1/", build);
} }
} }