Pourquoi le nom d'hôte est-il déclaré invalide lors de la création d'un URI

17

Exécuter ce code avec 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();
}

entraîne cette erreur: java.net.URISyntaxException: Illegal character in hostname at index 13: //5-12-145-35_s-81:443

D'où vient cette erreur, étant donné que tous les caractères du nom d'hôte semblent légitimes, selon types de caractères URI ?


Si j'utilise ces URL: //5-12-145-35_s-81:443 ou /5-12-145-35_s-81:443l'erreur a disparu.


D'après les commentaires, je comprends que, selon RFC-2396 , le nom d'hôte ne peut contenir aucun caractère de soulignement.

La question qui se pose toujours est pourquoi un nom d'hôte commençant par une barre oblique ou une double barre oblique est autorisé à contenir des traits de soulignement?

Eugen Covaci
la source
1
@ernest_k Le schéma n'est pas donné, il est nul.
Eugen Covaci
si vous voulez toujours _ dans url @ fg78nc, la solution fonctionnera pour vous. N'utilisez pas / car le nom d'hôte sera invalide et ne créera pas de champ
salesh
3
Voir RFC-2396 section 3.2.2. Un nom d'hôte dans un URI ne peut être qu'un ou plusieurs groupes de caractères alphanumériques + -, séparés par des points
Mark Rotteveel
@MarkRotteveel java.net.URI n'est pas à jour avec les dernières spécifications
fg78nc
@ fg78nc Bien que le RFC-3986 le détende, il mentionne toujours qu ' "un nom enregistré destiné à la recherche dans le DNS utilise la syntaxe définie dans la section 3.5 de [RFC1034] et la section 2.1 de [RFC1123]." , et c'est essentiellement la syntaxe de la section 3.2.2 de la RFC-2396.
Mark Rotteveel

Réponses:

8

Le nom d'hôte doit correspondre à la syntaxe suivante:

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

Comme vous pouvez le voir, seuls .et -sont autorisés,_ ne l'est pas.


Vous dites alors que //5-12-145-35_s-81:443c'est autorisé, et c'est le cas, mais pas pour nom d'hôte .

Pour voir comment cela se déroule:

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

Production

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

Comme vous pouvez le voir, lorsque le authoritya un nom d'hôte valide, le hostet portsont analysés, mais lorsqu'ils ne sont pas valides, le authorityest traité comme du texte de forme libre et n'est pas analysé davantage.


MISE À JOUR

Du commentaire:

System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))sorties: /// 5-12-145-35_s-81: 443. Je le donne comme nom d'hôte

Le URIconstructeur que vous appelez est une méthode pratique, et il crée simplement une chaîne URI complète, puis l'analyse.

Le passage "5-12-145-35_s-81", 443devient //5-12-145-35_s-81:443.
Le passage "/5-12-145-35_s-81", 443devient ///5-12-145-35_s-81:443.

Dans le premier, c'est un hôte et un port , et ne parvient pas à analyser.
Dans le second, la partie autorité est vide et /5-12-145-35_s-81:443est un chemin .

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

Production

uri = ///5-12-145-35_s-81:443
  authority = null
  host = null
  port = -1
  path = /5-12-145-35_s-81:443
Andreas
la source
Maintenant je comprends, mais pourquoi, disons /a_b, est autorisé. La seule différence est que celui-ci est absolu, pas relatif
Eugen Covaci
System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))sorties: ///5-12-145-35_s-81:443. Je le donne comme nom d'hôte.
Eugen Covaci
Ce comportement (lorsque le nom d'hôte est absolu) est pour le moins étrange. Le constructeur de l'URI donne un nom d'hôte et un port, et l'URI résultant n'a ni un, ni un seul chemin.
Eugen Covaci
5

Le bogue n'est pas en Java mais en nommant l'hôte, car un trait de soulignement n'est pas un caractère valide dans un nom d'hôte. Bien que largement utilisé de manière incorrecte, Java refuse de gérer de tels noms d'hôtes

salesh
la source
Celui-ci /5-12-145-35_s-81:443est légal.
Eugen Covaci
2

Les traits de soulignement ne sont pas pris en charge dans les URI.

Alors qu'un nom d'hôte ne peut pas contenir d'autres caractères, comme le caractère de soulignement (_), d'autres noms DNS peuvent contenir le trait de soulignement. [5] [6] Cette restriction a été levée par la RFC 2181, Section 11. Des systèmes tels que DomainKeys et les enregistrements de service utilisent le trait de soulignement comme moyen de s'assurer que leur caractère spécial n'est pas confondu avec les noms d'hôte. Par exemple, _http._sctp.www.example.com spécifie un pointeur de service pour un hôte de serveur Web compatible SCTP (www) dans le domaine example.com. Nonobstant la norme, Chrome, Firefox, Internet Explorer, Edge et Safari autorisent les traits de soulignement dans les noms d'hôte, bien que les cookies dans IE ne fonctionnent pas correctement si une partie du nom d'hôte contient un caractère de soulignement

Wikipédia

De Javadocs:

URI public (String str) lève URISyntaxException Lance: URISyntaxException - Si la chaîne donnée viole la RFC 2396, augmentée par les écarts ci-dessus

Javadocs

(Hacky) Solution:

    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

Cela a été signalé comme - bogue JDK

fg78nc
la source
1
Wow, c'est une solution hacky. Vous pouvez déclarer que cela peut se briser à l'avenir, car il suppose des internes sur une classe interne et utilise la réflexion pour y accéder directement. Ainsi, l'implémentation peut changer avec n'importe quelle version de Java, auquel cas cela pourrait casser. +1 pour avoir fourni une solution .
Zabuzard
Autant je voulais mettre cette solution de contournement que je ne l'ai pas fait, le problème avec ces choses est exactement ce que mentionne Zabuza. + Si nous commençons à obéir aux règles, tout commencera lentement à s'effondrer. Il y a une bonne raison pour laquelle cela ne fonctionne pas en premier lieu.
salesh
@salesh Et quelle est cette bonne raison?
fg78nc
"Les systèmes tels que DomainKeys et les enregistrements de service utilisent le trait de soulignement comme moyen de s'assurer que leur caractère spécial n'est pas confondu avec les noms d'hôte." wikipedia et il y a une bonne réponse ici quora
salesh
1
Si vous faites cela, vous serez nullhébergé.
fg78nc