Мне нужно написать сценарий на perl, который анализирует uris из html. В любом случае настоящая проблема заключается в том, как разрешить относительный uris.
У меня есть базовый URI (базовый href в html), например http: // a / b / c / d; p? q (давайте пройдем через rfc3986) и другие другие URI:
/ g, // g, /// g, //// g, h // g, g //// h, h /// g: f
В этом RFC, раздел 5.4.1 (ссылка выше) есть только пример // g:
"// g" = "http: // g"
А как насчет всех остальных случаев? Насколько я понял из RFC 3986, раздел 3.3, разрешено несколько слэшей. Итак, следующая резолюция верный?
"/// g" = "http: // a / b / c /// g"
Или что должно быть? Кто-нибудь может объяснить это лучше и доказать это с помощью не устаревшего RFC или документации?
Обновление # 1: Попробуйте посмотреть на этот рабочий url - https: ///stackoverflow.com////////a/////10161264/////6618577
Что тут происходит?
@Patrick Mevzek, Re "Первые два // после: являются частью схемы", Nit: Они связаны с властью (хостом). Например, http:foo/bar - действительный URI, и //stackoverflow.com - тоже.






Нет - ///g может показаться более эквивалентным /g. "Точечные сегменты" .. и . - это то, что используется для навигации вверх и вниз по иерархии с URL-адресами http. См. Также модуль URI для обработки путей в URI.
Да, это может быть упрощено браузерами, но действителен ли исходный URL-адрес?
Мне было любопытно, что сделает Mojo :: URL, поэтому я проверил. Есть большая оговорка, потому что он не претендует на строгое соответствие:
Mojo::URL implements a subset of RFC 3986, RFC 3987 and the URL Living Standard for Uniform Resource Locators with support for IDNA and IRIs.
Вот программа.
my @urls = qw(/g //g ///g ////g h//g g////h h///g:f
https:///stackoverflow.com////////a/////10161264/////6618577
);
my @parts = qw(scheme host port path query);
my $template = join "\n", map { "$_: %s" } @parts;
my $base_url = Mojo::URL->new( 'http://a/b/c/d;p?q' );
foreach my $u ( @urls ) {
my $url = Mojo::URL->new( $u )->base( $base_url )->to_abs;
no warnings qw(uninitialized);
say '-' x 40;
printf "%s\n$template", $u, map { $url->$_() } @parts
}
Вот результат:
----------------------------------------
/g
scheme: http
host: a
port:
path: /g
query: ----------------------------------------
//g
scheme: http
host: g
port:
path:
query: ----------------------------------------
///g
scheme: http
host: a
port:
path: /g
query: ----------------------------------------
////g
scheme: http
host: a
port:
path: //g
query: ----------------------------------------
h//g
scheme: http
host: a
port:
path: /b/c/h/g
query: ----------------------------------------
g////h
scheme: http
host: a
port:
path: /b/c/g/h
query: ----------------------------------------
h///g:f
scheme: http
host: a
port:
path: /b/c/h/g:f
query: ----------------------------------------
https:///stackoverflow.com////////a/////10161264/////6618577
scheme: https
host:
port:
path: /stackoverflow.com////////a/////10161264/////6618577
query:
Хорошо, но это мне не объясняет, что делать дальше)
Вы нашли ошибку в Mojo :: URL! Это не соответствует §5.2.2 вышеупомянутой спецификации. ///g должен производить http:///g (не http://a/g), а ////g должен производить http:////g (не http://a//g). Демо
Теперь http:///g - бесполезный URI (с пустыми полномочиями). Было бы интересно посмотреть, как веб-браузеры обрабатывают ///g, но придется подождать.
Я не думаю, что это ошибка в Mojo :: URL, но здесь Выпуск # 1269. Я думаю, что он следует 5.2.2 лучше, чем URI, потому что этот модуль меняет хост на пустую строку, когда это ложное значение. Он основан на том, что, по вашему мнению, находится между // и /, и если вы считаете, что определены пустые полномочия. Является ли «Компонент не определен, если связанный с ним разделитель не появляется в ссылке на URI» единственный способ быть неопределенным? Это не то, что написано, и это относится только к базе.
Я начну с подтверждения того, что все предоставленные вами URI действительны, и предоставлю результат упомянутых вами разрешений URI (и результат нескольких моих собственных):
$ perl -MURI -e'
for my $rel (qw( /g //g ///g ////g h//g g////h h///g:f )) {
my $uri = URI->new($rel)->abs("http://a/b/c/d;p?q");
printf "%-20s + %-7s = %-20s host: %-4s path: %s\n",
"http://a/b/c/d;p?q", $rel, $uri, $uri->host, $uri->path;
}
for my $base (qw( http://host/a/b/c/d http://host/a/b/c//d )) {
my $uri = URI->new("../../e")->abs($base);
printf "%-20s + %-7s = %-20s host: %-4s path: %s\n",
$base, "../../e", $uri, $uri->host, $uri->path;
}
'
http://a/b/c/d;p?q + /g = http://a/g host: a path: /g
http://a/b/c/d;p?q + //g = http://g host: g path:
http://a/b/c/d;p?q + ///g = http:///g host: path: /g
http://a/b/c/d;p?q + ////g = http:////g host: path: //g
http://a/b/c/d;p?q + h//g = http://a/b/c/h//g host: a path: /b/c/h//g
http://a/b/c/d;p?q + g////h = http://a/b/c/g////h host: a path: /b/c/g////h
http://a/b/c/d;p?q + h///g:f = http://a/b/c/h///g:f host: a path: /b/c/h///g:f
http://host/a/b/c/d + ../../e = http://host/a/e host: host path: /a/e
http://host/a/b/c//d + ../../e = http://host/a/b/e host: host path: /a/b/e
Затем мы рассмотрим синтаксис относительных URI, поскольку именно это ваш вопрос обходит стороной.
relative-ref = relative-part [ "?" query ] [ "#" fragment ]
relative-part = "//" authority path-abempty
/ path-absolute
/ path-noscheme
/ path-empty
path-abempty = *( "/" segment )
path-absolute = "/" [ segment-nz *( "/" segment ) ]
path-noscheme = segment-nz-nc *( "/" segment )
path-rootless = segment-nz *( "/" segment )
segment = *pchar ; 0 or more <pchar>
segment-nz = 1*pchar ; 1 or more <pchar> nz = non-zero
Ключевые моменты этих правил для ответа на ваш вопрос:
path-absolute) не может начинаться с //. Первый сегмент, если он предоставлен, не должен быть нулевым по длине. Если относительный URI начинается с //, то следующее должно быть authority.// может появиться в пути, потому что сегменты могут иметь нулевую длину.Теперь давайте посмотрим на каждое из предоставленных вами разрешений по очереди.
/g - это абсолютный путь path-absolute и, следовательно, действительный относительный URI (relative-ref) и, следовательно, действительный URI (URI-reference).
Анализ URI (скажем, с использованием регулярного выражения в Приложении B) дает нам следующее:
Base.scheme: "http" R.scheme: undef
Base.authority: "a" R.authority: undef
Base.path: "/b/c/d;p" R.path: "/g"
Base.query: "q" R.query: undef
Base.fragment: undef R.fragment: undef
Следуя алгоритму из §5.2.2, мы получаем:
T.path: "/g" ; remove_dot_segments(R.path)
T.query: undef ; R.query
T.authority: "a" ; Base.authority
T.scheme: "http" ; Base.scheme
T.fragment: undef ; R.fragment
Следуя алгоритму из §5.3, мы получаем:
http://a/g
//g другой. //gне абсолютный путь (path_absolute), потому что абсолютный путь не может начинаться с пустого сегмента ("/" [ segment-nz *( "/" segment ) ]).
Вместо этого он следует следующему шаблону:
"//" authority path-abempty
Анализ URI (скажем, с использованием регулярного выражения в Приложении B) дает нам следующее:
Base.scheme: "http" R.scheme: undef
Base.authority: "a" R.authority: "g"
Base.path: "/b/c/d;p" R.path: ""
Base.query: "q" R.query: undef
Base.fragment: undef R.fragment: undef
Следуя алгоритму из §5.2.2, мы получаем следующее:
T.authority: "g" ; R.authority
T.path: "" ; remove_dot_segments(R.path)
T.query: "" ; R.query
T.scheme: "http" ; Base.scheme
T.fragment: undef ; R.fragment
Следуя алгоритму из §5.3, мы получаем следующее:
http://g
Примечание: Этот сервер связывается с g!
///g похож на //g, за исключением того, что в поле "авторизация" указано пустое значение! Это на удивление верно.
Анализ URI (скажем, с использованием регулярного выражения в Приложении B) дает нам следующее:
Base.scheme: "http" R.scheme: undef
Base.authority: "a" R.authority: ""
Base.path: "/b/c/d;p" R.path: "/g"
Base.query: "q" R.query: undef
Base.fragment: undef R.fragment: undef
Следуя алгоритму из §5.2.2, мы получаем следующее:
T.authority: "" ; R.authority
T.path: "/g" ; remove_dot_segments(R.path)
T.query: undef ; R.query
T.scheme: "http" ; Base.scheme
T.fragment: undef ; R.fragment
Следуя алгоритму из §5.3, мы получаем следующее:
http:///g
Примечание: Хотя этот URI действителен, он бесполезен, потому что имя сервера (T.authority) пусто!
////g - это то же самое, что и ///g, за исключением того, что R.path - это //g, поэтому мы получаем
http:////g
Примечание: Хотя этот URI действителен, он бесполезен, потому что имя сервера (T.authority) пусто!
Последние три пути (h//g, g////h, h///g:f) являются относительными путями (path-noscheme).
Анализ URI (скажем, с использованием регулярного выражения в Приложении B) дает нам следующее:
Base.scheme: "http" R.scheme: undef
Base.authority: "a" R.authority: undef
Base.path: "/b/c/d;p" R.path: "h//g"
Base.query: "q" R.query: undef
Base.fragment: undef R.fragment: undef
Следуя алгоритму из §5.2.2, мы получаем следующее:
T.path: "/b/c/h//g" ; remove_dot_segments(merge(Base.path, R.path))
T.query: undef ; R.query
T.authority: "a" ; Base.authority
T.scheme: "http" ; Base.scheme
T.fragment: undef ; R.fragment
Следуя алгоритму из §5.3, мы получаем следующее:
http://a/b/c/h//g # For h//g
http://a/b/c/g////h # For g////h
http://a/b/c/h///g:f # For h///g:f
Я не думаю, что эти примеры подходят для ответа на то, что, я думаю, вы действительно хотите знать.
Взгляните на следующие два URI. Они эквивалентны не.
http://host/a/b/c/d # Path has 4 segments: "a", "b", "c", "d"
а также
http://host/a/b/c//d # Path has 5 segments: "a", "b", "c", "", "d"
Большинство серверов будут относиться к ним одинаково - и это нормально, поскольку серверы могут интерпретировать пути так, как они хотят, - но это имеет значение при применении относительных путей. Например, если бы это был базовый URI для ../../e, вы бы получили
http://host/a/b/c/d + ../../e = http://host/a/e
а также
http://host/a/b/c//d + ../../e = http://host/a/b/e
Действительно отличное объяснение. Но даже это заняло определенное время, чтобы понять и двигаться дальше. Я пытаюсь воспроизвести эти шаги самостоятельно. Во всяком случае, похоже, только ваш ответ подтолкнул меня в правильном направлении.
Есть предел тому, сколько я отвечу! Но я представил свой ответ таким образом, чтобы было легче выучить все, что нужно читателю, чтобы понять ответ. Рад, что получилось :)
Вам стоит написать "гайд по rfc для чайников" :) Еще один вопрос - пустой авторитет ("") в терминах rfc3986 то же, что и undefined?
Нет. "Компонент не определен, если связанный с ним разделитель не отображается в ссылке URI.". /g имеет неопределенные права доступа, а ///g - пустой. §5.2.2 специально проверяет наличие (не) определенного.
Однако нет различия между пустым и неопределенным путем.
В конце концов, мне удалось исправить ошибку в огромном приложении, основанном на этой проблеме. Спасибо! Подумайте о написании руководств, блогов, у вас неплохо получается.
Первые два // после: являются частью схемы (или, точнее, зафиксированы после схемы), а не относительный путь. Примеры являются относительными путями, основанными на начальном URL. В противном случае путь будет непрозрачным, проблема веб-сервера заключается в том, чтобы увидеть, означает ли
//значение/или нет (для систем на основе unix без правил перенаправления это будет, но это не свойство протокола). Кроме того, URI заменены на IRI (RFC3987), так что у вас есть даже более странные случаи, которые нужно учитывать, например, направление написания.