HTTP相关概念
“事务”
- 一个HTTP事务,是由一条客户端发往服务器的请求命令和一条从服务器发回客户端的响应结果组成,此通信以的格式化数据块(HTTP报文)的形式进行。
- 每条HTTP请求报文都包含一个“HTTP方法”;每条HTTP响应都携带一个“状态码”
- HTTP有规范的结构:
- 起始行:说明要做什么/出现了什么情况
- 首部:零个或者多个,每个包含一个名字和一个值,用冒号分割,首部以一个空行结束
- 主体:主体是可选的,与文本形式并结构化的起始行和首部不一样,主体除了文本可以包含任意二进制数据
客户端通过HTTP协议访问服务器上资源
- “所谓的输入URL敲回车后发生了什么”
- 浏览器从URL里解析出服务器的主机名
- 请求DNS查询服务器获取对应域名的IP地址
- DNS返回结果后,浏览器和该IP的机器,在URL指定的端口上(从URL里解析端口号,如果没有,默认80)通过三次握手尝试建立一个TCP连接。
- 建立TCP链接(传输控制协议Transfermission Control Protocol)(三次握手)
- 客户端发送HTTP请求报文
- 服务器从HTTP报文获取被请求的资源的URL,如果报文里有Cookie,服务器还会分析Cookie,在返回HTTP响应的时候,服务器还可能对Cookie做修改
- 服务器确认被访问的文件的权限,然后从磁盘上将文件内容(也可能是执行这个文件/调用程序,说明如下)返回作为HTTP的响应,并在日志里增加记录。
- 服务端回送HTTP响应报文
- 客户端接受报文,开始准备将内容展示出来
- 如果短时间内客户端没有向同一个服务器发出其他请求,服务器就会释放之前的TCP连接。
客户端通过Telnet协议
- Telnet程序将键盘连接到某个目标TCP端口,并将此端口的输出回送到显示屏上
- 几乎可以链接所有的TCP服务器,包括HTTP服务器
- Web服务器会将Telnet程序当作一个Web客户端处理
Web结构实体
- 代理:客户端和服务端之间的HTTP中间实体
- 缓存:HTTP的仓库
- 网关:连接其他应用程序的特殊Web服务器,作为其他服务器的中间实体,通常用于将HTTP流量转换成去其他协议(客户端无感知)
- 隧道:一旦建立,就在两条连接之间对HTTP通信报文进行盲转发的特殊代理。一种常见用途是通过HTTP连接承载加密的安全套接字层(SSL)流量
- Agent代理:发起自动HTTP请求的半智能Web客户端,代表用户发起HTTP请求,比如Web浏览器。网络爬虫也是。
从URI说起
- 每个Web服务器资源都有一个名字,称作“统一资源标识符”,也就是URI(Uniform Resource Identifier)
- URI有两种形式,就是
URL
和URN
了。现在,大部分的URI都是URL
URL
- URL,是“统一资源定位符”,描述一台特定服务器上的某个资源的特定位置
- URL是URI的子集,区别于“只说这是什么”的URI,URL主要表达的是“这是一个在某处的资源”
- URL要精确的说明某个资源的位置,才能去访问,所以它大多遵循一种标准格式:
方案(scheme,表明怎么访问)://服务器的地址/资源路径
- URL告知浏览器如何对资源进行访问和处理
URL的格式
大多都有这样的格式:scheme://user:password@host:port/path;params?query#frag
,当然,如上所说,最主要的就是scheme、host和path
- 方案scheme:大小写无关。常见的有“http https mailto ftp rtsp file telnet news”
- 路径path:很像UNIX文件系统里的文件系统路径,可以用字符
/
分成路径段,每个路径段有自己的参数,用;
分割 - 参数param:就是以等号
=
链接的键值对,相互之间、和路径之间,都用;
分割。这个初衷是为了“负责解析URL的应用程序需要这些协议参数来访问资源” - 查询字符串query:和参数来时但是以
?
开始,以&
分割之间。这个初衷是“通过‘提问题’来缩小查询范围”。所以对照参数的定位,就稍微能理解许多框架不推崇在URL上增加看起来是path其实是param的做法,而是都将查询条件放在query了 - 片段frag:表示指向一个资源的一个片段(其实就是html上的锚点啦),但是实际上,客户端并不会把frag发给服务器,服务器返回的就是整个资源,是客户端根据frag参数自行决定指向到那个片段的。
URL快捷方式
- 相对URL
- 这个方案对于保持一组资源的可移植性提供了便捷的解决
- 基础URL,作为相对URL的参考点使用。可以指定,也可以选择其所属资源的URL作为基础
- 自动扩展URL
- 浏览器的功能
- 主机名扩展(尝试),历史扩展
URL的字符安全
- URL要统一地命名资源,就意味着它要适应各种不同协议,并且不能丢失信息,而且最好还要能被人类阅读,所以就需要规定通用字母表和编码规则
- 为了避开安全字符集表示法带来的限制,设计了一种编码机制用于在URL里表示各种“不安全”的字符,就是通过“转义”:一个百分号
%
后面跟上两个表示字符ASCII码的十六进制数字。 - 转义方法
encodeURI
、decodeURI
和encodeURIComponent
、decodeURIComponent
的区别:encodeURI()不会对本身属于URI的特殊字符进行编码,例如冒号、正斜杠、问号和井字号;而encodeURIComponent()则会对它发现的任何非标准字符进行编码。日常后者用的多。
URL的大小写问题
- 前面已经提及,方案是不区分大小写的,一般而言,方案和服务器地址(也就是域名部分)都不区分,而且浏览器会在请求的时候将他们改成小写的
- URL里还有资源的路径,这里大小写是否敏感,和服务器怎么解析有关,如果是直接把路径映射到文件系统里去,那么就跟服务器的主机的操作系统的文件系统有关。(比如下面提到的nginx的案例)
- 另外,一般而言Web Server也可能对大小写做处理,所以URL的大小写问题其实是复杂的问题。
- query部分是大小写敏感的,一般而言,Web Server不会对他们做额外的处理
案例:Nginx部署静态资源的URL大小写问题
- Windows和OS X系统下,文件/文件夹的命名是不区分大小写的;Linux系统下则是大小写敏感的。所以,项目迁移的时候,就可能遇到大小写问题。(Window的文件系统是NTFS,OS X是HFS,Linux则是Ext3/4)
- 对于通过Nginx部署的网页,对于大小写的迁移问题,或者访问的时候的大小写问题,有如下方案:
- 通过url rewrite配置进行正则替换
- Linux下,文件/夹都小写,然后对url进行处理,将请求全部变成小写。比如使用lua模块或者ngx_http_lower_upper_case模块(另外,Nginx官方不推荐perl,有漏洞)
再提及一下存在感稀薄的URN
- “统一资源名”,和URL不同,是和目前的资源的所在地无关的,目的是希望解决资源移动之后就无法根据原先的URL去获取的问题
- 通过URN,可以用同一个名字通过多种网络访问协议来访问资源
- 它需要一个支撑架构来解析资源的位置
HTML文档里的外链
- 以下说法出自Google的《HTML/CSS风格指南》
- ,在引用样式表文件、脚本文件、图片以及其他媒体文件时忽略协议部分(http:,https:),除非使用这两种协议都无法获取到该资源。
- 省略协议可以避免相对 URL 产生的问题,也可以减少文件的体积。(虽然体积的节省真的微乎其微啦)
比如这样
1
2
3
4
5<!-- 不推荐 -->
<script src="http://www.google.com/js/gweb/analytics/autotrack.js"></script>
<!-- 推荐 -->
<script src="//www.google.com/js/gweb/analytics/autotrack.js"></script>1
2
3
4/* 不推荐 */
.example {
background: url(http://www.google.com/images/example);
}这么做是有其道理的,以
//
开头的叫做“相对URL”(protocol-relative URL)。浏览器遇到相对 URL,则会根据当前的网页协议,自动在//
前面加上相同的协议。如当前网页是 http 访问,那么所有的相对引用//
都会变成http://
或者https
同理。如果你在本地查看,协议就会变成file://
。- 所以,如果省略协议,就需要保证引用的外部资源也采用和网页相同的协议,或者保证资源可以同时通过 http 和 https 访问。经过 StackOverflow 网友测试,这种用法几乎所有的浏览器都能支持,只有在 IE7/8 下会有一点小问题,就是通过相对 URL 引用的 CSS 文件(无论
<link>
或@import
)会被下载两遍。所以对性能有一点影响。 - 但同时,这么做还能避免IE下因为混合内容而弹出警告框的问题——在浏览https网页的时候如果网页引用了http协议的外部资源,会被认为不安全,IE会直接弹框提示。