创建URI时为什么主机名无效


17

使用JDK 1.8运行以下代码:

try {
    System.out.println( new URI(null, null, "5-12-145-35_s-81", 443, null, null, null));
} catch (URISyntaxException e) {
    e.printStackTrace();
}

导致此错误: java.net.URISyntaxException: Illegal character in hostname at index 13: //5-12-145-35_s-81:443

根据URI字符的类型,考虑到所有主机名字符似乎都是合法的,此错误从何而来?


如果我使用这些URL://5-12-145-35_s-81:443/5-12-145-35_s-81:443错误消失了。


从注释中可以理解,根据RFC-2396,主机名不能包含任何下划线字符。

仍然存在的问题是,为什么允许以斜杠或双斜杠开头的主机名包含下划线?


1
@ernest_k未提供方案,它为null。
Eugen Covaci

如果您仍然希望_在url中使用@ fg78nc,则解决方法将为您工作。不要使用/,因为主机名将无效并且不会创建字段
salesh

3
请参阅RFC-2396第3.2.2节。URI中的主机名只能是一个或多个字母数字+组-,并用点号分隔
Mark Rotteveel

@MarkRotteveel java.net.URI不是最新的规范
fg78nc

@ fg78nc尽管RFC-3986放宽了它,但它仍然提到“用于DNS中查找的注册名称使用[RFC1034]的3.5节和[RFC1123]的2.1节中定义的语法。” ,基本上就是RFC-2396第3.2.2节的语法。
Mark Rotteveel

Answers:


8

主机名必须与以下语法匹配:

hostname      = domainlabel [ "." ] | 1*( domainlabel "." ) toplabel [ "." ]
domainlabel   = alphanum | alphanum *( alphanum | "-" ) alphanum
toplabel      = alpha | alpha *( alphanum | "-" ) alphanum

如您所见,只有.-被允许,_是不允许的。


然后,您说这//5-12-145-35_s-81:443是允许的,但允许,但是对于主机名则不允许。

要查看结果如何:

URI uriBadHost = URI.create("//5-12-145-35_s-81:443");
System.out.println("uri = " + uriBadHost);
System.out.println("  authority = " + uriBadHost.getAuthority());
System.out.println("  host = " + uriBadHost.getHost());
System.out.println("  port = " + uriBadHost.getPort());
URI uriGoodHost = URI.create("//example.com:443");
System.out.println("uri = " + uriGoodHost);
System.out.println("  authority = " + uriGoodHost.getAuthority());
System.out.println("  host = " + uriGoodHost.getHost());
System.out.println("  port = " + uriGoodHost.getPort());

输出量

uri = //5-12-145-35_s-81:443
  authority = 5-12-145-35_s-81:443
  host = null
  port = -1
uri = //example.com:443
  authority = example.com:443
  host = example.com
  port = 443

如您所见,当authority拥有有效的主机名时,将解析hostport,而当其无效时,则将authority其视为自由格式的文本,并且不再进行进一步解析。


更新

来自评论:

System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))输出:/// 5-12-145-35_s-81:443。我给它作为主机名

URI您要调用的构造函数是一种便捷方法,它可以简单地构建完整的URI字符串,然后对其进行解析。

通过"5-12-145-35_s-81", 443成为//5-12-145-35_s-81:443
通过"/5-12-145-35_s-81", 443成为///5-12-145-35_s-81:443

首先,它是主机端口,并且无法解析。
在第二部分中,授权部分为空,并且/5-12-145-35_s-81:443路径

URI uri1 = new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null);
System.out.println("uri = " + uri1);
System.out.println("  authority = " + uri1.getAuthority());
System.out.println("  host = " + uri1.getHost());
System.out.println("  port = " + uri1.getPort());
System.out.println("  path = " + uri1.getPath());

输出量

uri = ///5-12-145-35_s-81:443
  authority = null
  host = null
  port = -1
  path = /5-12-145-35_s-81:443

现在我明白了,但是为什么/a_b允许呢?唯一的不同是,这是绝对的,而不是相对的
Eugen Covaci

System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))输出:///5-12-145-35_s-81:443。我给它作为主机名。
Eugen Covaci

至少可以说,这种行为(当主机名是绝对的时)很奇怪。URI的构造函数提供了一个主机名和一个端口,并且生成的URI既没有路径,也只有路径。
Eugen Covaci

5

该错误不在Java中,而是在命名主机时,因为下划线不是主机名中的有效字符。尽管广泛使用不当,但Java拒绝处理此类主机名


/5-12-145-35_s-81:443是合法的。
Eugen Covaci

2

URI不支持下划线。

虽然主机名可能不包含其他字符,例如下划线字符(_),但其他DNS名称可能包含下划线。[5] [6] RFC 2181第11节取消了此限制。DomainKeys和服务记录之类的系统使用下划线作为确保其特殊字符不会与主机名混淆的手段。例如,_http._sctp.www.example.com为example.com域中支持SCTP的Web服务器主机(www)指定了服务指针。尽管有此标准,但是Chrome,Firefox,Internet Explorer,Edge和Safari允许在主机名中使用下划线,但是如果主机名中的任何部分包含下划线字符,则IE中的Cookie均无法正常工作

维基百科

从Javadocs:

公共URI(String str)引发URISyntaxException抛出:URISyntaxException-如果给定的字符串违反了RFC 2396(由上述偏差增加)

Java文档

(棘手的)解决方案:

    URI url = URI.create("https://5-12-145-35_s-8:8080");

    System.out.println(url.getHost()) // null

    if (url.getHost() == null) {
        final Field hostField = URI.class.getDeclaredField("host");
        hostField.setAccessible(true);
        hostField.set(url, "5-12-145-35_s-81");
    }
    System.out.println(url.getHost()); // 5-12-145-35_s-81

据报道为-JDK错误


1
哇,这是一个骇人听闻的解决方案。您可能会说这种情况将来可能会中断,因为它假设有关内部类的内部信息,并使用反射直接访问它。因此,任何Java版本都可能更改实现,在这种情况下可能会中断。+1可提供解决方案
Zabuzard

我本来不想提出这种解决方法,但这些事情的问题正是Zabuza提到的。+如果我们开始遵守规则,一切都会慢慢开始崩溃。有一个很好的理由说明为什么它不能一开始就起作用。
salesh

@salesh,那是什么好理由?
fg78nc

“诸如DomainKeys和服务记录之类的系统使用下划线来确保其特殊字符不会与主机名混淆。” 维基百科,这里有一个很好的答案quora
salesh

1
如果这样做,您将null作为主持人。
fg78nc
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.