The Http URI 解析

今天我在对照着rfc3986看java.net.URI的实现。URL无处不在,摆在我们面前的一个核心问题就是,如何解析它。

URI的全称是Uniform Resource Identifiers。URL的全称是Uniform Resource Locator,它是URI的子集。意思是说,我们是通过资源的primary access mechanism的一种表示方式来标识这个资源,而不是通过名字、或其它属性的方式标识。

Http request的第一行中,method后面是Request-URI。

如:

GET /index.html HTTP/1.1

其中"/index.html"就是 Request-URI。

Request-URI有4种形式:

  1. * 就一个星号。 如 "OPTIONS * HTTP/1.0"
  2. absoluteURI 。下面详细说
  3. abs_path: 即"/" path_segments [ "?" query ]
  4. authority。 给Connect方法用

absoluteURI 有三种形式:

net_path 模式: scheme "://" authority [ abs_path ] [ "?" query ]

abs_path 模式: scheme ":/" path_segments [ "?" query ]

opaque 模式: scheme ":" authority uric_no_slash *uric

平时用的主要都是第一种。

schema不分大小写。schema的第一个字符必须是[a-zA-Z],后面的字符必须是[a-zA-Z0-9]和+ - . 这三个字符。拿到input之后首先找":/?#"这4个字符,如果首先找到的是冒号,那么就说明input是含有scheme的。java通过这样把absoluteURI和abs_path分开。通常来说,absoluteURI用于发给http proxy,而abs_path发给普通的http server。http标准要求所有支持http/1.1的server都得支持absoluteURI形式的请求。也就是说,下面这样发给www.baidu.com是合法的。

GET http://www.baidu.com/index.html HTTP/1.1

此时要求从absoluteURI中提取Hostname,它优先于headers中的Host。解析完schema之后,如果冒号后面不是"/",那么它就是第三种模式,opaque模式。

如果是net_path,在跳过了"://"之后,搜索"/?#"这3个字符中的一个,这时的位置就是authority的past-the-end。所以此时有三种情况:

1.找到了,且authority不为空。

2.找到了,但是authority为空。例如"file:///test.txt"。rfc认为这是非法的,但是java出于自己的考虑要支持这种格式的URI。

3.没有找到。格式非法。

然后继续往后解析path即可。

如果是abs_path,就不用关心authority,跳过":/"往后解析path即可。

解析path时,首先查找"?#"这两个字符。它们之后的是query和fragment。这中间的部分就是path。

至此,URI已经被拆成scheme、authority、path、query、fragment这么几部分。拆的过程中用到的分割符只是":/?#"这4种字符,属于必须被转义的字符,所以对于如ascii兼容的编码(如utf-8),URI中有中文字符一样可被正确解析。如果只是想提取出path(这是http server最关心的),那么不必做decode,直接parse即可。

在encoding URI的时候要注意,

保留字符: ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "TMPL__XXXX_BODY_XXXXquot; | ","

这些只能用来做分割符,不能用在其它地方。

Unreserved Characters:

大写字母|小写字母 | 数字 | "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"

这些属于可转义也可不转义。把这样的字符替换成转义序列后,含义不变,与原来的等价。Unreserved Characters之外的都该转义。

转义的规则是先把character按照某种编码(如utf-8、gbk)进行编码。然后把编码后的每个8位的字节,替换成% hex hex这样的。比如"@"对应%40,等号"=" 对应%3d。如果百分号之后只有一个数字,如%3,应当写成%03,如果写成%3就是错的。这时候java就会抛异常,拒绝继续解析下去,但是实际上我们的parser在有时候是应该越宽容越好,比如日志分析程序中的url parser。

#后面的是fragment,fragment不属于URI,不应该被发给服务器。但是服务器给客户端的答复中,如Headers中的Location的值,其实应允许包含#的。我也不清楚浏览器现在如何实现的,待测试。

(待续)

此博客中的热门博文

在windows下使用llvm+clang

少写代码,多读别人写的代码

tensorflow distributed runtime初窥