Warum wird der Hostname beim Erstellen eines URI für ungültig erklärt?

17

Ausführen dieses Codes mit 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();
}

führt zu diesem Fehler: java.net.URISyntaxException: Illegal character in hostname at index 13: //5-12-145-35_s-81:443

Woher kommt dieser Fehler, wenn man bedenkt, dass alle Hostnamenzeichen je nach Art der URI-Zeichen legitim erscheinen ?


Wenn ich diese URLs verwende: //5-12-145-35_s-81:443oder /5-12-145-35_s-81:443der Fehler ist weg.


Aus den Kommentaren geht hervor, dass der Hostname gemäß RFC-2396 keine Unterstriche enthalten darf.

Die Frage, die immer noch offen ist, ist, warum ein Hostname, der mit einem Schrägstrich oder einem doppelten Schrägstrich beginnt, Unterstriche enthalten darf.

Eugen Covaci
quelle
1
@ernest_k Das Schema ist nicht angegeben, es ist null.
Eugen Covaci
Wenn Sie immer noch _ in url @ fg78nc möchten, funktioniert die Problemumgehung für Sie. Verwenden Sie / nicht, da der Hostname ungültig ist und kein Feld erstellt
salesh
3
Siehe RFC-2396, Abschnitt 3.2.2. Ein Hostname in einer URI kann nur eine oder mehrere Gruppen von alphanumerischen + sein -, die durch Punkte getrennt sind
Mark Rotteveel
@MarkRotteveel java.net.URI ist nicht auf dem neuesten Stand der
technischen Daten
@ fg78nc Obwohl RFC-3986 es lockert, wird immer noch erwähnt, dass "ein registrierter Name, der für die Suche im DNS vorgesehen ist, die in Abschnitt 3.5 von [RFC1034] und Abschnitt 2.1 von [RFC1123] definierte Syntax verwendet." , und das ist im Grunde die Syntax von RFC-2396 Abschnitt 3.2.2.
Mark Rotteveel

Antworten:

8

Der Hostname muss mit der folgenden Syntax übereinstimmen:

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

Wie Sie sehen können, ist nur .und -erlaubt, _nicht.


Sie sagen dann, dass dies //5-12-145-35_s-81:443zulässig ist, aber nicht für den Hostnamen .

Um zu sehen, wie sich das auswirkt:

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

Ausgabe

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

Wie Sie sehen können authority, ist das hostund , wenn das einen gültigen Hostnamen hat, das undport analysiert, wenn sie , aber wenn sie nicht gültig sind, werden authoritysie als Freiformtext behandelt und nicht weiter analysiert.


AKTUALISIEREN

Aus dem Kommentar:

System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))Ausgänge: /// 5-12-145-35_s-81: 443. Ich gebe es als Hostnamen

Das URI Konstruktor, den Sie aufrufen, ist eine praktische Methode, die einfach eine vollständige URI-Zeichenfolge erstellt und diese dann analysiert.

Übergeben "5-12-145-35_s-81", 443wird //5-12-145-35_s-81:443.
Vorbeigehen"/5-12-145-35_s-81", 443 wird ///5-12-145-35_s-81:443.

Im ersten Fall handelt es sich um einen Host und einen Port , die nicht analysiert werden können.
Im zweiten Teil ist der Autoritätsteil leer und /5-12-145-35_s-81:443ein Pfad .

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

Ausgabe

uri = ///5-12-145-35_s-81:443
  authority = null
  host = null
  port = -1
  path = /5-12-145-35_s-81:443
Andreas
quelle
Jetzt verstehe ich, aber warum, sagen wir /a_b, ist erlaubt. Der einzige Unterschied ist, dass dieser absolut und nicht relativ ist
Eugen Covaci
System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))Ausgänge : ///5-12-145-35_s-81:443. Ich gebe es als Hostnamen.
Eugen Covaci
Dieses Verhalten (wenn der Hostname absolut ist) ist, gelinde gesagt, seltsam. Der Konstruktor des URI gibt einen Hostnamen und einen Port an, und der resultierende URI hat keinen, nur einen Pfad.
Eugen Covaci
5

Der Fehler liegt nicht in Java, sondern in der Benennung des Hosts, da ein Unterstrich kein gültiges Zeichen in einem Hostnamen ist. Obwohl Java häufig falsch verwendet wird, weigert es sich, solche Hostnamen zu verarbeiten

salesh
quelle
Dieser /5-12-145-35_s-81:443ist legal.
Eugen Covaci
2

Unterstriche werden in URIs nicht unterstützt.

Während ein Hostname möglicherweise keine anderen Zeichen enthält, z. B. den Unterstrich (_), können andere DNS-Namen den Unterstrich enthalten. [5] [6] Diese Einschränkung wurde durch RFC 2181, Abschnitt 11, aufgehoben. Systeme wie DomainKeys und Servicedatensätze verwenden den Unterstrich, um sicherzustellen, dass ihr Sonderzeichen nicht mit Hostnamen verwechselt wird. Beispielsweise gibt _http._sctp.www.example.com einen Dienstzeiger für einen SCTP-fähigen Webserver-Host (www) in der Domäne example.com an. Ungeachtet des Standards erlauben Chrome, Firefox, Internet Explorer, Edge und Safari Unterstriche in Hostnamen, obwohl Cookies im IE nicht ordnungsgemäß funktionieren, wenn ein Teil des Hostnamens einen Unterstrich enthält

Wikipedia

Von Javadocs:

public URI (String str) löst URISyntaxException aus Auslöser: URISyntaxException - Wenn der angegebene String gegen RFC 2396 verstößt, was durch die obigen Abweichungen ergänzt wird

Javadocs

(Hacky) Lösung:

    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

Dies wurde als - JDK-Fehler gemeldet

fg78nc
quelle
1
Wow, das ist eine hackige Lösung. Sie können angeben, dass dies in Zukunft möglicherweise nicht mehr funktioniert, da Interna für eine interne Klasse vorausgesetzt werden und mithilfe der Reflexion direkt darauf zugegriffen wird. Daher kann sich die Implementierung mit jeder Java-Version ändern. In diesem Fall kann dies fehlschlagen. +1 für die Bereitstellung einer Lösung .
Zabuzard
So sehr ich diese Problemumgehung auch nicht durchführen wollte, das Problem mit diesen Dingen ist genau das, was Zabuza erwähnt. + Wenn wir anfangen, Regeln zu befolgen, beginnt alles langsam zusammenzubrechen. Es gibt einen guten Grund, warum dies überhaupt nicht funktioniert.
Verkauf
@salesh Und was ist das für ein guter Grund?
fg78nc
"Systeme wie DomainKeys und Service-Datensätze verwenden den Unterstrich, um sicherzustellen, dass ihr Sonderzeichen nicht mit Hostnamen verwechselt wird." wikipedia und es gibt hier eine gute Antwort quora
salesh
1
Wenn Sie das tun, erhalten Sie nullals Gastgeber.
fg78nc