HTTP 简介
layout: post
第一章 HTTP概述
媒体类型
MIME(Multipurpose Internet Mail Extension, 多用途因特网邮件扩展)
MIME 是一种文本标记,表示一种主要的对象类型和一个特定的子类型,中间由一条斜杠来分隔。如
- HTML文本文档:text/html
- ASKII文本文档:text/plain
- JPEG图片:img/jpeg
- GIF图片:image/gif
- PowerPoint:application/vnd.ms-powerpoint
URI
Uniform Resource Identifier, 统一资源标识符
它包括URL和URN
URL
Uniform Resource Location, 统一资源定位符
它包括:
- 协议(scheme):
http:// - 服务器的因特网地址:
www.joes-hardware.com - 指定资源:
/sepcials/saw-blade.gif
URN
Uniform Resource Name, 统一资源名
URN仍处于试验阶段
HTTP 事务
Transaction
一个 HTTP 事务由一条客户端请求命令和一个服务器响应结果组成。
通信的内容就是 HTTP 报文。
HTTP Methods
- HEAD:仅发送命名资源响应中的HTTP头部
- GET
- POST
- PUT
- DELETE
HTTP 报文
HTTP Message
HTTP 报文的起始行和首部都是纯文本,而且是结构化的。
HTTP 报文包含3个部分:
- 起始行:报文的第一行就是起始行,在请求报文中用来说明要做些什么,在响应报文中说明出现了什么情况
- 首部字段:首部的键值用冒号相隔;首部以一个空行结束
- 主体:可以包含任意二进制数据,或者是文本
连接
TCP/IP
Transmission Control Protocol / Internet Protocol
HTTP 是应用层协议。TCP/IP 是因特网传输协议。
HTTP 应用层
TCP 传输层
IP 网络层
网络特有的链路接口 数据链路层
物理网络硬件 物理层只要建立了 TCP 连接,客户端和服务器之间的报文交换就不会丢失、不会被破坏、不会错序。
Web 的结构组件
- 代理
- 缓存
- 网关
- 隧道
- Agent 代理
后面章节会介绍。
第二章 URL 和资源
URL的构成
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
注意:多个params用;相隔;多个query用&相隔。
URL中,锚点frag的内容不会传送给服务器。
编码机制
不安全的字符可以通过转义来表示。格式为%加上两个16进制数字。
如词语中文转义为%E4%B8%AD%E6%96%87,JavaScript使用encodeURI。
第三章 HTTP 报文
报文流
报文由3个部分组成:起始行(start line)、首部(header)以及可选的主体(body)。
标准的行终止序列是CRLF(ASCII码是13和10),就是一个回车符加一个换行符\r\n。但稳健的应用程序也应该接受单个换行符作为行的终止。
首部是以一个CRLF结束。
请求报文的格式
<method> <request-URL> <version>
<headers>
<entity-body>method:请求方法,如GET、HEAD
响应报文的格式
<version> <status> <reason-phrase>
<headers>
<entity-body>reason-phrase:原因短语只对人类有意义
方法
PUT和POST包含主体(请求体),GET、HEAD、TRACE、DELETE、OPTIONS不包含主体。
安全方法
GET和HEAD被认为是安全方法,它们不会产生什么动作。
HEAD
HEAD 的作用:
- 在不获取资源的情况下了解资源的情况,比如判断类型;
- 通过查看响应中的状态码,看看某个对象是否存在;
- 通过查看首部,测试资源是否被修改了。
TRACE
TRACE 用来验证请求是否如愿穿过了请求/响应链。
它不能携带主体。
TRACE 的响应主体会携带它收到的原始请求报文。
OPTIONS
OPTIONS 请求 Web 服务器告知其支持哪些方法,或者对某些特殊资源支持哪些方法。如
OPTIONS * HTTP/1.1
Host: www.abc.com
Accept: *
-----
HTTP/1.1 200 OK
Allow: GET, POST, PUT, OPTIONS
Content-length: 0首部
通用首部
请求和响应通用
| FIELD | DESCRIPTION |
|---|---|
| Connection | 允许客户端和服务器指定与请求/响应连接有关的选项 |
请求首部
| FIELD | DESCRIPTION |
|---|---|
| Host | 给出了接收请求的服务器的主机名和端口号 |
Accept 首部:
| FIELD | DESCRIPTION |
|---|---|
| Host | 给出了接收请求的服务器的主机名和端口号 |
| User-Agent | 将发起请求的应用程序名称告知服务器 |
| Accept | 告诉服务器,我能够接收哪些媒体类型 |
| Accept-Charset | 告诉服务器,我能够使用哪些字符集 |
| Accept-Encoding | 告诉服务器,我能够使用哪些编码方式 |
条件请求:
| FIELD | DESCRIPTION |
|---|---|
| Expect | 允许客户端列出某请求所要求的服务器行为 |
| If-Match | 如果实体标记与文档当前的实体标记相匹配,就获取这份文档 |
| If-Modified-Since | 除非在某个指定的日期之后资源被修改过,否则就限制这个请求 |
| If-None-Match | 如果提供的实体标记与当前文档的实体标记不相符,就获取文档 |
| If-Range | 允许对文档的某个范围进行条件请求 |
| If-Unmodified-Since | 除非在某个指定日期之后资源没有被修改过,否则就限制这个请求 |
| Range | 如果服务器支持范围请求,就请求资源的指定范围 |
安全请求:
| FIELD | DESCRIPTION |
|---|---|
| Authorization | 包含了客户端提供给服务器,以便对其自身进行认证的数据 |
| Cookie | 客户端用它向服务器传送令牌 |
| Cookie2 | 用来说明请求端支持的 cookie 版本 |
代理请求:
响应首部
实体首部
请求体和响应体的首部
| FIELD | DESCRIPTION |
|---|---|
| Allow | 列出了可以对此实体执行的请求方法 |
| Location | 告知客户端实体实际上位于何处;用于将接收端定向到资源的URL上 |
内容首部:
| FIELD | DESCRIPTION |
|---|---|
| Content-Type | 主体的对象类型 |
| Content-Length | 主体的长度 |
| Content-Encoding | 主体的编码方式 |
缓存首部:
| FIELD | DESCRIPTION |
|---|---|
| ETag | 与此实体相关的实体标记 |
| Expires | 实体不再有效,要从原始的源端再次获取此实体的日期和时间 |
| Last-Modified | 该实体最后一次被修改的日期和时间 |
第四章 连接管理
TCP 连接
TCP 连接是通过4个值来识别的:源 IP 地址、源端口号、目的 IP 地址、目的端口号。
两条不同的 TCP 连接不能拥有4个完全相同的地址组件值。
用 TOP 套接字编程
套接字(Socket),是网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是进程通信的一端。
常见套接字接口函数:
| 套接字 API 调用 | 描述 |
|---|---|
| s = socket( | 创建一个新的、未命名的、未关联的套接字 |
| bind(s, | 向套接字赋一个本地 IP 和端口号 |
| connect(s, | 创建一条连接本地套接字与远程主机及端口的连接 |
| listen(s, …) | 标识一个本地套接字,使其可以合法接受连接 |
| s2 = accept(s) | 等待某人建立一条到本地端口的连接 |
| n = read(s, buffer, n) | 尝试从套接字读取 n 个字节到缓冲区 |
| n = write(s, buffer, n) | 尝试从缓冲区写入 n 个字节到套接字 |
| close(s) | 完全关闭 TCP 连接 |
| shutdown(s, | 只关闭 TCP 连接的输入或输出端 |
| getsockopt(s, …) | 读取某个内部套接字配置选项的值 |
| setsockopt(s, …) | 修改某个内部套接字配置选项的值 |
TCP API 隐藏了所有底层网络协议的握手细节,以及 TCP 数据流与 IP 分组之间的分段和重装细节。
客户端和服务器在实现 HTTP 事务所执行的步骤:
# C 是客户端,S 是服务器
(S1) 创建新的套接字(socket)
(S2) 将套接字绑定到端口80上去(bind)
(S3) 允许套接字进行连接(listen)
(S4) 等待连接(accept)
(C1) 获取 IP 地址和端口号
(C2) 创建新的套接字(socket)
(C3) 连接到服务器 IP:port 上去(connect)
(S5) 通知应用程序有连接到来
(S6) 开始读取请求(read)
(C4) 连接成功
(C5) 发送 HTTP 请求(write)
(C6) 等待 HTTP 响应(read)
(S7) 处理 HTTP 请求报文
(S8) 回送 HTTP 响应(write)
(S9) 关闭连接(close)
(C7) 处理 HTTP 响应
(C8) 关闭连接(close)对 TCP 性能的考虑
HTTP 事务的性能,在很大程度上取决于底层 TCP 通道的性能。
HTTP 事务的时延
HTTP 事务时延的主要原因:
- 客户端要根据 URL 确定 Web 服务器的 IP 地址和端口号,即 DNS 解析时延;
- 客户端向服务器发送 TCP 连接请求,即连接建立时延;当 HTTP 事务数量增多时,这个值会增加;
- 一旦连接建立,客户端就会通过新建立的 TCP 管道来发送 HTTP 请求;服务器需要处理这个请求;传输请求和处理请求的时延;
- Web 服务器的 HTTP 响应时延。
TCP 连接的握手时延
TCP 建立连接握手的步骤:
- 客户端向服务器发送一个小的 TCP 分组,这个分组中设置了一个特殊的 SYN 标记,说明这是一个连接请求;
- 如果服务器接受了连接,就会对一些连接参数进行计算,并向客户端回送一个 TCP 分组,这个分组中的 SYN 和 ACK 标记都被置位,说明连接已被接受;
- 客户端向服务器回送一条确认信息,通知服务器连接已成功建立。
HTTP 连接的处理
并行连接
如果只能串行连接的话,比如多张图片要一张一张的下载,给人的感觉是很慢的。
现代浏览器一般都使用了并行连接,而且将总数限制在4个左右。服务器可以随时关闭来自特定客户端的超量连接。
持久连接
每次 TCP 连接都需要握手,以及 TCP 慢启动的步骤。长连接可以
在 HTTP/1.0,设置 keep-alive 可以启动长连接。如
Connection: Keep-Alive
Keep-Alive: max-5, timeout=120但 keep-alive 的问题是:
客户端和服务器中间如果有哑代理的话:客户端发送了 connection: keep-alive 首部,代理一字不漏地发送给服务器,服务器将代理认作客户端,因此同意和代理建立长连接,所以不会关闭连接;此时代理在等待服务器关闭连接,所以客户端发送的请求会被忽略,浏览器就会一直处于挂起状态。
哑代理:不能识别 Connection 首部的代理。
现代浏览器都实现了 Proxy-Connection 来解决哑代理的问题,但只能处理只有一个代理的情况。
HTTP/1.1 停止了 keep-alive 的支持,用一种名为持久连接(persistent connection)的改进型设计取代了它。它的作用与 keep-alive 相同,但机制更优。除非响应中包含了 Connection: Close,不然连接就仍维持在打开状态。
管道化连接
HTTP/1.1 允许在持久连接上可选地使用请求管道,就是把多条请求放入队列。当第一条请求发送到服务器时,第二、第三条也可以开始发送了。
关闭连接
所有 HTTP 客户端、代理和服务器都可以在任意时刻关闭一条 TCP 传输连接。
如果一个事务,不管执行一次还是很多次,得到的结果都相同,这个事务就是幂等的。GET、HEAD、PUT、TRACE、和 OPTIONS 都共享这一特性。客户端不应该以管道化方式传送非幂等请求(如 POST)。
第五章 Web 服务器
请求报文
请求报文的格式前面章节说过了,回顾一下:
- 请求行:
GET /a/b http/1.1,请求方法、URI 和版本号,以 CRLF 作为结尾; - 请求首部:以 CRLF 作为结尾;
- 请求主体:如果请求头包含
Content-Length,则读取长度可以由此决定;注意这个值有时候是不准确的。经验上来说,JavaScript 文件通常包含这个首部,且长度值是正确的。
响应报文
- 响应行:
HTTP/1.1 200 OK,版本号、状态码、原因短语,以 CRLF 作为结尾;原因短语仅给人类参考,不作开发使用; - 响应头部:包含描述响应主体 MIME 类型的
Content-Type首部;可能包含描述响应主体长度的Content-Length首部;以 CRLF 作为结尾; - 响应主体:略
MIME
Multipurpose Internet Mail Extensions
MIME 常见类型有:
- text/html;charset=utf-8
- application/javascript
- text/css; charset=utf-8
- application/json
- image/png
- text/plain
- image/svg+xml
代理
代理既是服务器,又是客户端。
代理和网关
严格来说,代理连接的是两个或多个使用相同协议的应用程序,网关连接的是两个或多个不同协议的端点。
比如,HTTP 代理可以连接 HTTP 协议的客户端和服务器;HTTP/POP 网关可以连接 HTTP 的客户端和 POP 的后端。
但实际上,代理和网关区别很模糊。商业化的代理也会实现网关的功能来支持 SSL 安全协议、SOCKS 防火墙、FTP 访问、Web 应用程序。
为什么使用代理
下面是一些示例:
- 儿童过滤器:过滤掉成人内容;
- 文档访问控制:限制指定 URL 的访问权限;
- 安全防火墙:限制某些应用层协议的数据的流入或流出;
- Web 缓存:缓存常用文档副本;
- 反向代理(reverse proxy):也称作服务器加速器;它可以创建分布式网络;
- 匿名代理:主动从 HTTP 报文中删除身份特性(如客户端 IP 地址、User-Agent 首部、From 首部、Referer 首部、cookie、URI 的回话 ID),从而提供高度的私密性和匿名性。
代理去向何处
代理服务器的部署
- 出口代理:将代理固定在本地网络的出口点,提供防火墙保护或过滤指定内容;
- 访问代理:比如做内容缓存;
- 反向代理:就是直接把代理当服务器,由代理处理所有请求;代理可以合理规划网络请求;
- 网络交换代理:略
代理如何获取流量
- 修改客户端:很多 Web 客户端,比如微软的浏览器,都支持手工和自动的代理配置;配置代理后,HTTP 请求就会直接发送给代理;
- 修改网络:这种代理称作拦截代理(intercepting proxy);依赖于监视 HTTP 流量的交换设备和路由设备,通过拦截网络流量,将流量导入到代理;
- 修改 DNS 的命名空间:放在 Web 服务器之前的代理服务器;
- 修改 Web 服务器:将 Web 服务器配置为向客户端发送一条 HTTP 重定向命令。
客户端的代理配置
PAC 文件是小型的 JavaScript 程序,客户端会取出这个文件并运行。示例:
// 必须包含这个方法
function FindProxyForURL(url, host) {
if (url.substring(0, 5) === 'http:') {
return 'PROXY http-proxy.mydomain.com:8080'
} else if (url.substring(0, 4) === 'ftp:') {
return 'PROXY ftp-proxy.mydomain.com:8080'
} else {
return 'DIRECT'
}
}WPAD 协议:参考第20章。
代理使用 URI 的规则
如果提供的是完整 URI,代理应该使用这个完整 URI;
如果提供的是部分 URI,而且有 Host 首部,应该用 Host 首部来确定原始服务器的 IP 和端口。
追踪报文
Via 首部
报文每经过一个代理节点,都必须将这个中间节点添加到 Via 列表的末尾。
Via: 1.1 proxy-62.irenes-isp.net, 1.0 cache.joes-hardware.com但由于隐私和安全问题,很多网站都没有 Via 字段。
Server 首部
Server 是响应字段,它描述了原始服务器使用的软件。
Server: Microsoft-IIS/10.0代理应该添加的是 Via 首部,不应该修改 Server 首部。
TRACE 方法
可以使用 Max-Forwards 来限制 TRACE 和 OPTIONS 请求所经过的代理跳数。如果 Max-Forwards 的值为0,那么即使接收者不是原始服务器,它也必须将 TRACE 报文回送给客户端,而不应该继续转发。
TRACE 响应的 Content-Type 是 message/http。
代理认证
Proxy Authentication
代理可以限制对内容的请求,客户端需要提供 Proxy-Authentication 首部来验证身份,否则收到 407 Proxy Authorization Required 响应。
OPTIONS 方法
判断服务器支持的方法。
Allow 首部
请求中如果有 Allow,表示建议服务器支持某些方法。
响应中如果有 Allow,表示服务器支持的所有方法。
缓存
Web 缓存是可以自动保存常见文档副本的 HTTP 设备。
命中和未命中的
缓存命中(cache hit)
缓存未命中(cache miss)
缓存的拓扑结构
私有缓存:Web 浏览器有内建的私有缓存。
公有缓存:就是缓存代理服务器(caching proxy server / proxy cache)。
缓存可以是多层级的,甚至是网状的(cache mesh)。
缓存的处理步骤
- 接收:缓存从网络中读取抵达的请求报文;
- 解析:缓存对报文进行解析,提取出 URL 和各种首部;
- 查询:缓存查看是否有本地副本可用,如果没有,就获取一份副本(并将其保存在本地磁盘);
- 新鲜度检测:缓存查看已缓存副本是否足够新鲜,如果不是,就询问服务器是否有任何更新;
- 创建响应:缓存会用新的首部和已缓存的主体来构建一条响应报文;
- 发送:缓存通过网络将响应发回给客户端;
- 日志:缓存可选地创建一个日志文件条目来描述这个事务。
新鲜度检测
文档过期
document expiration
服务器用 HTTP/1.0+ 的 Expires ,和 HTTP/1.1 的 Cache-Control: max-age 来指定过期日期。
Expires 指定一个绝对的过期时间:
Expires: Fri, 05 Jul 2021, 05:00:00 GMTCache-Control 指定文档的最大使用期,就是保质期:
Cache-Control: max-age=864000 # 保质期864000s,就是10天服务器再验证
revalidation
文档过期,意味着到了要进行核对的时间了,不能表示文档内容发生了变化。
如果文档过期:
- 如果再验证显示内容发生了变化,缓存会获取一份新的文档副本,保存并发送给客户端;
- 如果再验证显示内容没有变化,缓存只需要获取新的首部,包括新的过期日期。
条件方法进行再验证
再验证触发的前提是,文档过期了或者没有设置文档过期时间。
所有的条件首部都以 If- 开头。
If-Modified-Since
If-Modified-Since 首部存放了文档最后的修改日期。服务器解析出这个日期进行比对,如果缓存的最后修改日期和服务器文档当前的最后修改日期相符,则返回 304 Not Modified。如果不相符,则返回新文档。
返回新文档时,服务器还可以在响应首部添加 Last-Modified,标识最新的文档最后修改日期。
示例:
if-modified-since: Tue, 13 Jul 2021 02:18:20 GMT有些服务器将 If-Modified-Since 的值作为字符串进行比对。
If-None-Match
仅使用最后修改日期再验证是不够的。比如有些服务器无法准确判定最后修改日期,或者文档经常被修改且修改内容不重要。
If-None-Match 首部存放了文档的实体标签(ETag),来标识文档的版本。
同样,如果 ETag 相符,服务器返回 304 Not Modified;如果不相符,则返回新文档,同时响应首部中包含 ETag 标签。
示例:
if-none-match: W/"191f7d3a60c443a12f17c473a4c58763"有些网站会 If-Modified-Since 和 If-None-Match 两个首部都用,比如 mozilla 文档。
其他条件首部
If-Unmodified-Since、If-Range 和 If-Match。
控制缓存的能力
下面是指定文档过期时间的几种方式,以优先级递减的顺序:
Cache-Control: no-store # 禁用缓存
Cache-Control: no-cache # 可以缓存,但必须先对服务器发送再验证请求
Cache-Control: must-revalidate
Cache-Control: max-age
Expires # 不推荐使用,因为服务器时间很可能不正确或不同步网关、隧道和中继
网关
网关(gateway)是资源和应用程序之间的粘合剂。
HTTP/*: 服务器端 Web 网关
当请求流入服务器时,网关会将 HTTP 请求转换为其他协议。
比如:
# HTTP客户端
GET ftp:// ftp.irs.gov/pub/OO-index.txt HTTP/1.0
Host: ftp.irs.gov
User-Agent: SuperBrowser 4.2这是一条对 FTP 资源的 HTTP 请求,网关会通过 FTP 请求到服务器上进行文件的查询和获取。
隧道
通过 HTTP 应用程序访问使用非 HTTP 协议的应用程序。
中继
HTTP 中继(relay)是没有完全遵循 HTTP 规范的简单 HTTP 代理。
第四章提到了,当客户端和服务器中间有盲中继时,由于不能处理 Connection: keep-alive 首部,导致客户端处于挂起状态直至超时。
Web 机器人
Web robot
Web 机器人是能够在无需人类干预的情况下自动进行一系列 Web 事务处理的软件程序。很多机器人会从一个 Web 站点逛到另一个 Web 站点,获取内容,跟踪超链,处理数据。
Web 机器人的示例:
- 股票图形机器人每隔几分钟就会请求股票市场的服务器,来构建股票市场价格趋势图;
- Web 统计机器人会收集与万维网规模及发展有关的统计信息,它们会在 Web 上游荡,统计页面数量,记录每个页面大小、语言、媒体类型等 (netcraft.com);
- 搜索引擎机器人会搜集它们所找到的所有文档,以创建搜索数据库。
爬虫
爬虫 (crawler),也叫蜘蛛 (spider),从第一个 Web 页面,获取这个页面指向的所有页面,依此类推。
根集
root set
爬虫开始访问的 URL 初始集合被称作根集。
根集不需要有很多页面,就可以涵盖一大片 Web 结构。
搜索引擎使用的那些爬虫,都为用户提供了向根集提交新页面的方式。
避免环路
爬虫机器人要特别小心不要陷入循环或环路,所以机器人必须要知道它们到过何处。
存储机器人去过的 URL,需要复杂的数据结构,其在访问速度和内存使用都应该是非常高效的。
下面列出了大规模 Web 爬虫对访问 URL 进行管理时使用的一些有用的技术:
- 搜索树和散列表:来记录已访问的 URL;这些是加速 URL 查找的软件数据结构;
- 有损的存在位图:为了减少空间,大型爬虫会使用有损数据结构,比如存在位数组(presense bit array)。用一个散列函数将每个 URL 都转换成一个定长的数字,这个数字在数组中有个相关的存在位;爬行过一个 URL 后,就将响应的存在位置位。如果存在位已经置位,则爬虫认为已经爬过那个 URL 了;
- 检查点:已访问 URL 列表保存到硬盘;
- 分类:使用机器人集群,每个独立的计算机是一个机器人,分别爬行分配的特定 URL 片。
规范化 URL
为了防止互为别名的 URL 出现,可以将 URL 进行格式规范:
- 如果没有指定端口,则默认添加
:80; - 将所有
%xx转换成等价字符; - 删除 # 标签
避免循环重复的技术手段
- 规范化 URL;
- 广度优先的爬行:这样可以将环路的影响最小化;即便碰到机器人陷阱,也可以在回到环路中获取的下一个页面之前,从其他 Web 站点中获取成百上千的页面;
- 节流:限制一段时间内机器人可以从一个 Web 站点获取的页面数量;
- 限制 URL 的大小:如拒绝爬行长度超过 1KB 的 URL;
- 站点黑名单:恶意站点要拉入黑名单;
- 模式检测:比如具有某种重复机制的 URL,如“/a/b/a/b/a/b”,这样的 URL 可能是环路;
- 内容指纹:通过页面内容和字节长度来计算出一个校验和(checksum),来标识这个站点的唯一性;任何动态内容(如添加日期、访问计数)都可能会阻碍重复检测;
- 人工监测。
机器人的 HTTP
机器人和其他 HTTP 客户端重续没有什么区别,它们也要遵守 HTTP 规范。
识别请求首部
- User-Agent: 将机器人名字告知服务器;
- From: 提供机器人的用户/管理者的 E-mail 地址;
- Accept: 告知接受的媒体类型;
- Referer: 提供来源,就是从哪个地方进来当前站点的。
拒绝机器人访问
机器人可能会由于环路,机器人会向 Web 服务器发出大量的负载;或者访问到一些私密信息,即便站点把内容删除,一段时间内还是可以在搜索引擎上看到它。
通过在服务器上设置 robots.txt 文件,来阻止机器人访问,或限制机器人仅访问指定的页面。
或者设置 HTML 的元标签来设置机器人的访问权限:
<META NAME="ROBOTS" CONTENT="NOINDEX,NOFOLLOW">大的搜索引擎基本都实现了这样的规范。
HTTP-NG
HTTP 发展存在的问题
- 复杂性:HTTP 相当复杂,由于存在一些复杂的、相互交织的要求,以及连接管理、报文处理和功能逻辑之间的混合作用,要想正确地实现 HTTP 软件肯定是非常痛苦、容易出错的;
- 可扩展性:HTTP 很难实现递增式扩展。很多流传下来的 HTTP 引用程序中都没有自主的功能性扩展技术,使协议的扩展无法兼容;
- 性能:HTTP 中有些部分效率不高,其中很多低效特性会随着高时延、低吞吐量的无线访问技术的广泛使用而变得更加严重;
- 传输依赖性:HTTP 是围绕 TCP/IP 网络协议栈设计的。HTTP 要位替代协议提供更多的支持,才能作为一个更广阔的报文发送瓶体啊应用于嵌入式和无线应用程序之中。
HTTP-NG 是想要解决这些问题。但是目前还没有什么主导力量驱动 HTTP-NG 的应用。
客户端识别与 cookie 机制
用户相关的 HTTP 头部
- From: 用户的 E-mail 地址
- User-Agent: 用户的客户端软件
- Referer: 用户从哪个页面跳转过来的
- Authorization: 用户名和密码的加密串
- Client-IP: 客户端的 IP 地址
- X-Forwarded-For: 客户端的 IP 地址
- Cookie: 用来识别用户和持久对话
胖 URL(fat url):就是把信息(比如用户信息、购物车信息)都放在 URL 中。
Cookie
Cookie 有版本0和版本1。
版本0
Set-Cookie: session-id=002-114265; session-id-time=100784800; domain=.baidu.comSet-Cookie 的值是键值对,下面是键值对列表(所有的属性都可选):
- NAME=VALUE,任意信息属性;
- expires=,指定一个日期字符串来定义 cookie 的生存期;如果没写,则回话结束后 cookie 过期;
- domain=,只向指定域的服务器发送 cookie;
- path=,只向以指定 path 开头的域对应的服务器发送 cookie;
- secure,只有 HTTP 使用 SSL 安全连接才发送 cookie。
Set-Cookie 每次只能设置一条 cookie,所有响应首部通常会有多个 Set-Cookie。
版本1
版本1使用的是 Set-Cookie2,它有更多的属性如:version、discard、domain、max-age、port 等。
Cookie 和缓存
Cache-Control 会缓存 cookie,如果不希望缓存 cookie 的话,可以设置:
Cache-Control: no-cache="Set-Cookie"
# 或者
Cache-Control: must-revalidate, max-age=0基本认证机制
base authentication
示例
用户请求一张家庭照片:
GET /family/jeff.jpg HTTP/1.0服务器不能识别用户信息,返回:
HTTP/1.0 401 Authorization Required
WWW-Authenticate: Basic realm="family"客户端需要进行身份认证,再重新请求这个照片:
GET /family/jeff.jpg HTTP/1.0
Authorization: Basic YnJpY4tdG90dHk6T3ch服务器返回图片:
HTTP/1.0 200 OK
Content-Type: image/jpeg基本认证首部
WWW-Athenticate: Basic realm=quoted-realm:响应首部指定要访问的安全域;Authorization: Basic base64-username-and-password:请求首部用来标记身份信息。
Authorization 头部是用 base64 方法,将 “username
基本认证的安全缺陷
- base64 编码的用户名密码可以很轻易的被反向解码出来;
- 即使密码是加密的,第三方用户仍然可以捕获被修改过的用户名和密码;
- 如果密码被劫持,用户的其他平台的密码可能是相同的,导致其他平台也危;
- 假冒服务器很容易骗过基本认证。
摘要认证
digest authentication
基本认证便捷灵活,但极不安全。
摘要认证目前没有得到广泛应用。
安全 HTTP
HTTPS
使用 HTTPS 时,所有的 HTTP 请求和响应数据在发送到网络之前,都要进行加密。HTTPS 在 HTTP 下面提供了一个传输级的密码安全层,如下:
HTTP 应用层
SSL or TLS 安全层 # HTTPS 新增的安全层
TCP 传输层
IP 网络层
网络接口 数据链路层SSL: Security Socket Layer, 安全套接层
TLS: Transport Layer Security, 传输层安全,它是 SSL 的后继者
数字加密
一些名词:
| 名称 | 含义 |
|---|---|
| 密码 | 对文本进行编码,使偷窥者无法识别的算法。或者叫加密 |
| 密钥 | 改变密码行为的数字化参数 |
| 对称密钥加密系统 | 编码和解码使用相同密钥的算法 |
| 不对称密钥加密系统 | 编码和解码使用不同密钥的算法 |
| 公开密钥加密系统 | 一种能够使数百万计算机便捷地发送机密报文的系统 |
| 数字签名 | 用来验证报文未被伪造或篡改的校验和 |
| 数字证书 | 由一个可信的组织验证和签发的识别信息 |
密码 (cipher):是一套编码方案。使用密码加密之前的报文称为明文 (plaintext / cleartext),加密之后的称为密文 (ciphertext)。
密钥 (key):服务于密码的钥匙。比如简单的加密算法是 “循环移位 3 字符”,带有密钥的算法是 “循环移位 N 字符”,其中 N 就是密钥。
对称密钥加密技术
symmetric-key
对称密钥加密,指编码时和解码时使用的密钥一样(decode-key=encode-key)。
公开密钥加密技术
公开密钥加密使用非对称加密。
每台主机的编码密钥是众所周知的,但只有主机才知道私有的解密密钥。
每个人都可以用同一个密钥对发给主机 X 的报文进行编码,但除了 X,其他人都无法对报文进行解码。
RSA
RSA 是研究得最广泛的公钥算法。
混合加密系统
通常的做法是,两个节点通过公开密钥加密建立起安全通信,然后使用这个安全通道发送临时的随机对称密钥,通过更快的对称加密技术对其他数据进行加密。
数字签名
除了加密解密报文之外,加密系统还可以对报文进行签名 (sign),以说明是谁编写的报文,同时证明报文没有被篡改过。这种技术叫做数字签名 (digital sign)。
数字证书
数字证书 (certs) 包含了由每一个受信任组织担保的用户或公司的相关信息。类似于现实中的身份证、护照或驾照。
证书包含的内容
证书通常都是由官方的、受人尊敬的证书颁发机构以数字方式签发的。个人也可以创建,但是不具备公共使用价值。
- 对象的名称(人、服务器、组织等);
- 过期时间;
- 证书发布者(由谁位证书担保);
- 来自证书发布者的数字签名;
- 对象的公开密钥;
- 证书签名算法等。
X.509 v3 证书
证书有很多不同的格式,不过现在的大多数证书都以一种标准格式、X.509 v3,来存储它们的消息。
X.509 证书字段:
| 字段 | 描述 |
|---|---|
| 版本 | 版本号,现在使用的通常是3 |
| 序列号 | CA (证书颁发机构) 生成的唯一整数 |
| 签名算法 ID | 签名所使用的加密算法。例如:“用 RSA 加密的 MD2 摘要” |
| 证书颁发者 | 发布并签署这个证书的组织名称,以 X.500 格式表示 |
| 有效期 | 此证书何时有效,由一个起始日期和一个结束日期来表示 |
| 对象名称 | 人或组织,以 X.500 格式表示 |
| 对象的公开密钥 | 公开密钥,公开密钥使用的算法,以及所有的参数 |
| 发布者唯一 ID (可选) | |
| 对象唯一 ID (可选) |
用证书对服务器进行认证
通过 HTTPS 建立了一个安全 Web 事务之后,现代浏览器都会自动获取所连接服务器的数字证书。
HTTPS
HTTPS 就是在安全的传输层上发送的 HTTP。
HTTP 安全层是通过 SSL 或 TLS 来实现的。
HTTPS 方案
安全 HTTP 是可选的。
如果 URL 的方案是 http,客户端默认就会打开一条到服务器端口80的连接;
如果 URL 的方案是 https,客户端默认就会打开一条道服务器端口443的连接。
对于加密的 https 事务而言,TCP 连接之后会多一次 SSL 安全参数握手,然后才开始 HTTP 请求。
实体和编码
报文是箱子,实体是实物
HTTP 实体首部:
- Content-Type: 实体类型;如
Content-Type: multiple/form-data; boundary=ad83lh2,Content-Type: text/html; charset=utf-8 - Content-Length: 实体大小;它是 gzip 以后的文档长度
- Content-Language: 文件是提供给指定的某种语言使用者(比如说明站点是给德语用户使用的),很少用
- Content-Encoding: 实体使用的编码类型,常用值 “gzip”;它对应的请求首部的字段是
Accept-Encoding; - Content-Range: 这是一个部分实体,说明它是整体的哪个部分,如
content-range: bytes 0-499/25500;它对应的请求首部的字段是Range; - Expires: 文档的过期日期
- Cache-Control: 指出文档如何缓存
- Last-Modified: 内容在服务器上创建或最后修改的日期时间
- ETag: 文档的唯一验证码
- Allow: 资源允许的请求方法
HTTP 报文示例:
HTTP/1.0 200 OK
Content-type: text/plain
Content-length: 18
Hi! I'm a message!传输过程长这样:
00: 4854 5450 2f31 3e30 2032 3030 204f 4b0d
16: 0a43 6f6e 7465 6e74 2d74 7970 653a 2074
32: 6578 742f 706c 6169 6e0d 0a43 6f6e 7465
48: 6e74 2d6c 656e 6774 683a 2031 380d 0a0d # 注意这里两个0d0a(即CRLF)的后面就是请求主体
64: 0a48 6921 2049 276d 2061 206d 6573 7361
80: 6765 210a国际化
HTTP 对国际性内容的支持
HTTP 报文中可以承载以任何语言表示的内容,就像它能承载图像、影片或任何类型的媒体那样。对 HTTP 来说,实体主体只是二进制信息的容器而已。
在请求首部中:
Accept-Language: fr, en;q=0.8
Accept-Charset: iso-8859-1; utf-8Accept-Language 指用户可以接受的语言。其中 en;q=0.8 的 q 指的是 quality factor,指的是 en 的优先级是0.8(默认是1.0)。
Accept-Charset 指客户端支持的字符集编码。
字符集与 HTTP
HTTP 字符集的值说明如何把实体内容的二进制码转换为特定字母表中的字符。
在响应首部中设置 MIME 的 charset:
Content-Type: text/html; charset=utf-8UTF-8 这种字符编码比较复杂,它是无模态的可变长 (variable-length) 编码,就是每个字符的位数都是可变的。(编码规则略)
如果响应首部没有列出字符集的话,需要从文档中推断出字符集。对 HTML 文档可以这么写:
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
</HEAD>语言标记
语言标记通常包括两个子标记 (也可以有更多的子标记),如 en-GB,de-CH,zh-xiang(分别表示英国英语、瑞士德语、湖南中文)。
主子标记表示语言,第二个子标记表示地区。
所有标记都是忽略大小写的,但通常语言都是用全小写表示。
内容协商与转码
共有3种方法可以决定服务器上哪个页面最适合客户端。
- 客户端驱动:客户端自己选择,然后发起请求;优点是准确,缺点是至少要发送两次请求;
- 服务器驱动:服务器检查客户端请求首部来决定提供版本;优点是快,缺点是可能不准确;
- 透明:由中间代理代替客户端进行请求;目前没有正式规范。
Web 主机托管
虚拟主机托管
许多 Web 托管者通过让顾客共享一台计算机来提供便宜的 Web 主机托管服务,称为共享主机托管或虚拟主机托管。
假如同一台计算机上部署了多个 Web 服务器,比如 joes.hardware.com/index.html 和 marys.antiques.com/index.html,我们访问 joes.hardware.com 时,DNS 解析出 Web 服务器的 IP,那它怎么判断我们要的是 joes.hardware.com 还是 marys.antiques.com 的 index.html 呢?
通过 URL 路径
比如 Joes 的路径要这么写 /joe/index.html,Mary 的路径要这么写 /mary/index.html。
这种方法很糟糕。
绑定不同的端口号
为每个域名绑定不同的端口号,但这种方法导致只能有一个用户使用80端口号。
通过不同的 IP 地址
现在通用的方式是,为每个虚拟主机域名绑定自己的 IP 地址。这样就能正确的解析出自己的服务器了。
Host 首部
为避免过度的地址消耗和虚拟 IP 地址的限制,HTTP/1.1 添加了 Host 首部。如
GET /index.html HTTP/1.1
Host: marys.antiques.comURL 也可以包含主机名,此时 Host 首部必须跟 URL 中的主机名保持一致。
使网站更可靠
镜像的服务器集群
集群通常会有一个主服务器 (master origin server),它含有内容作者上传的原始内容。然后其他镜像服务器称为复制原始服务器 (replica origin server)。
第一种情况是服务器在一个机房。对外部来说,内容所在的 IP 地址就是交换机的 IP 地址,交换机负责把请求发送到服务器上去。
第二种情况是服务器分布在不同的城市。通过 HTTP 重定向或者 DNS 重定向来把客户端的请求导向特定的服务器。见20章。
内容分发网络
CDN, Content Delivery Network
就是对特定内容进行分发的专门网络,使得用户可以就近获取所需内容。
发布系统
就是创建 Web 页面放到服务器上的工具。略
重定向与负载均衡
重定向包括通用重定向、代理重定向和缓存重定向。这里我们只说明 HTTP 重定向和 DNS 重定向两种通用重定向。
HTTP 重定向
客户端向重定向服务器发送一条请求,重定向服务器向客户端发送一条到指定服务器的 HTTP 重定向。
示例:
GET /hammers.html HTTP/1.0
Host: www.joes-hardware.com
HTTP/1.0 302 Redirect
Server: Stronghold/2.4.2 Apache/1.3.6
Location: http://161.58.228.45/hammers.html
# 浏览器会用重定向URL重新发送请求
GET /hammers.html HTTP/1.0
Host: 161.58.228.45HTTP 重定向的缺点:
- 需要原始服务器 (就是负责重定向的服务器) 进行大量处理来判断要重定向到哪台服务器上去,非常耗时;
- 增加了用户时延,因为增加了一次请求;
- 如果重定向服务器故障,站点就会瘫痪。
DNS 重定向
就是添加一个 DNS 服务器,比如百度,访问站点的域名时,DNS 服务器会把这条请求重定向到指定的服务器 IP 地址(比如离得近的服务器、或负载较轻的服务器)。
