¿Por qué el nombre de host se declara no válido al crear un URI?

17

Ejecutando este código con 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();
}

da como resultado este error: java.net.URISyntaxException: Illegal character in hostname at index 13: //5-12-145-35_s-81:443

¿De dónde viene este error, teniendo en cuenta que todos los caracteres del nombre de host parecen legítimos, de acuerdo con los tipos de caracteres URI? ?


Si uso estas URL: //5-12-145-35_s-81:443o/5-12-145-35_s-81:443 el error se ha ido.


Según los comentarios, entiendo que, de acuerdo con RFC-2396 , el nombre de host no puede contener caracteres de subrayado.

La pregunta que aún se mantiene es ¿por qué un nombre de host que comienza con una barra oblicua o una barra doble puede contener guiones bajos?

Eugen Covaci
fuente
1
@ernest_k El esquema no se da, es nulo.
Eugen Covaci
si aún desea _ en url @ fg78nc, la solución funcionará para usted. No use / porque el nombre de host no será válido y no creará un campo
salesh
3
Ver RFC-2396 sección 3.2.2. Un nombre de host en un URI solo puede ser uno o más grupos de caracteres alfanuméricos + -, separados por puntos
Mark Rotteveel
@MarkRotteveel java.net.URI no está actualizado con las últimas especificaciones
fg78nc
@ fg78nc Aunque RFC-3986 lo relaja, aún menciona que "Un nombre registrado destinado a la búsqueda en el DNS utiliza la sintaxis definida en la Sección 3.5 de [RFC1034] y la Sección 2.1 de [RFC1123]". , y esa es básicamente la sintaxis de RFC-2396 sección 3.2.2.
Mark Rotteveel

Respuestas:

8

El nombre del host debe coincidir con la siguiente sintaxis:

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

Como puede ver, solo .y -están permitidos, _no lo está.


Luego dice que //5-12-145-35_s-81:443está permitido, y lo está, pero no para el nombre de host .

Para ver cómo funciona:

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

Salida

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 puede ver, cuando authoritytiene un nombre de host válido, se analiza el hosty port, pero cuando no es válido, authorityse trata como texto de forma libre y no se analiza más.


ACTUALIZAR

De comentario:

System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))salidas: /// 5-12-145-35_s-81: 443. Lo estoy dando como nombre de host

El URIconstructor al que está llamando es un método conveniente, y simplemente crea una cadena de URI completa y luego la analiza.

Pasar se "5-12-145-35_s-81", 443convierte //5-12-145-35_s-81:443.
Pasar se "/5-12-145-35_s-81", 443convierte ///5-12-145-35_s-81:443.

En el primero, es un host y un puerto , y no se analiza.
En el segundo, la parte de autoridad está vacía y /5-12-145-35_s-81:443es un camino .

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

Salida

uri = ///5-12-145-35_s-81:443
  authority = null
  host = null
  port = -1
  path = /5-12-145-35_s-81:443
Andreas
fuente
Ahora entiendo, pero por qué, digamos /a_b, está permitido. La única diferencia es que este es absoluto, no relativo
Eugen Covaci
System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))salidas: ///5-12-145-35_s-81:443. Lo estoy dando como nombre de host.
Eugen Covaci
Este comportamiento (cuando el nombre de host es absoluto) es extraño, por decir lo menos. El constructor del URI está dando un nombre de host y un puerto, y el URI resultante no tiene ninguno, solo una ruta.
Eugen Covaci
5

El error no está en Java sino en nombrar el host, ya que un guión bajo no es un carácter válido en un nombre de host. Aunque se usa incorrectamente de manera generalizada, Java se niega a manejar dichos nombres de host

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

Los guiones bajos no son compatibles con los URI.

Si bien un nombre de host no puede contener otros caracteres, como el carácter de subrayado (_), otros nombres DNS pueden contener el subrayado. [5] [6] Esta restricción fue eliminada por RFC 2181, Sección 11. Los sistemas como DomainKeys y los registros de servicio utilizan el guión bajo como un medio para garantizar que su carácter especial no se confunda con los nombres de host. Por ejemplo, _http._sctp.www.example.com especifica un puntero de servicio para un host de servidor web compatible con SCTP (www) en el dominio example.com. A pesar del estándar, Chrome, Firefox, Internet Explorer, Edge y Safari permiten guiones bajos en los nombres de host, aunque las cookies en IE no funcionan correctamente si alguna parte del nombre de host contiene un carácter de subrayado.

Wikipedia

De Javadocs:

URI público (String str) arroja URISyntaxException Throws: URISyntaxException - Si la cadena dada viola RFC 2396, como se incrementa por las desviaciones anteriores

Javadocs

(Hacky) Solución:

    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

Esto se informó como - error JDK

fg78nc
fuente
1
Wow, esa es una solución hacky. Puede indicar que esto puede romperse en el futuro, ya que está asumiendo aspectos internos sobre una clase interna y utiliza la reflexión para acceder a ella directamente. Por lo tanto, la implementación podría cambiar con cualquier versión de Java, en cuyo caso esto podría romperse. +1 para proporcionar una solución sin embargo.
Zabuzard
Por mucho que quisiera poner esta solución alternativa, el problema con estas cosas es justo lo que menciona Zabuza. + Si comenzamos a obedecer las reglas, todo comenzará a colapsar lentamente. Hay una buena razón por la cual esto no funciona en primer lugar.
salesh
@salesh ¿Y cuál es esa buena razón?
fg78nc
"Los sistemas como DomainKeys y los registros de servicio utilizan el guión bajo como un medio para garantizar que su carácter especial no se confunda con los nombres de host". wikipedia y hay una buena respuesta aquí quora
salesh
1
Si haces eso, serás el nullanfitrión.
fg78nc