16 Ноябрь 2017

Comments

0
 Ноябрь 16, 2017
 0

Вводная

На просторах интернета не так давно, по меркам интернета, появилась возможность регистрировать доменные имена с использованием национальных символов. На момент написания этой статьи эту опцию поддерживали домены .su и .рф

Всё бы ничего, но для разработчиков это создало некоторые трудности в работе с такими доменами. Трудность собственно состоит в том, что если вам необходимо сделать redirect по абсолютному пути, то вас подстеригают определённые проблемы, ваш браузер просто откажется это делать 🙂 Почему? Потому, что в соответствии с RFC 3490 домены такого порядка должны быть преобразованы в так называемый punycode,

Redirect with Punycode

т.е. если простыми словами, кажный домен не соответствующий ASCII должен быть преобразован в ASCII символы и только после этого можно использовать его на просторах интернета.

Обратите внимание на то, как именно происходит редирект: В заголовках передаётся параметр Location, который и сообщает браузеру, что он должен не просто сделать refresh, а именно уйти с этой страницы туда, гуда ведёт его Location, даже если это совершенно другой домен. Но вот курьёз, если в локатион запихать домен состоящий из национальных символов, то браузер сделает слещующее, он просто вырезет неизвестные ему символы и сделает refresh.

Почему так происходит? Дело в том, что в Location должно попадать значение уже подготовленное, т.е. если это доменное имя состоящее из национальных символов, оно должно быть преобразовано в соответствии со спецификацией RFC 3490, т.е. в ASCII символы.

Но и тут не всё так просто. Вы не можете преобразовать полный URI, т.е. вместе с протоколом (Protocol), портом(Port) и запросом(Query), модификацию должно пройти только доменное имя, т.е если мы имеем такой uri https://ит-весна.рф:8080/?page=1&sort=id, то соответственно мы должны расчленить этот URI

 

  • https:// — protocol
  • ит-весна.рф — доменное имя
  • :8080 — собственно порт
  • /?page=1&sort=id — запрос

 

Вот из всего этого мы должны вычленить только доменное имя — ит-весна.рф  и подвергнуть его преобразованию.

И вот мы достигли наконец сего действия, с чем вас и поздравляю.

Механизм

В разных языках програмирования используются разные методы преобразования, в php например это функция idn_to_ascii принимает всего 3 параметра и 2 из них необязательные 🙂 Вобщем передаёте доменное имя и получаете результат. Но я не об этом, моя специализация java, потому и опишу эту часть.

В языке java несколько сложнее, дело в том, что для того, чтобы выделить доменное имя из URI, недостаточно использовать класс  java.net.URL Как мы могли бы сделать:

public static String getHost(String url) throws URISyntaxException {
    URI uri = new URI(url);
    String domain = uri.getHost();
    return domain.startsWith("www.") ? domain.substring(4) : domain;
}
...
this.getHost("https://ит-весна.рф/");

Так к сожалению ничего не выйдет, класс URI понятия не имеет о доменах в национальных кодировках, потому вы получите URISyntaxException, може т в jdk 1.9 что то изменится, но что то мне подсказывает, что это класс менять не станут.

Я сдела так, сразу оговорюсь, это решение не идеально и если вы знаете другой вариант, я был бы вам очень признателен, если вы поделитесь своими идеями со мной в коментариях.

Итак, мой код такой:

private void parse(String uri) {
    Pattern pattern = Pattern.compile("^(https?)://(www)?\\.?([^/]+)(/.*)?$");
    Matcher matcher = pattern.matcher(uri);

    if(matcher.matches()) {
        this.uri = new URI();

        this.uri.setProtocol(matcher.group(1));
        this.uri.setCname(matcher.group(2));
        this.uri.setHost(matcher.group(3));
        this.uri.setQuery(matcher.group(4));
    }
}
...
String host = this.getHost();

Ну и после чего, преобразуем доменное имя в соответствии со спецификацией RFC 3490

private String getIDNHost() {
    String host = this.getHost();
    return IDN.toASCII(host);
}

Теперь вам осталось собрать всё вместе и сделать долгожданный redirect 🙂

Удачи!

Добавить комментарий