Почему имя хоста объявлено недействительным при создании URI

17

Запуск этого кода с 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();
}

приводит к этой ошибке: java.net.URISyntaxException: Illegal character in hostname at index 13: //5-12-145-35_s-81:443

Откуда эта ошибка, учитывая, что все символы имени хоста кажутся допустимыми, в соответствии с типами символов URI ?


Если я использую эти URL-адреса: //5-12-145-35_s-81:443или /5-12-145-35_s-81:443ошибка исчезла.


Из комментариев я понимаю, что, согласно RFC-2396 , имя хоста не может содержать символов подчеркивания.

Вопрос, который все еще остается, состоит в том, почему имя хоста, начинающееся с косой черты или двойной косой черты, может содержать подчеркивания?

Евгений Ковачи
источник
1
@ernest_k Схема не указана, она нулевая.
Евгений Ковачи
если вы все еще хотите _ в url @ fg78nc, обходной путь будет работать для вас. Не используйте /, потому что имя хоста будет недействительным и не будет создавать поле
salesh
3
См. RFC-2396 раздел 3.2.2. Имя хоста в URI может быть только одной или несколькими группами буквенно-цифровых символов + -, разделенных точками
Марк Роттвил
@MarkRotteveel java.net.URI не соответствует последним спецификациям
fg78nc
@ fg78nc Хотя RFC-3986 ослабляет его, он все же упоминает, что «зарегистрированное имя, предназначенное для поиска в DNS, использует синтаксис, определенный в разделе 3.5 [RFC1034] и разделе 2.1 [RFC1123]». и это в основном синтаксис RFC-2396, раздел 3.2.2.
Марк Роттвил

Ответы:

8

Имя хоста должно соответствовать следующему синтаксису:

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

Как видите, только так .и -разрешено, _нет.


Затем вы говорите, что //5-12-145-35_s-81:443разрешено, и это так, но не для имени хоста .

Чтобы увидеть, как это получается:

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

Вывод

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

Как вы можете видеть, если у authorityфайла есть действительное имя хоста, то hostи portанализируются, но когда они недействительны, они authorityобрабатываются как текст произвольной формы и больше не анализируются.


ОБНОВИТЬ

Из комментария:

System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))выходы: /// 5-12-145-35_s-81: 443. Я даю это как имя хоста

URIКонструктор вы вызываете удобный метод, и он просто строит полную строку URI , а затем разбирает это.

Проходя "5-12-145-35_s-81", 443становится //5-12-145-35_s-81:443.
Проходя "/5-12-145-35_s-81", 443становится ///5-12-145-35_s-81:443.

Во-первых, это хост и порт , и он не может разобрать.
Во втором авторитетная часть пуста и /5-12-145-35_s-81:443является путем .

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

Вывод

uri = ///5-12-145-35_s-81:443
  authority = null
  host = null
  port = -1
  path = /5-12-145-35_s-81:443
Andreas
источник
Теперь я понимаю, но почему, допустим /a_b, это разрешено. Единственное отличие состоит в том, что этот является абсолютным, а не относительным
Евгений Ковачи
System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))выходы: ///5-12-145-35_s-81:443. Я даю это как имя хоста.
Евгений Ковачи
Такое поведение (когда имя хоста является абсолютным) странно, если не сказать больше. Конструктор URI дает имя хоста и порт, а полученный URI не имеет ни одного, а только путь.
Евгений Ковачи
5

Ошибка не в Java, а в именовании хоста, поскольку подчеркивание не является допустимым символом в имени хоста. Хотя широко используется неправильно, Java отказывается обрабатывать такие имена хостов

salesh
источник
Это /5-12-145-35_s-81:443законно.
Евгений Ковачи
2

Подчеркивания не поддерживаются в URI.

Хотя имя хоста может не содержать других символов, например символа подчеркивания (_), другие имена DNS могут содержать подчеркивание. [5] [6] Это ограничение было снято в RFC 2181, раздел 11. Системы, такие как DomainKeys и служебные записи, используют подчеркивание как средство, чтобы гарантировать, что их специальный символ не будет перепутан с именами хостов. Например, _http._sctp.www.example.com указывает указатель службы для хоста веб-сервера с поддержкой SCTP (www) в домене example.com. Несмотря на стандарт, Chrome, Firefox, Internet Explorer, Edge и Safari допускают подчеркивание в именах хостов, хотя файлы cookie в IE работают неправильно, если какая-либо часть имени хоста содержит символ подчеркивания

Википедия

От Javadocs:

public URI (String str) throws URISyntaxException Throws: URISyntaxException - Если данная строка нарушает RFC 2396, как дополнено вышеупомянутыми отклонениями

Javadocs

(Хаки) Решение:

    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

Это было сообщено как - ошибка JDK

fg78nc
источник
1
Вау, это хакерское решение. Вы можете утверждать, что это может сломаться в будущем, так как он предполагает внутреннее описание внутреннего класса и использует отражение для прямого доступа к нему. Таким образом, реализация может измениться с любым выпуском Java, в этом случае это может сломаться. +1 за предоставление решения, хотя.
Забузард
Как бы я не хотел использовать этот обходной путь, проблема с этими вещами заключается в том, что упоминает Забуза. + Если мы начнем подчиняться правилам, все начнет медленно разрушаться. Есть веская причина, почему это не работает в первую очередь.
продажа
@salesh И что это за хорошая причина?
fg78nc
«Такие системы, как DomainKeys и служебные записи, используют подчеркивание как средство, чтобы гарантировать, что их специальный символ не будет путать с именами хостов». Википедия и есть хороший ответ здесь Quora
Salesh
1
Если вы сделаете это, вы получите в nullкачестве хоста.
fg78nc