Por que o nome do host é declarado inválido ao criar um URI

17

Executando este código com o 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();
}

resulta neste erro: java.net.URISyntaxException: Illegal character in hostname at index 13: //5-12-145-35_s-81:443

De onde vem esse erro, considerando que todos os caracteres do nome do host parecem legítimos, de acordo com Tipos de caracteres URI ?


Se eu usar esses URLs: //5-12-145-35_s-81:443ou /5-12-145-35_s-81:443o erro se foi.


Pelos comentários, entendo que, de acordo com a RFC-2396 , o nome do host não pode conter caracteres sublinhados.

A questão que ainda se mantém é por que um nome de host que começa com barra ou barra dupla pode conter sublinhados?

Eugen Covaci
fonte
11
@ernest_k O esquema não é dado, é nulo.
Eugen Covaci
se você ainda quiser _ in url @ fg78nc, a solução funcionará para você. Não use / porque o nome do host será inválido e não criará campo
salesh 15/02
3
Consulte a seção 3.2.2 da RFC-2396 . Um nome de host em um URI pode ser apenas um ou mais grupos de alfanuméricos + -, separados por pontos
Mark Rotteveel 15/02
@MarkRotteveel java.net.URI não está atualizado com as últimas especificações
fg78nc 15/02
@ fg78nc Embora o RFC-3986 o relaxe, ele ainda menciona que "Um nome registrado destinado à pesquisa no DNS usa a sintaxe definida na Seção 3.5 de [RFC1034] e na Seção 2.1 de [RFC1123]". , e essa é basicamente a sintaxe da seção 3.2.2 da RFC-2396.
Mark Rotteveel 15/02

Respostas:

8

O nome do host deve corresponder à seguinte sintaxe:

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

Como você pode ver, apenas .e -é permitido, _não é.


Você então diz que //5-12-145-35_s-81:443é permitido, e é, mas não para o nome do host .

Para ver como isso ocorre:

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());

Resultado

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

Como você pode ver, quando authoritytem um nome de host válido, o hosteport são analisados, mas quando não é válido, o authorityé tratado como texto de forma livre, e não analisado mais adiante.


ATUALIZAR

Do comentário:

System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))saídas: /// 5-12-145-35_s-81: 443. Estou dando como nome de host

o URI construtor que você está chamando é um método de conveniência, e ele cria uma cadeia de URIs completa e analisa isso.

Passar "5-12-145-35_s-81", 443se torna //5-12-145-35_s-81:443.
Passar "/5-12-145-35_s-81", 443se torna ///5-12-145-35_s-81:443.

No primeiro, é um host e uma porta , e falha ao analisar.
No segundo, a parte da autoridade está vazia e /5-12-145-35_s-81:443é um caminho .

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());

Resultado

uri = ///5-12-145-35_s-81:443
  authority = null
  host = null
  port = -1
  path = /5-12-145-35_s-81:443
Andreas
fonte
Agora eu entendo, mas por que, digamos /a_b, é permitido. A única diferença é que este é absoluto, não relativo
Eugen Covaci 15/02
System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))saídas: ///5-12-145-35_s-81:443. Estou dando como nome de host.
Eugen Covaci
Esse comportamento (quando o nome do host é absoluto) é estranho, para dizer o mínimo. O construtor do URI está fornecendo um nome de host e uma porta, e o URI resultante não possui um, apenas um caminho.
Eugen Covaci 15/02
5

O bug não está em Java, mas em nomear o host, pois um sublinhado não é um caractere válido em um nome de host. Embora amplamente utilizado incorretamente, o Java se recusa a lidar com esses nomes de host

salesh
fonte
Este /5-12-145-35_s-81:443é legal.
Eugen Covaci
2

Sublinhados não são suportados em URIs.

Enquanto um nome de host não pode conter outros caracteres, como o caractere sublinhado (_), outros nomes DNS podem conter o sublinhado. [5] [6] Essa restrição foi levantada pela RFC 2181, Seção 11. Sistemas como DomainKeys e registros de serviço usam o sublinhado como um meio de garantir que seu caractere especial não seja confundido com nomes de host. Por exemplo, _http._sctp.www.example.com especifica um ponteiro de serviço para um host de servidor da Web compatível com SCTP (www) no domínio example.com. Não obstante o padrão, Chrome, Firefox, Internet Explorer, Edge e Safari permitem sublinhados nos nomes de host, embora os cookies no IE não funcionem corretamente se qualquer parte do nome do host contiver um caractere de sublinhado

Wikipedia

De Javadocs:

URI público (String str) lança URISyntaxException Throws: URISyntaxException - se a string especificada viola o RFC 2396, aumentada pelos desvios acima

Javadocs

Solução (Hacky):

    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

Isso foi relatado como - bug do JDK

fg78nc
fonte
11
Uau, essa é uma solução hacky. Você pode declarar que isso pode ser interrompido no futuro, pois assume pressupostos sobre uma classe interna e usa a reflexão para acessá-la diretamente. Portanto, a implementação pode mudar com qualquer versão do Java; nesse caso, isso pode ser interrompido. 1 por fornecer uma solução .
Zabuzard 15/02
Por mais que eu quisesse colocar essa solução alternativa, o problema com essas coisas é exatamente o que Zabuza menciona. + Se começarmos a obedecer às regras, tudo começará lentamente a entrar em colapso. Há uma boa razão para isso não funcionar em primeiro lugar.
salesh 15/02
@ salesh E qual é essa boa razão?
fg78nc 15/02
"Sistemas como DomainKeys e registros de serviço usam o sublinhado como um meio de garantir que seu caractere especial não seja confundido com nomes de host". wikipedia e há uma boa resposta aqui quora
salesh 15/02
11
Se você fizer isso, receberá nullcomo host.
fg78nc 15/02