Dlaczego podczas tworzenia identyfikatora URI nazwa hosta została uznana za niepoprawną?

17

Uruchamianie tego kodu z 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();
}

powoduje ten błąd: java.net.URISyntaxException: Illegal character in hostname at index 13: //5-12-145-35_s-81:443

Skąd bierze się ten błąd, biorąc pod uwagę, że wszystkie znaki nazwy hosta wydają się zgodne z prawdą, zgodnie z typami znaków URI ?


Jeśli użyję tych adresów URL: //5-12-145-35_s-81:443lub /5-12-145-35_s-81:443błąd zniknie.


Z komentarzy rozumiem, że zgodnie z RFC-2396 nazwa hosta nie może zawierać znaków podkreślenia.

Pozostaje pytanie, dlaczego nazwa hosta zaczynająca się od ukośnika lub ukośnika podwójnego może zawierać podkreślenia?

Eugen Covaci
źródło
1
@ernest_k Schemat nie jest podany, jest zerowy.
Eugen Covaci
jeśli nadal chcesz _ w adresie URL @ fg78nc, obejście zadziała. Nie używaj / ponieważ nazwa hosta będzie niepoprawna i nie utworzy pola
saleh
3
Zobacz RFC-2396 sekcja 3.2.2. Nazwą hosta w URI może być tylko jedna lub więcej grup alfanumerycznych + -, oddzielonych kropkami
Mark Rotteveel
@MarkRotteveel java.net.URI nie jest na bieżąco z najnowszymi specyfikacjami
fg78nc
@ fg78nc Chociaż RFC-3986 go rozluźnia, nadal wspomina, że „Zarejestrowana nazwa przeznaczona do wyszukiwania w DNS korzysta ze składni zdefiniowanej w sekcji 3.5 [RFC1034] i sekcji 2.1 [RFC1123]”. i taka jest w zasadzie składnia sekcji 3.2.2 RFC-2396.
Mark Rotteveel

Odpowiedzi:

8

Nazwa hosta musi być zgodna z następującą składnią:

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

Jak widać, tylko .i -są dozwolone, _nie jest.


Mówisz wtedy, że //5-12-145-35_s-81:443jest to dozwolone, ale nie w przypadku nazwy hosta .

Aby zobaczyć, jak to wygląda:

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

Wynik

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

Jak widać, gdy authorityma poprawną nazwę hosta, hosti portsą analizowane, ale gdy nie są poprawne, authoritytraktowane jest jako dowolny tekst i nie jest dalej analizowane.


AKTUALIZACJA

Od komentarza:

System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))wyjścia: /// 5-12-145-35_s-81: 443. Podaję go jako nazwę hosta

URIKonstruktor Dzwonisz to metoda wygoda i prostota buduje pełny ciąg URI, a następnie analizuje to.

Przechodzenie "5-12-145-35_s-81", 443staje się //5-12-145-35_s-81:443.
Przechodzenie "/5-12-145-35_s-81", 443staje się ///5-12-145-35_s-81:443.

Po pierwsze, jest to host i port i nie można go parsować.
W drugiej części autorytet jest pusty i /5-12-145-35_s-81:443jest ścieżką .

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

Wynik

uri = ///5-12-145-35_s-81:443
  authority = null
  host = null
  port = -1
  path = /5-12-145-35_s-81:443
Andreas
źródło
Teraz rozumiem, ale dlaczego, powiedzmy /a_b, jest dozwolony. Jedyna różnica polega na tym, że ten jest absolutny, a nie względny
Eugen Covaci
System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))Wyjścia: ///5-12-145-35_s-81:443. Podaję go jako nazwę hosta.
Eugen Covaci
To zachowanie (gdy nazwa hosta jest bezwzględna) jest co najmniej dziwne. Konstruktor identyfikatora URI podaje nazwę hosta i port, a wynikowy identyfikator URI nie ma jednej, tylko ścieżkę.
Eugen Covaci
5

Błąd nie dotyczy języka Java, ale nazwy hosta, ponieważ znak podkreślenia nie jest prawidłowym znakiem w nazwie hosta. Chociaż powszechnie stosowany nieprawidłowo, Java odmawia obsługi takich nazw hostów

Salesh
źródło
Ten /5-12-145-35_s-81:443jest legalny.
Eugen Covaci
2

Podkreślenia nie są obsługiwane w identyfikatorach URI.

Chociaż nazwa hosta nie może zawierać innych znaków, takich jak znak podkreślenia (_), inne nazwy DNS mogą zawierać znak podkreślenia. [5] [6] Ograniczenie to zostało zniesione w RFC 2181, Rozdział 11. Systemy takie jak DomainKeys i rekordy usług używają podkreślenia jako środka zapewniającego, że ich specjalny charakter nie jest mylony z nazwami hostów. Na przykład _http._sctp.www.example.com określa wskaźnik usługi dla hosta serwera WWW z obsługą SCTP (www) w domenie example.com. Niezależnie od standardu Chrome, Firefox, Internet Explorer, Edge i Safari pozwalają na podkreślenia w nazwach hostów, chociaż pliki cookie w IE nie działają poprawnie, jeśli jakakolwiek część nazwy hosta zawiera znak podkreślenia

Wikipedia

Od Javadocs:

publiczny identyfikator URI (ciąg znaków) zgłasza wyjątek URISyntaxException: wyjątek URISyntaxException - jeśli podany ciąg narusza RFC 2396, powiększony o powyższe odchylenia

Javadocs

(Hacky) Rozwiązanie:

    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

Zgłoszono to jako - błąd JDK

fg78nc
źródło
1
Wow, to jest hacky rozwiązanie. Możesz stwierdzić, że może się to zepsuć w przyszłości, ponieważ zakłada wewnętrzne elementy dotyczące klasy wewnętrznej i używa refleksji, aby uzyskać do niej bezpośredni dostęp. W związku z tym implementacja może ulec zmianie z każdą wersją Java, w którym to przypadku może się zepsuć. +1 za dostarczenie rozwiązania .
Zabuzard
Tak bardzo, jak chciałem zastosować to obejście, czego nie zrobiłem, problem z tymi rzeczami jest właśnie tym, o czym Zabuza wspomina. + Jeśli zaczniemy przestrzegać zasad, wszystko zacznie się powoli zapadać. Jest dobry powód, dla którego to nie działa na pierwszym miejscu.
sprzedaż
@salesh A jaki jest dobry powód?
fg78nc
„Systemy takie jak DomainKeys i rekordy usług używają podkreślenia jako środka zapewniającego, że ich charakter specjalny nie jest mylony z nazwami hostów”. wikipedia i jest dobra odpowiedź tutaj Quora
salesh
1
Jeśli to zrobisz, otrzymasz nulljako host.
fg78nc