解析相对部分有多个斜杠的 URI
Resolve URI with multiple slashes in relative part
我必须用 perl 编写一个脚本来解析来自 html 的 uris。无论如何,真正的问题是如何解决相对uris。
我有基本 URI(html 中的基本 href),例如 http:///a/b/c/d;p?q(让我们通过 rfc3986)和其他不同的 URI:
/g, //g, ///g, ////g, h//g, g////h, h///g:f
在此 RFC 中,第 5.4.1 节(link 以上)只有 //g:
的示例
"//g" = "http://g"
所有其他情况呢?据我从 rfc 3986, section 3.3 了解到,允许使用多个斜杠。那么,是否遵循决议
正确吗?
"///g" = "http:///a/b/c///g"
或者应该是什么?有没有人可以更好地解释它并用未过时的 rfc 或文档来证明它?
更新#1:
试着看看这个工作 url - https:///whosebug.com////////a/////10161264/////6618577
这是怎么回事?
否 - ///g
似乎更等同于 /g
。 "dot-segments" ..
和 .
用于通过 http
URL 在层次结构中上下导航。另请参阅用于处理 URI 中的路径的 URI 模块。
我很好奇 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:///whosebug.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:///whosebug.com////////a/////10161264/////6618577
scheme: https
host:
port:
path: /whosebug.com////////a/////10161264/////6618577
query:
首先我将确认您提供的所有 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"
大多数服务器会以相同的方式对待它们——这很好,因为服务器可以自由地以他们希望的任何方式解释路径——但在应用相对路径时会有所不同。例如,如果这些是 ../../e
的基本 URI,您将得到
http://host/a/b/c/d + ../../e = http://host/a/e
和
http://host/a/b/c//d + ../../e = http://host/a/b/e
我必须用 perl 编写一个脚本来解析来自 html 的 uris。无论如何,真正的问题是如何解决相对uris。
我有基本 URI(html 中的基本 href),例如 http:///a/b/c/d;p?q(让我们通过 rfc3986)和其他不同的 URI:
/g, //g, ///g, ////g, h//g, g////h, h///g:f
在此 RFC 中,第 5.4.1 节(link 以上)只有 //g:
的示例"//g" = "http://g"
所有其他情况呢?据我从 rfc 3986, section 3.3 了解到,允许使用多个斜杠。那么,是否遵循决议 正确吗?
"///g" = "http:///a/b/c///g"
或者应该是什么?有没有人可以更好地解释它并用未过时的 rfc 或文档来证明它?
更新#1: 试着看看这个工作 url - https:///whosebug.com////////a/////10161264/////6618577
这是怎么回事?
否 - ///g
似乎更等同于 /g
。 "dot-segments" ..
和 .
用于通过 http
URL 在层次结构中上下导航。另请参阅用于处理 URI 中的路径的 URI 模块。
我很好奇 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:///whosebug.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:///whosebug.com////////a/////10161264/////6618577
scheme: https
host:
port:
path: /whosebug.com////////a/////10161264/////6618577
query:
首先我将确认您提供的所有 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"
大多数服务器会以相同的方式对待它们——这很好,因为服务器可以自由地以他们希望的任何方式解释路径——但在应用相对路径时会有所不同。例如,如果这些是 ../../e
的基本 URI,您将得到
http://host/a/b/c/d + ../../e = http://host/a/e
和
http://host/a/b/c//d + ../../e = http://host/a/b/e