java.net.URI 获取带下划线的主机

java.net.URI get host with underscores

我发现该方法有一个奇怪的行为:

import java.net.URI

    URI url = new URI("https://pmi_artifacts_prod.s3.amazonaws.com");
    System.out.println(url.getHost()); /returns NULL
    URI url2 = new URI("https://s3.amazonaws.com");
    System.out.println(url2.getHost());  //returns s3.amazonaws.com

`

我希望第一个 url.getHost() 成为 pmi_artifacts_prod.s3.amazonaws.com,但它给了我 NULL。原来问题出在域名中的下划线,这是一个已知的错误,但仍然可以做些什么,因为我需要准确地使用这个主机?

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

https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_hostnames

可能的解决方法:

public static void main(String...a) throws URISyntaxException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
    URI url = new URI("https://pmi_artifacts_prod.s3.amazonaws.com");
    System.out.println(url.getHost()); //NULL


    URI uriObj = new URI("https://pmi_artifacts_prod.s3.amazonaws.com");
    if (uriObj.getHost() == null) {
        final Field hostField = URI.class.getDeclaredField("host");
        hostField.setAccessible(true);
        hostField.set(uriObj, "pmi_artifacts_prod.s3.amazonaws.com");
    }
    System.out.println(uriObj.getHost()); //pmi_artifacts_prod.s3.amazonaws.com


    URI url2 = new URI("https://s3.amazonaws.com");
    System.out.println(url2.getHost());  //s3.amazonaws.com
}

可以通过修补将下划线支持直接添加到 URI 中:

public static void main(String[] args) throws Exception {
    patchUriField(35184372088832L, "L_DASH");
    patchUriField(2147483648L, "H_DASH");
    
    URI s = URI.create("http://my_favorite_host:3892");
    // prints "my_favorite_host"
    System.out.println(s.getHost());
}

private static void patchUriField(Long maskValue, String fieldName)
        throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
        Field field = URI.class.getDeclaredField(fieldName);
        
        Field modifiers = Field.class.getDeclaredField("modifiers");
        modifiers.setAccessible(true);
        modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        
        field.setAccessible(true);
        field.setLong(null, maskValue);
}

注意虽然

new URI("https://pmi_artifacts_prod.s3.amazonaws.com");

不会抛出并且@Vurtatoo 提供的解决方法适用于这种情况,它无法处理 url 例如 https://a_b?c={1}

我也发现

new URI("https://a_b?c={1}")

会抛出但是

new URI("https://a_b?c=1")

不会。

不确定为什么会这样,但我的收获是我们不应该对 Java URI class 的实现细节做出任何假设。如果您必须使用 Java URI,最好分叉源代码并进行所需的更改。