HTTP权威指南阅读笔记 第一部分 HTTPWeb基础(第一章到第四章)
目录:
第一部分 HTTP:Web的基础
1.1 HTTP————因特网的多媒体信使
HTTP是可靠的数据传输协议,访问的时候不用担心数据的完整性,开发的时候也不用担心数据的传输和因特网中的问题。
1.2 Web客户端和服务器
client向server请求数据,而server接收到请求会根据请求会返回对应的数据
1.3 资源
获取的资源可以是静态文件,也可以是根据请求用户,时间段等等情况动态返回资源
1.3.1 媒体类型
HTTP对每个通过web传输的对象都打上了MIME类型的数据格式标签,最初MIME是为了解决不同的电子邮件之间搬移报文存在的问题。
Web服务器会为所有HTTP对象数据附加一个MIME类型,浏览器通过从服务器中取回一个对象的时候通过MIME类型进行对应的处理,例如显示图片,解析HTML文件,通过计算机声卡播放音频文件,运行外部插件来处理特殊格式的数据。
MIME类型是一种文本标记,表示一种主要的对象类型和一个特定的子类型,中间由斜杠隔开
- HTML格式的文本文档由text/html类型来标记
- 普通的ASCII文本文档由text/plain类型来标记
- JPEG版本的图片为image/jpeg类型来标记
- GIF格式的图片为image/gif类型
- Apple的QuickTime电影为video/quicktime类型
- 微软的PowerPoint演示文档为application/vnd.ms-powerpoint类型
记录在Content-type: image/jpeg,Content-length: 12984
1.3.2 URI
统一资源标识符,示例:
http://image.whysdomain.com/linux/inotify1.gif
1.3.3 URL
统一资源标识符,示例
http://image.whysdomain.com/linux/inotify1.gif
可以带参数,可以是非http(例如https,ftp)
第一步使用http协议,第二步进入image.whysdomain.com
站点,第三步获取指定资源/linux/inotify1.gif
几乎所有的URI都只URL
1.3.4 URN
URI的第二种形式,为统一资源名,还在试验中。
1.4 事务
Client通过HTTP协议对Server的"请求命令"和"相应结果"两个合起来一个事务处理。
1.4.1 方法
每个请求报文都包含一个方法,这些方法会告诉Server执行什么动作,常见的方法为GET(服务端数据获取),POST(发送数据到服务端),PUT(客户端数据上传),DELETE(服务端删除资源)和HEAD(获取HTTP首部)
1.4.2 状态码
每个响应报文可会有一个状态码,告知Client刚才的请求是否成功或者是否需要采取其他动作。状态码后还会有一条解释性的原因短语对其进行描述
1.4.3 Web页面中可以包含多少对象
应用程序完成一项任务通常会发布多个HTTP事务。
对获取Web页面,浏览器会执行一个事务来获取描述布局页面的HTML,然后发布另外的HTTP事务来获取嵌入的图片等资源,Java小程序等。
1.5 报文
HTTP报文也分为请求报文和响应报文,由一行一行简单的字符串构成,为纯文本
HTTP报文包括三部分
- 起始行
报文的第一行就是起始行,在请求报文中主要说明要做些什么,在响应报文中说明了什么问题
- 首部字段
起始行后边有零个或多个首部字段。每个首部字段都包含一个名字和一个值,为了便于解析,两者之间用冒号":"来分隔。首部以一个空行结束,添加一个首部字段和添加新行一样。
- 主体
空行之后是可选的报文主体,包含了所有类型的数据,请求主体中包含了要发送给Server端的数据,而响应主体中包含了要返回给客户端的数据,这些数据可以是文本,也可以是二进制的数据,例如图片,视频,音频,程序软件等。
因为是GET请求,不需要传递主体。
1.6 连接
1.6.1 TCP/IP
HTTP是应用层协议,HTTP不需要关心网络通信的具体细节,都通过TCP/IP协议进行网络传输。
TCP提供了:
- 无差别数据传输
- 按序传输(数据按照发送顺序达到)
- 未分段的数据流(可以在任意时刻以任意尺寸将数据发送出去)
因特网本身就是基于TCP/IP协议,可以隐藏各种网络和硬件设备的特点和弱点。
只要建立了TCP连接,Client端和Server端之间的报文交换就不会丢失,不会被破坏,也不会接收出现错序。
1.6.2 连接,IP地址及端口号
HTTP的Client发送报文前会建立Client端和Server端之间的TCP连接。
TCP协议需要Server端的IP和端口号,IP和端口号通过URL获取,可以是
- IP地址:端口
- 域名:端口
- 域名
域名通过DNS解析为IP地址,而不带端口就是默认HTTP协议请求80端口,HTTPS协议请求443端口。
1.6.3 Telnet
Telnet可以很好的模拟HTTP客户端,nc能可以测试基于TCP和UDP的流量测试。
http://www.bgw.org/tutorials/utilites/nc.php
1.7 协议版本
- HTTP/0.9
HTTP的1991版本,有很多的缺陷,用于与老Client进行交互,并且只支持GET方法,不支持MIME类型,各种HTTP首部,或者版本号,初衷是为了获取HTML对象
- HTTP/1.0
1.0是第一个得到广泛使用的HTTP版本,添加了版本号,各种HTTP首部,和一些额外的方法,和对多媒体对象的处理,使包含图片和交互式表单等成为可能
- HTTP/1.0+
20世纪90年代中叶,很多流行Client和Server都在向HTTP中添加各种特性,来满足需要,包括keep-alive,虚拟主机,代理连接等,这种非正式的HTTP扩展被称为HTTP/1.0+
- HTTP/1.1
校正HTTP中设计的结构性缺陷,明确语义,引入性能优化措施,并删除一些不好的特征,为当前使用的HTTP版本。
- HTTP/2.0
又称HTTP-NG,关注重点在性能的大幅优化以及更强的服务逻辑远程执行框架。
1.8 Web结构组件
除Client和Server,还可能有其他组件
- 代理
位于Client和Server之前的HTTP中间实体。
- 缓存
HTTP的仓库,常使用的页面或脚本保存在离Client更近的地方。CDN就是做这个的。
- 网关
连接其他应用程序的特护Web服务器
- 隧道
对HTTP通信报文进行盲转发的特殊代理
- Agent代理
发起自动HTTP请求的半智能Web客户端
1.9和1.10是对本章的总结以及更多对HTTP协议有相关介绍的链接。
2. URL和资源
URL是指向了资源的位置,而HTTP协议使用的是URI的子集URL。
2.1 浏览因特网资源
URL第一部分http是URL方案,告知Web客户端如何访问资源,第二部分image.whysdomain.com是指服务器的位置,告知Web客户端资源在何处,第三部分/linux/inotify1.gif是资源路径。
URL用于简化信息的访问过程。
2.2 URL语法
通用格式
<acheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
组件 | 描述 | 默认值 |
---|---|---|
方案 | 访问服务器以获取资源时要使用那种协议 | 无 |
用户 | 某些方案访问资源时需要的用户名 | 匿名 |
密码 | 用户名可能需要包含密码 | 无 |
主机 | Server端主机名或者点分IP地址 | 无 |
端口 | Server端主机监听端口 | 摸个方案特有 |
路径 | 资源本地名 | 无 |
参数 | 某些方案需要 | 无 |
查询 | 用于传递参数 | 无 |
片段 | 引用对象的时候不会将frag字段发给Server,用来Client内部使用 | 无 |
2.2.1 方案-使用什么协议
方案大小写不区分
2.2.2 主机和端口
2.2.3 用户名和密码
ftp和http服务都支持用户名密码
2.2.4 路径 2.2.5 参数 说实话没看懂是啥用途 2.2.6 查询字符串
2.2.7 片段
HTTP会对请求的对象做处理,但是不会对片段做处理,并且也不会发送给Server端,只是Client用来解析展示使用。
2.3 URL
Web浏览器可以理解多种URL的快捷方式。
- 相对URL,是在某资源内部制定一个资源的便捷略缩方式
- 自动扩展URL,用户输入URL进行自动扩展
2.3.1 相对URL.
相对URL是不完整的,相对URL获取访问资源所需的全部信息,就需要基于base的URL进行解析。
例如html中<a href="/jquery.js">
,就是基于Server获取/jquery.js
,省略了URL中的方案,主机和一些其他组件,这些都从所属资源的的基础URL中推导。
相对URL的优点是保持一组资源的便捷性,使用相对URL,迁移一组资源的时候仍然可以保持链接的有效性,因为相对URL是相对新的基础进行解析
1.基础URL
转化处理的第一步是找到基础URL,基础URL是作为相对URL的参考点使用。
- 在资源中显示提供
有些资源中显示指定URL,例如HTML文档中定义了标记<base>
,转化的时候会以此定义HTML文档。
- 封装资源的基础URL
如果一个没有显示指定基础URL的资源中发现URL,就将所属资源的URL作为基础。
- 没有基础URL
文中说可能是不完整或者损坏的URL,暂时没有想到为何会有这种情况。
2.解释的相对引用
相对URL需要被解析为一个绝对URL,解析过程就是分解URL为各个组件,然后换算为绝对URL。
解析为绝对URL就可以用来引用资源了,这个算法在RFC1808中定制,后来被合并到RFC2396。
<a href="/jquery.js">
就是:
- 方案为空,继承URL中的所有
- 路径不为空,直接与继承的所有合并为绝对URL
2.3.2 自动扩展URL
浏览器会在用户提交URL后,或在用户输入时扩展URL。
- 主机名扩展
在一些提示后就可以进行扩展,例如输入baidu,会扩展在前边加入www.
,在后边扩展.com
,构建出百度域名,如果找不到baidu匹配的站点会放弃之前尝试扩展的形式。浏览器通过这种方式来节省用户时间。
- 历史扩展
将用户之前访问的URL历史进行存储,根据输入和历史URL前缀匹配,提供完整的URL供用户选择。
如果与代理共同使用的时候扩展的URL可能会有所不同。
2.4 各种令人头疼的字符
2.4.1 URL字符集
最开始的字符集倾向于英语,US-ASCII码使用7位二进制来表示按键和少数用于文本格式和硬件通知的不可打印控制字符。
US-ASCII码历史悠久,移植性强,但是支持全球的各种语言。
并且URL中还有二进制数据,就有了二进制转义序列,通过转义序列,可以通过有限自己对任意字符值或数据进行编码。
2.4.2 编码机制
转义通过"%
"符号。
2.4.3 字符限制
特殊字符
2.4.4 另外一点说明
只会对特殊字符转码,而安全字符不进行转码
2.4.5 方案的世界
参考附录A
2.6 未来展望
3. HTTP报文
3.1 报文流
HTTP报文是HTTP应用程序之间发送的数据块。
数据块以一些文本形式的元信息开头,用来描述报文的内容和含义,后边是可选的数据部分。
报文在Client,代理,Server之间流动。
3.1.1 报文流入源端服务器
流入和流出用来描述事务处理
3.1.2 报文向下游流动
3.2 报文的组成部分
起始行,首部(header)和主体(body)
起始行和首部是由行分割的ASCII文本,每行都以一个由两个字符组成的终止序列作为结束,为回车符(ASCII码13)和一个换行符(ASCII码10)
主体为可选择数据块,可以包含文本或二进制文件,也可以为空。
Content-Type代表主体是什么,纯文本文档,Content-Length主体有多大,为19个字节。
3.2.1 报文语法
报文分请求报文和响应报文。
请求报文格式
<method><request-URL><version>
<header>
<ehtity-body>
响应报文格式
<version><status><reason-phrase>
<header>
<ehtity-body>
3.2.2 起始行
请求报文的起始行说明了要做些什么
包含
- 方法:用那种方式去请求Server
- 请求URL:对哪个资源执行这个方法
- HTTP版本:告知服务器使用那种HTTP协议
响应报文的起始行说明了发生了什么
包含
- HTTP版本
- 数字状态码
- 原因短语
方法
- TRACE可以用来对可能进行过的代理服务器到服务器上去的报文进行追踪
- OPTIONS决定可以在服务器上执行那些方法
状态码
整体范围 | 已定义范围 | 分类 |
---|---|---|
100~199 | 100~101 | 信息提示 |
200~299 | 200~206 | 成功 |
300~399 | 300~305 | 重定向 |
400~499 | 400~415 | 客户端错误 |
500~599 | 500~505 | 服务器错误 |
- 200 OK
- 401 Unauthorized 需要输入用户名密码
- 404 Not Found
原因短语是状态码的可读版本
版本号说明了应用程序支持的最大版本,进而进行响应。另外HTTP/1.22比HTTP/1.3协议大,22和3分别是小版本号。
3.2.3 首部
首部分类
- 通用首部 可以在请求报文和响应报文中出现
- 请求首部 提供更多的请求信息
- 响应首部 提供更多的响应信息
- 实体首部 描述主体的长度和内容,或者资源本身
- 扩展首部 规范中没有定义的新首部
首部延续行
例如
Server: Test Server
Version 1.0
实际上是Test Server Version 1.0
,分成多行为了提高可读性,多出来的每行前有空格或者制表符。
3.2.4 实体的主体部分
可选,承载很多类型的数字数据,图片,视频,HTML文档,应用程序等。
3.2.5 版本0.9的报文
3.3 方法
并不是每个HTTP Server都实现了所有的方法,对于兼容HTTP1.1版本,只需要支持GET和HEAD方法。
不过即使支持DELETE和PUT方法的Server端也不希望任何人都能访问。
3.3.1 安全方法
GET和HEAD方法都是安全的方法,在这个请求过程中不会产生动作。
不产生动作代表不会在服务器上产生什么结果,当然也可以产生动作。
3.3.2 GET
3.3.3 HEAD
和GET类似,只返回首部,不返回实际主体。
使用的情况:
- 在不获取资源的情况下了解资源情况或者检查资源
- 通过查看响应的状态码看看对象是否存在
- 通过查看首部,测试资源是否被修改
3.3.4 PUT
可以更新或者创建资源,如果资源存在就更新,如果资源不存在就创建。
一般会有密码认证。
3.3.5 POST
POST请求一般用来支持HTML的表单
3.3.6 TRACE
客户端发送请求的时候可能需要防火墙,代理,网关或者一些其他应用程序。每个中间节点可能会修改原始的HTTP请求,而TRACE请求可以允许Client在最终将请求发送给目标服务器时,看看它变成了什么样子。
TRACE请求会在目的Server端发起一个环回诊断,行程的最后一站服务器会返回一条TRACE响应,并在响应主体中携带它收到的原始请求报文,这样客户端可以检测可以查看所有中间HTTP应用程序组成的请求/响应链上,原始报文是否,以及如何修改过。
不过TRACE请求对不同类型的请求处理都是相同的,需要中间应用程序会自行决定TRACE请求的处理方式。
3.3.7 OPTIONS
询问Server端支持的服务方式。
3.3.8 DELETE
DELETE,Client请求删除,Server端可以拒绝删除
3.3.9 扩展方法
HTTP被设计为可扩展的,在HTTP/1.1中定义的方法。
对于扩展方法不能实现,回报501 Not Implementd(无法实现)
3.4 状态码
3.4.1 100~199——信息性状态码
- 100 Continue 请客户端继续发送实体
- 101 Switching Protocols 说明服务器根据客户端的指定,将协议切换为Update首部所列协议
- 客户端与100 Continue
如果客户端在向服务端发送一个实体,并愿意在发送实体之前等待100 Continue
响应,客户端就需要发送一个携带了值为100 Continue
的Expect请求首部,如果客户端没有发送实体,就不应该发送100 Continue
的Expect首部,发送后服务端会误认为客户端要发送实体。
100 Continue是一种优化,在客户端在避免像服务器发送一个无法处理或者使用的大实体的使用使用。
客户端不会一直等待100的返回状态码响应,超过一定时间,会将实体直接发送。
不过客户端也需要应对好非预期的100响应的处理。
- 服务器与100 Continue
如果服务器收到了带有100 Continue的Except的首部请求,回返回100 Continue响应或者一条错误码来进行响应。
服务器不会向没有发送100 Continue的Except的首部请求的客户端发送100 Continue的状态码。
如果处于某种原因,服务端在有机会发送100 Continue的状态码的时候,已经收到了部分或者全部实体,说明客户端已经决定发送数据了,就不会发送100 Continue的状态码,而是在读完请求之后,为请求发送一个最终的状态码(跳过了100 Continue)
- 代理与100 Continue
代理如果收到100 Continue期望的请求,代理服务器会做以下事情。
如果代理服务器知道下一跳服务器是HTTP/1.1兼容的或者并不知道下一跳是否兼容都会将Expect首部发在请求中进行转发,如果知道代理服务器只与HTTP/1.1版本以前兼容,则会直接返回417 Expection Failed错误进行响应。
所以代理维护一些有关下一跳服务器或者其所支持的HTTP版本的状态信息是可以很好处理100 Continue请求
3.4.2 200~299——成功状态码
客户端发送的请求通常都是成功的
- 200 OK 请求没有问题,实体的主体部分包含了所请求的资源
- 201 Created 用于创建服务器对象的请求,比如PUT,响应的实体主体部分中应该包含各种引入了已创建的资源的URL,Location首部包含的是具体的引用,服务器发送状态码的是必须在发送这个状态码之前创建好对象
- 202 Accepted 请求已被接收,但服务器还未对其执行任何动作,不保证服务器会完成这个请求,这只是意味着接收请求的时候看起来是有效的。服务器应该在实体的主体部分包含对请求状态的描述,或许应该还有对请求完成时间的估计(或者包含一个指针,指向可以获取此信息的位置)
- 203 Non-Authoritative Information 实体首部包含的信息不是来自源端服务器,而是来自资源的一个副本。如果中间节点上有一份资源副本,但无法或者没有对它所发送的与资源有关的元信息(首部)验证就会出现这种情况。如果实体首部来自源端服务器,响应200
- 204 No Content 响应报文中包含若干首部和一个状态行,但是没有实体的主体部分,主要用于浏览器不转为显示新文档的情况下,对其进行更新(例如刷新一个表单页面)
- 205 Reset Content 用于浏览器的代码,清除当前页面中所有HTML表单元素
- 206 Parial COntent 成功执行了一部分或者Range请求,客户端通过一个特殊首部来获取部分或者范围的文档,这个代表范围请求成功。响应中必须包含Content-Range,Date和ETag或者Content-Location首部。
3.4.3 300~399——重定向状态码
重定向用于通知Client端使用代替位置来访问,要么就是代替的响应而不是资源内容。
如果资源被移走,可以发送一个重定向状态码和一个可选的Location,浏览器根据这些直接去请求资源而不用通知使用者
也可以通过重定向对本地资源和源端服务器资源进行验证,检查本地资源是否为最新或者源端服务器是否被修改,客户端需要发送一个特殊的If-Modified-Since的首部。如果文档没有被修改,会返回一个304的状态码。
- 300 Multiple Choices 客户端请求一个实际指向多个资源的URL时会返回这个状态码,例如Server端有某个HTML的英语和法语版本,返回这个状态码的时候会有一个列表,用户可以选择希望使用那一项,Server端可以在Location首部包含首选的URL。
- 301 Moved Permanetly 在请求的URL已经被移除时使用,响应的Location首部中应该包含资源现在所处URL
- 302 Found 与301类似,Client端应该使用Location首部给出的URL来临时定位资源,将来的请求仍用老的URL
- 303 See Other 告知Client端应该用另一个URL来获取资源,新的URL位于响应报文的Location首部,其主要目的是允许POST请求的响应将Client端定向到某个资源
- 304 Not Modified Client端可以通过所包含的请求首部,使请求变为有条件的,例如客户端发起一个条件的GET请求,而资源近期没有修改,就会返回这个状态码,并且不会返回实体
- 305 Use Proxy 用来说明必须通过一个代理来访问资源,代理的位置由Location首部给出,很重要的一个点是,Client端是相对某个特定资源来解析这条响应的,不能假定所有请求,甚至所有对持有所请求资源的Server的请求都通过这个代理进行。如果Client端错误的让代理介入了某条请求,可能会引发破坏性的行为,而且会造成安全漏洞。
- 306 未使用
- 307 Temporary Redirect 与302一样
302,303和307之间的区别
在HTTP/1.0,Client在POST请求的时候,收到302状态码的时候,会接收Location首部的重定向URL,并向这个URL发送一个GET请求,而不是POST请求。
在HTTP/1.1, Client使用303实现以上功能,对于HTTP/1.1的Client,用307状态码实现302,Server端就可以将302保留给HTTP/1.0使用
但是为啥为发送GET请求?
3.4.4 400~499——客户端错误状态码
主要应对Client发送的一些Server端无法处理的东西,例如格式错误的请求报文,或者URL等
- 400 Bad Request 用于告知客户端它发送了一个错误请求
- 401 Unauthorized 需要进行权限认证
- 402 Payment Required 被保留
- 403 Forbidden 请求被Server端拒绝,服务器并没有提供拒绝原因
- 404 Not Found 说明Server端无法找到请求的URL
- 405 Method Not Allowed 发起的请求的方法不支持,在响应的Allow首部,以告知客户端所请求资源可以使用那些方法
- 406 Not Acceptable Client端可以指定参数来说明愿意接收什么样类型的实体,Server端没有与Client可接受的URL匹配的资源,会返回此状态码并会包含首部
- 407 Proxy Authentication Required 与401状态码类似,但是永于代理的后端Server
- 408 Request Timeout 如果Client端完成请求所花时间过长,Server端可以发送此状态码,并关闭连接。
- 409 Conflict 说明请求可能在资源上引发一些冲突,服务器担心请求会引发冲突时,可以发送此状态码,响应中应该包含描述主体
- 410 Gone 与404类似,只是服务器曾经拥有过此资源,用于站点维护,可以在资源被移除的情况下通知Client
- 411 Length Required Server端要求在请求报文中包含Content-Length首部时使用
- 412 Precondition Failed Client端发起了条件请求,且其中一个条件是失败了的时候使用,Client端包含了Expect首部的时候就是条件请求。
- 413 Request Entity Too Large Client端发送的实体主体部分比Server端能够活着希望处理的要大时,使用此状态码。
- 414 Request URI Too Long Client端发送的URL比Server能够希望处理的要长时,适应此状态码
- 415 Unsupport Media Type Server端无法理解或者无法支持Client端发送实体内容类型时,使用此状态码
- 416 Requested Range Not Satisfiable 请求报文所请求的是指定资源的某个范围,而范围无法正常满足会使用此状态码
- 417 Expectation Failed 请求的Expect请求首部包含一个期望,但是Server端没有办法正常满足此期望时,使用此状态码
3.4.5 500~599——服务器错误状态码
对于Client发送有效请求的时候,Server端自身出现问题。
- 500 Internal Server Error Server端遇到一个妨碍它为请求提供服务的错误时,使用此状态码
- 501 Not Implemented Client发起的请求超出服务器能力范围(使用了服务器不支持的方法,那不是405吗?)
- 502 Bad Gateway 作为代理网关使用的服务器请求响应链的一下链路上收到了一条伪响应
- 503 Service Unavailable 用来说明Server端现在无法为请求提供服务,可以在首部加入Retry-After的首部用于标明什么时候资源可用
- 504 Geteway Timeout 与408类似,这里只是一个代理或者网关,在等待另一服务器对其的响应时超时
- 505 HTTP Version Not Supported Server端收到请求
3.5 首部
首部和方法配合,共同决定Client和Server能做什么。
在请求和响应报文中都可以用首部来提供信息,有些首部是某种报文专用的,有些是通用的。
- 通用首部
- 请求首部
- 响应首部
- 实体首部
- 扩展首部
3.5.1 通用首部
提供报文的基本信息
- Connection 允许Client和Server指定与请求或响应相关的选项
- Date 提供日期和时间标志,说明报文是什么时间创建
- MIME-Version 给出了发送端使用的MIME版本
- Trailer 如果报文采用了分块传输编码,可以用这个首部列出位于报文拖挂(Trailer)部分的首部集合
- Transfer-Encoding 告知接收端为了保证报文的可传输性,报文采用了什么编码方式
- Update 给出了发送端可以想要升级使用新版本或协议
- Via 显示了报文经过的中间节点(代理,网关)
通用缓存首部
HTTP/1.0引入了第一个允许HTTP应用程序缓存对象本地副本的首部,这样就不需要要总是直接从源端Server获取了。
- Cache-Control 用于随报文传送缓存指示
- Pragma 另一种随报文传送指示的方式,但不专用于缓存
3.5.2 请求首部
用于说明是谁或什么发送请求,请求来源何处,或者Client端的喜好及能力。Server可以根据请求首部给出的Client信息为Client提供更好的响应。
- Client-IP 提供Client机器的IP地址
- From 提供了Client用户的E-mail地址
- Host 提供了Client的主机名和端口号
- Referer 当前请求URI的文档的URL
- UA-Color 提供Client端显示器的显示颜色相关信息
- UA-CPU 提供Client的CPU类型或制造厂商
- UA-Disp 提供了Client显示器能力相关的信息
- UA-OS 给出了运行在Client的操作系统和版本
- UA-Pixels 提供了Client端显示器的像素信息
- User-Agent 将发起请求的应用程序名称告知Server
1. Accept首部
Accept首部为Client提供了将喜好和能力告知Server端,想要什么,可以使用什么,以及最重要的不想要什么,Server端可以根据这些额外信息,对响应信息做出更好的返回。
好处是Client可以获取到想要的内容,而Server端不会浪费时间和带宽来发送Client端不想要或者不能解析使用的东西
- Accept 告知Server能够发送那些媒体类型
- Accept-Charset 告知Server能发送什么字符集
- Accept-Encoding 告诉Server能发送什么编码
- Accept-Language 告诉Server能够发送那些语言
- TE 告知Server能够使用那些扩展传输编码
2. 条件请求首部
Client希望对请求加上某些限制,例如Client端有资源副本,去确认Client端副本和Server端有区别的时候才请求Server端传输资源。
- Expect 允许Client列出某些请求所需要Server行为
- If-Match 如果实体标记与文档当前的实体标记相匹配,就获取这份文档
- If-Modified-Since 除非在某个指定的日期之后资源被修改,否则就限制这个请求
- If-Range 允许对文档的某个范围进行条件请求
- If-Unmodified-Since 除非在某个指定日期之后资源没有被修改,否则就限制这个请求
- Range 如果Server支持范围请求,就请求资源的指定范围
3. 安全请求首部
HTTP支持请求进行质询/响应认证,可以使事务安全。
- Authorization 包含Client提供给Server,以便对其自身认证的数据
- Cookie Client用Cookie向Server发送一个令牌,并非真正的安全首部
- Cookies2 用来说明Client端的Cookie版本
4. 代理请求首部
- Max-Forward 在通往源端服务器的路径上,将请求转发给其他代理或者网关的最大次数,与TRACE方法一起使用
- Proxy-Authorization 与Authorization首部相同,用于代理进行认证时使用
- Proxy-Connection 与Connection首部相同,用于代理建立连接时使用
3.5.3 响应首部
响应首部位Client提供一些额外的信息,例如谁在发送响应,响应者功能,甚至与响应相关的一些特殊指令,有助于Client处理响应,并在将来更好的发送请求。
- Age 从最初开始创建,响应持续时间
- Public Server为其资源支持的请求方法列表,在最新的HTTP定义(RFC2616)中并没有出现
- Retry-After 如果资源不可用,在此日期或者时间重试
- Title 对HTML文档,就是HTML文档源端给出的标题
- Warning 比原因短语中更详细的一些警告报文
1. 协商首部
Server端有多种语言的版本,HTTP/1.1可以为Server和Client提供对资源协商的能力
- Accept-Ranges 对此资源来说,Server可以接收的范围类型
- very Server端查看其它首部的列表,可能会使响应发生变化,这是一个首部列表,Server会根据这些首部内容挑选最合适的资源版本发送给Client
2. 安全响应首部
和安全首部相对应。
- Proxy-Authenticate 来自代理的Client端质询列表
- Set-Cookie
- Set-Cookie2
- WWW-Authenticate 来自Server端质询列表
3.5.4 实体首部
实体首部提供关于实体相关的大量信息,包括对象的类型信息,能够对资源使用的各种有效的请求方法。
- Allow 列出了可以对此实体执行的请求方法
- Location 告知Client实体位于何处,用于将接收端定向到资源的位置
1. 内容首部
提供实体内容相关信息,说明尺寸,类型以及处理所需信息
- Content-Base 解析主体时的基础URL
- Content-Encoding 编码方式
- Content-Language 主体自然语言
- Content-Length 主体长度
- Content-Location
- Content-MDS
- Content-Range
- Content-Type
2. 实体缓存首部
通用的缓存首部说明了如何或者什么时候缓存,实体缓存首部说明了被缓存实体相关信息,例如验证已缓存的副本有效所需信息等
- ETag
- Expires
- Last-Modified
4. 连接管理
- HTTP是如何使用TCP连接
- TCP连接的时延,瓶颈以及存在的障碍
- HTTP的优化,包括并行连接,keep-alive和管道化连接
4.1 TCP连接
HTTP通信都是由TCP/IP承载,一旦TCP连接建立起来,Client和Server之间的报文就不会丢失。
4.1.1 TCP的可靠数据管道
TCP为HTTP提供了可靠的比特传输管道,从一端填入的字节会从另一端有序的顺序,正确的传出来。
4.1.2 TCP流是分段的,由IP分组传送
TCP的数据是通过IP分组(或者IP数据包)的小数据块来发送。
HTTP要传送一条报文,会以流的形式将数据报文通过一条打开的TCP连接按序传输,TCP收到数据流之后,将数据流分成数据段进行传输,将其封装在IP分组,通过因特网传输。
4.1.3 保持TCP连接的正确运行
TCP连接时通过端口号来保持机器上连接正确运行。
TCP连接通过源地址,源端口号,目的地址,目的端口号来识别。
4.1.4 用TCP套接字进行编程
TCP套接字API由Unix操作体统提供,其他语言也是调用的Unix的TCP套接字API。
TCP API隐藏了所有底层网络协议的握手细节,以及TCP数据流与IP分组之间的分段和重组。
4.2 对TCP性能的考虑
HTTP位于TCP的上层,HTTP事务的性能很大程度取决于底层的TCP通道性能。
4.2.1 HTTP事务的时延
- 除非Client和Server超载,或者正在处理复杂的动态资源,否则HTTP的时延就是由TCP网络时延构成。
- Client需要根据URI来确定Web服务器的IP地址和端口号,如果最近没有对URI中主机名进行访问,通过DNS解析将其解析为IP地址,这个解析过程可能会是几十毫秒甚至更多。
- Client和Server之间建立新的TCP连接请求,并等待Server端进行应答,如果事务中包含多个事务,那么这个值会快速的叠加上去。
- 请求的时间和响应的时间
这些时延取决于硬件速度,网络,服务器负载,请求和响应报文大小,Client和Server之间的距离。另外TCP协议的复杂性也对时延有很大影响。
4.2.2 性能聚焦区域
- TCP连接建立握手
- TCP慢启动拥塞控制
- 数据聚集的Nagle算法
- 用于捎带确认的TCP延迟确认算法
- TIME_WAIT时延和端口耗尽
4.2.3 TCP连接的握手时延
就是我们俗称的三次握手。
HTTP事务通常不会交互太多的数据,进行三次握手会产生一个可测的时延,ACK请求的时候可承载整个请求报文,而Server的响应报文也可以放到一个IP分组(包括高速缓存产生的304请求),所以造成的结果是小的HTTP事务可能会在TCP连接建立的时候消耗超过整体时间的50%以上,所以如果可以重用这些现存连接,可以减少TCP连接时延造成的影响。
4.2.4 延迟确认
因特网自身无法确保可靠的分组传输,因特网路由如果超负荷可以随意丢弃分组,所以可靠性需要靠TCP协议来保证数据的成功传输。
每个TCP端都会有一个序列号和数据完整性校验和,每个段的接收者收到完好的段时,都会向发送者回送小的确认分组,如果发送者没有在指定的窗口时间内收到确认信息,发送者会认为分组已被破坏或者损毁,并重发数据。
由于确认报文很小,所以TCP允许在发往相同方向的输出数据分组中进行捎带,这样可以有效的利用网络,为了增加确认报文找到同向传输数据分组的可能性,很多TCP栈都实现了延时确认的算法。
延迟确认算法是在一个特定的时间窗口,一般是100ms到200ms被将输出确认放到缓冲区,以寻找能够捎带它的输出数据分组,如果在那个时间段内没有输出数据分组,就将数据分组放在单独分组中进行传输。
不过通常是没有的,所以延迟确认算法会加大时延,可以根据操作系统调整或者禁用延迟确认算法。
4.2.5 TCP慢启动
TCP数据传输数据的性能还取决于TCP连接的使用期,TCP连接会随着时间进行自我调谐,起初会限制连接的最大速度,如果数据传输成功会提高传输速度,这种协调被称为TCP慢启动,用于防止因特网过载或者堵塞。
TCP慢启动限制了TCP端点在任意时刻可以传输的分组数,简单说每成功接收一个分组,发送端就有发送两个分组权限,然后四个,八个以此类推,这种方式被称为“打开拥塞窗口”。
由于存在拥塞控制特性,新连接的传输速度会慢一些,所以持久连接的效果会更好一些。
4.2.6 Nagle算法与TCP_NODELAY
TCP端不论实体长度为多少,都会有40个字节的标记和首部,如果TCP发送大量包,但是包含少量的数据的分组,网络的性能就会严重下降。
Nagle算法是试图在发送一个数据分组的时候,将大量的TCP数据绑定到一起,以提高网络效率。算法鼓励发送全尺寸(LAN上为1500字节),只有当所有分组都被确认后,Nagle算法才允许发送非全尺寸的分组,如果其他分组仍在传输过程中,就将那部分数据缓存下来,有只有挂起分组被确认或者缓存中积累够一个全尺寸分组的数据,才会将缓存的数据发送出去。
Nagle算法会引发HTTP的性能问题
- 小的HTTP报文可能会无法填满一个分组,可能会因为等待那些永远不会到来的额外数据而产生时延
- Nagle算法与延迟确认之间的交互存在问题,nagle算法会阻止数据的发送,直到有确认分组抵达为止,但是确认分组自身会被延迟确认算法延迟100~200毫秒。
HTTP程序通常会在自己的栈内设置参数TCP_NODELAY,来禁用Nagle算法,提高性能。
4.2.7 TIME_WAIT累积与端口耗尽
TIME_WAIT端口耗尽是很严重的性能问题,会影响到性能基准。
当TCP端口关闭TCP连接的时候,会在内存中维护一个小的控制块,用来记录最近关闭连接的IP和端口号,这类信息一般会维持一小段时间,通常是所估计的最大分段使用期的两倍(称为2MSL,通常为2分钟),以确保不会创建具有相同地址和端口号的连接。
现在几乎不可能在连接关闭的几分钟后,在操作系统会将2MSL设置为一个较小的值,但超过此值时要特别小心,分组确实被复制,如果来自之前连接的复制分组中插入了具有相同连接值的新TCP流,会破坏TCP数据。
对于服务器通常在HTTP的默认为80端口,对80端口进行监听,用TIME_WAIT防止端口号重用时,这些也限制了可用连接值组合。
在只有一个Client端和一个Server端的异常情况,构建一条TCP连接
<source-IP-address, source-port, destination-IP-address, destination-port>
其中的三个都是固定的,只有源端口号可以随意改变。
Client端每次连接到Server上去的时候,会获得一个新的源端口,以实现连接的唯一性。
4.3 HTTP连接的处理
4.3.1 常被误解的Connection首部
HTTP协议允许Client和最终源端Server之间存在一串HTTP中间实体(代理,高速缓存等),从Client端开始,逐跳的将HTTP报文经过这些中间设备,转发到源端服务器上去或者反向代理。
在两个相邻的HTTP应用程序会为它们共享的连接应用一组选项,HTTP的Connetion首部字段中有一个由逗号分隔的连接标签列表,这些标签为此连接指定了一些不会传播到其他连接中去的选项,比如可以用Connection: close
来说明发送完下一条报文之后必须关闭的连接。
Connection首部可以承载三种不同类型的标签
- HTTP首部字段名,列出与此连接相关的首部;
- 任意值标签,用于描述此连接的非标准选项;
- 值close,说明操作完成后关闭这条持久连接
如果一个连接标签中包含了一些与连接相关的信息,就不能将其转发出去。在将报文转发出去的时候必须删除Connection首部列出的所有首部字段,由于Connection首部可以防止无意中对本地首部的转发,因此将逐跳的首部名放入Connection首部被称为对首部的保护。
解释一下,只是去除HTTP首部字段,在代理第一台服务器剔除,再进行转发,其他两类不进行剔除
4.3.2 串行事务处理时延
如果连接进行简单的管理,TCP的时延就会叠加。
如果是一个嵌入三个图片的Web页面,就需要发起4个HTTP请求。如果每个事务都需要建立新的连接,那么连接,慢启动等等时延都会叠加起来。
如果加载了一个大图片,页面其他地方就会等待这个加载,会让用户觉得速度很慢,及时同时加载,也会因为不是同时加载完成,一幅比一幅慢,还是会有这种感觉,这就是产品设计来消除了。
串行加载的缺点还有就是,在浏览器加载完毕的之前,无法获取对象的尺寸,而尺寸信息会决定对象加载到屏幕的那个位置,所以在加载够足够多的对象之前,无法在屏幕上显示任何内容,用户面对的是空白屏幕,而对装载进度一无所知。
可以提高HTTP连接性能的方法
- 并行执行: 通过多条TCP连接发起并发的HTTP请求
- 持久连接: 重用TCP连接,以消除连接和关闭的时延
- 管道化连接: 通过共享的TCP连接发起并发的HTTP请求
4.4 并行连接
HTTP允许Client同时打开多条连接,并行执行多个HTTP事务。
4.4.1 并行连接可能会提高页面加载速度
并行首先加载HTML页面,然后并行处理余下的三个事务,每个事务有自己的连接,连接的时延也是重叠的。
4.4.2 并行连接不一定更快
如果带宽不足就会造成每个对象的加载进行竞争,带来的性能提升很小甚至没有提升。
打开连接会消耗很多的内存资源,并引发性能问题,对于复杂的Web页面可能会有数十甚至数百个内嵌对象,Client端就需要打开数百个连接,而对于Server端一个用户就是数百连接,而多个用户就是成倍增长,并造成服务器性能下降。
实际上,浏览器虽然使用了并行连接,也会将并行连接的总数限制在一个小的范围,而Server端可以关闭来着特定Client的连接。
4.4.3 并行连接可能让人感觉快一些
略
4.5 持久连接
Web浏览器会经常打开同一个站点的连接。例如Web页面中大部分内嵌图片都来自一个Web站点,这种特性被称为站点本地性(site locality)。
在HTTP/1.1以及HTTP/1.0的各种增强版本,允许HTTP在事务结束后仍保持在打开TCP连接被称为HTTP请求重用现存的连接,仍保持在打开状态的TCP连接被称为持久连接。非持久连接在每个事务之后关闭,持久连接会在不同事务之间保持打开状态,直到Client或者Server端断开连接。
持久连接可以避免慢启动和建立连接的时延。
4.5.1 持久以及并行连接
并行连接的缺点
- 每个事务都会打开和关闭一条新的TCP连接,会消耗时间和带宽
- TCP慢启动特性,每条新的连接的性能会有所降低
- 可打开的并行连接数量是有限制的
持久连接有一些比并行连接好的地方,降低建立连接的时延和开销,将连接保持在已调谐状态,并减少了打开连接的潜在数量,不过在管理资源的时候需要特别的小心,不然会出现大量的空闲连接,消耗本地和Server端上的资源。
持久连接和并行连接配合可能是最高效的方式。
4.5.2 HTTP/1.0+ keep-alive连接
keep-alive可以去除进行连接和关闭连接的开销,去除了慢启动阶段,请求和响应时间也有所缩减。
4.5.3 Keep-Alive操作
keep-alive在HTTP/1.1规范中没有说明,但是Client和Server之间的keep-alive仍然应用广泛。
实现HTTP/1.0 keep-alive连接的Client可以通过包含Connection: Keep-Alive首部请求将一条连接保持在打开状态。
如果Server端愿意为下一条请求连接保持在打开状态,就在响应中包含相同的首部,如果响应中没有Connection: Keep-Alive首部,client认为服务器不支持keep-alive。
4.5.4 Keep-Alive选项
Keep-Alive首部只是请求将连接保持在活跃状态,但是Client和Server端并不一定同意keep-alive会话。
- 参数timeout是在Keep-Alive响应首部发出,预估了服务器希望将连接保持活跃状态的时间。
- 参数max是在Keep-Alive响应首部发出,预估了服务器还希望为多少个事务保持此活跃的连接状态。
- 也支持任意未经处理的属性,用于诊断调试
Connection: Keep-Alive
Keep-Alive: max=5, timeout=120
说明服务器最多还会为另外5个事务保持连接或者打开状态保持到空闲2分钟后。
4.5.5 Keep-alive连接的限制和规则
- keep-alive不是默认启用
- Connection: Keep-Alive首部必须随着希望保持连接的报文一同发送
- Client接收到响应的时候,就可以获取到是否有保持连接
- 实体的主体必须有正确的Content-Length,否则无法确认报文结束和报文开始
- 代理和网关必须执行Connection首部的规则,在将报文转发出去或者将其高速缓存之前,删除Connection首部中命名的所有首部字段以及Connection本身
- 不应该与无法确定是否支持Connection首部的代理服务器建立keep-alive连接,以防出现哑代理
- 应该忽略HTTP/1.0设备的Connection首部字段
4.5.6 Keep-alive和哑代理
1. Connection首部和盲中继
问题出现在代理上,尤其那些不理解Connection首部,而且不知道沿着转发链路将其转发出去之前,应该讲Connection首部删除的代理,就是盲中继。它们只是将字节从一个连接转发到另一个连接中去,不对Connection首部做特殊处理。
正常情况下,中间的代理识别这个首部会保持在连接的ESTABLISHED状态,而不是之后的等待Server端管理连接状态,对于新的沿这个TCP链路的请求,会被忽略,所以造成了浏览器挂起,直到Client或者Server端断开连接。
2. 代理和逐跳首部
为了避免此类代理通信的问题发生,现代的代理都绝不能转发Connectin值中的首部和所有名字出现在Connection值中的首部。
如果一个代理收到了Connection: Keep-Alive首部,会应该转发这个首部和所有Keep-Alive的首部。同理还有Proxy-Authenticate,Proxy-Connection,Transfer-Encoding和Ungrage。
4.5.7 插入Proxy-Connection
Proxy-Connection可以解决一些盲中继的问题,不过Proxy-Connection是非标准首部。如果代理是盲中继,将这个首部发送给Server,Server会进行忽略,如果代理足够聪明,就可以直接将其转为Connection首部取代原来的Proxy-Connection,转发给目标服务器,来达到效果。
不过如果先经过聪明的代理,再经过盲代理,仍然会有这个问题。
4.5.8 HTTP/1.1 持久连接
HTTP/1.1 逐渐停止对keep-alive的支持,用一种持久连接的方法取代。
HTTP/1.1 的持久连接默认是激活的,如果需要关闭,需要Server端显式的返回一个Connection: close的首部。
4.5.9 持久连接的限制和规则
- 发送了Connection: close请求首部后,Client是无法在这条连接上发送更多的请求
- Client不需要再次使用该连接也可以发送Connection: close请求首部来关闭连接
- 当连接上的所有报文都是正确的,并且自定义长度,连接才能保持
- HTTP/1.1的代理必须能分别管理与Client和Server的连接,每个持久连接都只适用于一跳传输
- HTTP/1.1的代理服务器不应与HTTP/1.0的Client建立长连接,但是现在很多厂商都违背了这一规则
- 尽管Server不应该在传输报文的过程中关闭连接,而且在关闭连接前至少应该响应一条请求
- 除非重复发送请求会产生副作用,否则如果在Client收到整条响应之前连接关闭了,Client就必须重新发送请求
- 一个用户Client对任何Server或者代理最多只能维护两条持久连接,以防Server过载。
4.6 管道化连接
HTTP/1.1 允许在持久连接上可选的使用请求管道。
这是在keep-alive连接上的优化,在响应到达之前,可以将多条请求放入队列,当第一条请求通过网络流向Server,第二条和第三条请求就可以发送了,在高时延网络的条件下,可以降低网络的环回时间,提高性能。
限制条件
- 如果HTTP客户端无法确认连接是持久的,就不会使用管道化连接。
- 必须按照请求顺序返回HTTP响应,因为HTTP请求没有序号。
- HTTP的Client必须做好连接会在任意时刻关闭的准备,准备好重发所有未完成的管道化请求。
- HTTP的Client不应该使用管道化的方式发送一些会产生副作用的请求。例如POST
4.7 关闭连接的奥秘
4.7.1 “任意”解除连接
所有HTTP的Client,Server和代理都可以在任意时刻解除连接,但是对于Server永远不会知道Server端关闭空闲连接的时候,Client端是否有数据要发送,如果出现这种情况,在写入半截请求报文时出现了连接错误。
4.7.2 Content-Length以及截尾操作
每条HTTP响应都应该有精确的Content-Length首部,用以描述响应主体的尺寸。老的HTTP服务器省略Content-Length首部,或者发送错误的长度指示,就依赖Server发送的连接关闭来说明数据的真实末尾。
Client和代理收到一条随连接关闭而结束的HTTP响应,且实际传输的实体长度与Content-Length并不匹配(或者没有Content-Length)时,接收端会质疑长度的正确性。
如果接收端是一个缓存代理,接收端就不应该缓存这条响应,代理应该将报文原封不动转发出去,而不是校正有Content-Length。
4.7.3 连接关闭容限,重试以及幂等性
连接在关闭后可能会对Client造成副作用,例如Client无法确认Server端激活了多少事务,GET可以反复执行,而POST提交的时候就不能重复执行。
如果一个事务,不论是执行一次还是多次得到的结果都会相同,这个事务就是幂等的,例如GET,HEAD,PUT,DELETE,TRACE和OPTIONS方法都是这样。
Client端不应该通过管道化方式传送非幂等性请求,比如POST,所以在发送非幂等性请求就需要等待前一条请求的响应状态。
非幂等请求一定不能自动重试,浏览器可以通过提供对话框的方式询问是否再次发起事务处理。
4.7.4 正常关闭连接
TCP的连接是双向的。
1. 完全关闭和半关闭
应用程序可以关闭TCP的输入和输出信道中的任意一个,或者两者都关闭,套接字调用close()会将TCP连接的输入和输出信道都关闭,这被称作完全关闭。还可以用套接字shutdown()单独关闭输入或输出信道,这被称作半关闭。
2. TCP关闭及重置错误
简单的HTTP应用可以只使用完全关闭,但当应用程序开始与很多其他类型的HTTP Client,Server和代理进行对话且开始使用持久化连接,使用半关闭来防止对实体收到非预期的写入错误。
关闭输出信道是很安全的,连接的另一端在缓冲区读到关闭后也就知道连接关闭了。
而关闭输入信道是危险的,如果另一端(举例Client)发送信息到已关闭的输入信道,操作系统(Server端)会向另一端(Client端)发送一条TCP连接被对端重置,收到这个错误后,大多数Client操作系统认为是严重错误来处理,会删除缓冲区中未读取的数据
3. 正常关闭
正常关闭需要应用程序先关闭输出信道,然后等待对端关闭输出信道,当两端都关闭信道并告诉对方不再发送数据就可以连接完全关闭了
无法确保对等实体会出现半关闭状态,所以在应用程序半关闭之后,会周期的检查输入信道,如果在一定时间内对端没有关闭,则强制关闭连接