URL即Uniform Resource Locator,统一资源定位符,用来指定互联网上标准资源的地址。
URL标准结构
scheme://login:password@address:port/path/to/resource?query_string#fragment
- scheme 协议名称
- 协议名称由一串不分大小写的字符串组成,以单个冒号结束。
- 官方认可的有效URL协议由IANA维护,包括http:、https:、ftp:等十余种,但常见浏览器也支持其他伪协议,如data:、javascript:。
- 浏览器在做进一步解析前,会先判断URL是相对URL还是绝对URL,相对URL要根据当前浏览的上下文才能判断完整的URL。最关键的区别就在于,URL最前面是否包含有效的协议名称。
- 按照RFC1738规定,URL在冒号之前只允许出现数字字母及”+”,”-“,”.”这三个符合,但实际应用环境中,所有主流浏览器都会忽略前导换行符和空格。IE还会忽略所有不可打印字符(ASCII 0x01~0x1F),Chrome会忽略0x00和NUL空字符,Opera协议名里可以包含高位字符。
- // 层级标记符
- RFC1738规定,每个层级结构URL里都必须包含//层级标记符,实际应用环境里各个浏览器表现不同。
- http:example/ 在FireFox、Chrome、Safari里直接输入这个URL是可以正常解析的,等同与http://example/,但有基准URL时,这会被认为是指向example目录的相对路径。
- javascript://example.com/%0alert(1) 所有浏览器都会认为这个字符串是有效的非层级伪URL,忽略前面的部分并直接执行Js。
- mailto://user@example.com IE会忽略层级标记符,直接指向电子邮件地址,其他浏览器不能解析。
- IE可以使用\\代替//。
- login:password 身份验证
- URL身份验证属于可选项,身份验证信息的传输和协议有关,FTP等部分协议有效,HTTP等协议里如果强行加入这部分信息,没有明确规定怎么处理。
- 大部分浏览器在身份验证部分里不接受数字字母外的几乎任何字符。
- address 服务器地址
- RFC允许不区分大小的域名(example.com),IPv4地址(192.168.1.1),一对方括号里的IPv6地址([0:0:0:0:0:0:0:1]),FireFox支持方括号里的IPv4地址和主机名,其他浏览器不允许。
- 实际环境里,浏览器接受八进制、十进制、十六进制,甚至多字节专程单个整数的写法(和浏览器锁依赖的标准C类库有关)。
- 大部分浏览器会忽略出现在URL内的 0x0A~0x0D、0xA0~0xAD之间的控制字符,而且会主动的把主机名里的全角句号”。”转为”.”(只是主机名,在其他部分则不会转换)
- port 服务器端口
- 在服务器连接的网络端口并非标准端口时会用到,UDP和TCP都依赖一个16位的端口号来区分不同服务。
- 我们可以用浏览器想任意服务器发送数据,即便浏览器不支持这些服务的协议,比如SMTP。
- path to resource 层级文件路径
- 层级文件路径借鉴自UNIX目录语义,支持”/../“和”/./“。
- 现在的Web应用层级文件路径大多和实际物理路径没什么联系了。
- query_string 查询字符串
- 常见格式为 name1=value1&name2=value2… 但RFC实际上没有规定必须用这种格式,所以也允许query_string是一堆乱七八糟 的字符串,关键在于接受后怎么处理。
- fragment 片段ID
- fragment和查询字符串有点类似,但它是用于客户端的一种可选信息,RFC里没有明确规定片段ID的功能和格式。
- 实际运用里,片段ID在浏览器中只有一个用途:指向HTML页面的某个锚点。
- 与query_string不同的是,改变fragment不会触发页面重载,也没有时间开销,所以可以用来存储一些客户端相关的数据。
浏览器解析步骤
- 提取协议名称:查找第一个”:”,该字符左边的部分就是协议名称,如果协议名称里出现了不该有的字符,则该URL就是相对URL,这部分内容也不会当成协议处理。
- 去除层级标记符:如果有”//“就跳过,如果没有……那就不管了。在实际环境里,不用斜杠用一个用两个用三个其实都可以正常解析。
- 获取授权部分信息(登录信息+地址+端口):依次查找“/”、“?”、“#”,哪个先出现以哪个为准截取,除了IE和Safari,分号;也是可接受的分隔符。
- 授权部分信息提取出来后,先查找@符号,如果找到了,那它前面的部分就是登录信息,冒号前面是用户名,后面是密码
- 剩下部分就是地址了,查找冒号:,如果有则前面的部分是地址,后面部分是端口,如果发现方括号[],说明地址是IPv6
- 确定路径
- 提取查询字符串
- 提取片段ID
陷阱和问题
- http://example.com&gibberish=1234@167772161/
这个地址实际上指向的是167772161,转成正常的IPv4地址是10.0.0.1,@前面的部分是无效身份验证信息。 - http://example.com\@coredump.cx/
在FireFox里会访问coredump.cx,而其他浏览器里会访问example.com。 - http://example.com;.coredump.cx/
IE会访问coredump.cx,而其他浏览器会访问example.com。