一、基础概念
HTTP( HyperText Transfer Protocol, 超文本传输协议 ) 是一种通信协议,它允许将 HTML (超文本标记语言) 文档从 Web 服务器传送到客户端的浏览器。
URL
URI 包含 URL 和 URN。
URL(Uniform Resource Locator,统一资源定位符),是使用浏览器访问 Web 页面时需要输入的网页地址。
URN(Uniform Resource Name,统一资源名称)
URI(Uniform Resource Identifier,统一资源标识符),URI 用字符串标识某一互联网资源,URN 表示资源的名称,URL表示资源的地址(互联网上所处的位置)。可见 URL、URN 都是 URI 的子集。
URL 基本格式如下:
- scheme:协议方案名,指定访问资源所使用的协议类型(例如:http,ftp等)
- host:服务器 IP 地址或域名
- port:端口号
- path:资源路径
- query:查询参数
- fragment:片段标识符,即
#
后的 hash 值,一般用来定位到某个位置
HTTP 报文
用于 HTTP 协议交互的信息被称为 HTTP 报文
。请求端(客户端)的 HTTP 报文叫做请求报文
,响应端(服务器端)的叫做响应报文
。
HTTP 报文本身是由多行(用 CR+LF 作换行符)数据构成的字符串文本。 HTTP 报文大致可分为报文首部
和报文主体
两块。两者由最初出现的空行(CR+LF)来划分。通常,并不一定要有报文主体。
请求报文和响应报文的结构
请求报文
- 请求行:包含请求方法,请求 URI 和 HTTP 版本。
响应报文
- 状态行:包含表明响应结果的状态码,原因短语和 HTTP 版本。
二、HTTP 方法
客户端发送的 请求报文 第一行为请求行,包含了方法字段。
GET
获取资源
当前网络请求中,绝大部分使用的是 GET 方法。
HEAD
获取报文首部。
和 GET 方法一样,只是不返回报文主体部分。
用于确认 URI 的有效性及资源更新的日期时间等。
POST
传输实体主体
主要用来传输数据,而 GET 主要用来获取资源。
PUT
上传文件。
由于自身不带验证机制,任何人都可以上传文件,存在安全性问题,因此一般的 Web 网站不使用该方法。
PATCH
对资源进行部分修改
PUT 也可以用于修改资源,但是只能完全替代原始资源,PATCH 允许部分修改。
DELETE
删除文件
与 PUT 功能相反,并且同样不带验证机制。
OPTIONS
询问支持的方法
查询请求 URI 指定的资源支持的方法。
会返回 Allow: GET, POST, HEAD, OPTIONS 这样的内容。
CONNECT
要求用隧道协议连接代理
CONNECT 方法要求在与代理服务器通信时建立隧道,实现用隧道协议进行 TCP 通信。主要使用 SSL(Secure Sockets Layer,安全套接层)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输。
TRACE
追踪路径
服务器会将通信路径返回给客户端。
发送请求时,在 Max-Forwards 首部字段中填入数值,每经过一个服务器就减 1,当数值刚好减到 0 时就停止继续传输,最后接收到请求的服务器端则返回状态码 200 OK 的响应。
通常不会使用 TRACE,并且它容易受到 XST 攻击(Cross-Site Tracing,跨站追踪)。
三、HTTP 状态码
服务器返回的 响应报文 中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求的结果。
状态码 | 类别 | 原因短语 |
---|---|---|
1XX | Informational(提示信息) | 接受的请求正在处理 |
2XX | Success(成功) | 请求正常处理完毕 |
3XX | Redirection(重定向) | 需要进行附加操作以完成请求 |
4XX | Client Error(客户端错误) | 服务器无法处理请求 |
5XX | Server Error(服务器错误) | 服务器处理请求出错 |
1XX 提示信息
- 100 Continue :表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。
2XX 成功
- 200 OK :请求成功。
- 204 No Content :请求已成功处理,但是返回的响应报文中不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。
- 206 Partial Content :表示客户端进行了范围请求,而服务器成功执行了这部分 GET 请求。响应报文中包含由 Content-Range 指定范围的实体内容。
3XX 重定向
- 301 Moved Permanently :永久性重定向。
- 302 Not Found :临时性重定向。
- 303 See Other :和 302 有着相同的功能,但是 303 明确要求客户端应该采用 GET 方法获取资源。
当返回状态码 301、302、303 时,几乎所有的浏览器都会把 POST 改成 GET,并删除请求报文内的主体,之后请求会自动再次发送。
301、302 标准是禁止将 POST 方法改成 GET 方法的,但是实际使用时大家都会这么做。
- 304 Not Modified :如果客户端发送一个条件 GET 请求(请求报文首部中包含一些条件,如: If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since),而最近资源未被修改的话,服务器就会返回 304 状态码,此时,响应报文中不包含任何响应主体的部分。
- 307 Temporary Redirect :临时重定向。与 302 Found 含义相同,但是 307 会遵照浏览器标准,不会从 POST 变成 GET。
4XX 客户端错误
- 400 Bad Request :请求报文中存在语法错误。
- 401 Unauthorized :当前请求需要有通过 HTTP 认证(BASIC 认证、DIGEST 认证)的认证信息。
- 403 Forbidden :请求被拒绝。
- 404 Not Found :服务器上没有请求的资源。
5XX 服务器错误
- 500 Internal Server Error :服务器在执行请求时发生错误。
- 503 Service Unavailable :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。
四、HTTP 首部
HTTP 首部字段有 4 种类型:通用首部字段、请求首部字段、响应首部字段和实体首部字段。
通用首部字段
首部字段名 | 说明 |
---|---|
Cache-Control | 控制缓存的行为 |
Connection | 逐跳首部、连接的管理 |
Date | 创建报文的日期时间 |
Pragma | 报文指令 |
Trailer | 报文末端的首部一览 |
Transfer-Encoding | 指定报文主体的传输编码方式 |
Upgrade | 升级为其他协议 |
Via | 代理服务器的相关信息 |
Warning | 错误通知 |
请求首部字段
首部字段名 | 说明 |
---|---|
Accept | 用户代理可处理的媒体类型 |
Accept-Charset | 优先的字符集 |
Accept-Encoding | 优先的内容编码 |
Accept-Language | 优先的语言(自然语言) |
Authorization | Web 认证信息 |
Expect | 期待服务器的特定行为 |
From | 用户的电子邮箱地址 |
Host | 请求资源所在服务器 |
If-Match | 比较实体标记(ETag) |
If-Modified-Since | 比较资源的更新时间 |
If-None-Match | 比较实体标记(与 If-Match 相反) |
If-Range | 资源未更新时发送实体 Byte 的范围请求 |
If-Unmodified-Since | 比较资源的更新时间(与 If-Modified-Since 相反) |
Max-Forwards | 最大传输逐跳数 |
Proxy-Authorization | 代理服务器要求客户端的认证信息 |
Range | 实体的字节范围请求 |
Referer | 对请求中 URI 的原始获取方 |
TE | 传输编码的优先级 |
User-Agent | HTTP 客户端程序的信息 |
响应首部字段
首部字段名 | 说明 |
---|---|
Accept-Ranges | 是否接受字节范围请求 |
Age | 推算资源创建经过时间 |
ETag | 资源的匹配信息 |
Location | 令客户端重定向至指定 URI |
Proxy-Authenticate | 代理服务器对客户端的认证信息 |
Retry-After | 对再次发起请求的时机要求 |
Server | HTTP 服务器的安装信息 |
Vary | 代理服务器缓存的管理信息 |
WWW-Authenticate | 服务器对客户端的认证信息 |
实体首部字段
首部字段名 | 说明 |
---|---|
Allow | 资源可支持的 HTTP 方法 |
Content-Encoding | 实体主体适用的编码方式 |
Content-Language | 实体主体的自然语言 |
Content-Length | 实体主体的大小 |
Content-Location | 替代对应资源的 URI |
Content-MD5 | 实体主体的报文摘要 |
Content-Range | 实体主体的位置范围 |
Content-Type | 实体主体的媒体类型 |
Expires | 实体主体过期的日期时间 |
Last-Modified | 资源的最后修改日期时间 |
五、HTTP 缓存机制
前后端的 HTTP 交互中,使用缓存可以很大程度上提升效率,良好的缓存策略可以降低资源的重复加载,提高网页的整体加载速度。
缓存策略通常分为两种:强缓存
和协商缓存
。
1. 强缓存
实现强缓存通过两种响应头实现:Expires
和 Cache-Control
。强缓存表示在缓存期间不需要请求,state code
为 200。
Expires
Expires: Wed, 22 Oct 2018 08:30:00 GMT
Expires
是 HTTP/1.0 的产物,表示资源会在 Wed, 22 Oct 2018 08:30:00 GMT
后过期,需要再次请求。并且 Expires
受限于本地时间,如果修改了本地时间,可能会造成缓存失效。
Cache-Control
Cache-Control
常见的指令有:private
、public
、no-cache
、max-age
、no-store
,默认为 private。
指令 | 说明 |
---|---|
private | 客户端可以缓存 |
public | 客户端和代理服务器都可以缓存 |
max-age=xxx | 缓存的内容将在 xxx 秒后失效 |
no-cache | 需要使用协商缓存来验证缓存数据 |
no-store | 所有内容都不会缓存 |
Cache-Control
出现于 HTTP/1.1,优先级高于 Expires
。
2. 协商缓存
如果缓存过期了,我们就可以使用协商缓存来解决问题。协商缓存需要请求,如果缓存有效会返回 304。
协商缓存需要客户端和服务端共同实现,和强缓存一样,也有两种实现方式。
Last-Modified 和 If-Modified-Since
Last-Modified
表示本地文件的最后修改日期,If-Modified-Since
会将 Last-Modified
的值发送给服务器,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来。
但是如果在本地打开缓存文件,就会造成 Last-Modified
被修改,所以在 HTTP/1.1 出现了 ETag
。
ETag 和 If-None-Match
ETag
类似于文件指纹,If-None-Match
会将 ETag
发送给服务器,询问该资源 ETag
是否变动,有变动的话就将新的资源发送回来。并且 ETag
优先级比 Last-Modified
高。
总结
对于强缓存,服务器通知浏览器一个缓存时间,如果在缓存时间内,下次请求时直接用缓存。如果不在缓存时间内,则执行协商缓存策略。
对于协商缓存,将缓存信息中的 Etag 和 Last-Modified 通过请求发送给服务器,由服务器校验,返回 304 状态码时,浏览器直接使用缓存。
客户端第一次请求:
客户端再次请求时:
六、HTTPS
HTTP 有以下安全性问题:
- 使用明文进行通信,内容可能会被窃听;
- 不验证通信方的身份,通信方的身份有可能遭遇伪装;
- 无法证明报文的完整性,报文有可能被篡改。
HTTPS(Hypertext Transfer Protocol Secure,超文本传输安全协议,常称为 HTTP over TLS,HTTP over SSL 或 HTTP Secure)并非是一种新协议,只是 HTTP 通信接口部分用 TLS 协议代替而已。通常,HTTP 直接和 TCP 通信。当使用 TLS 时,则变成先和 TLS 通信,再由 TLS 和 TCP 通信。
TLS
TLS 的前身是 SSL(Secure Sockets Layer,安全套接字层),由网景公司开发,后来被 IETF(国际互联网工程任务组) 标准化并改名。通常没有特别说明时,SSL 和 TLS 指的是同一个协议,不做严格区分。
TLS 协议位于传输层之上,应用层之下。首次进行 TLS 协议传输需要消耗两个 RTT(Round-Trip Time,往返时间),接下来可以通过 Session Resumption 减少到一个 RTT。
在 TLS 中使用了两种加密技术,分别是:对称密钥加密
和 非对称密钥加密
。
对称密钥加密
对称密钥加密(Symmetric-Key Encryption),加密和解密使用同一密钥。
优点:运算速度快;
缺点:无法安全地将密钥传输给通信方。
非对称密钥加密
非对称密钥加密,又称公开密钥加密(Public-Key Encryption),使用一对非对称的密钥。一把叫做公开密钥(public key),另一把叫做私有密钥(private key)。
公开密钥所有人都可以获得,通信发送方获得接收方的公开密钥之后,就可以使用公开密钥进行加密,接收方收到通信内容后使用私有密钥解密。
非对称密钥除了用来加密,还可以用来进行签名。因为私有密钥无法被其他人获取,因此通信发送方使用其私有密钥进行签名,通信接收方使用发送方的公开密钥对签名进行解密,就能判断这个签名是否正确。
优点:可以更安全的将公开密钥传输给通信发送方;
缺点:运算速度慢。
HTTPS 采用的加密方式
HTTPS 采用对称密钥加密和非对称密钥加密两者并用的混合加密机制。在交换密钥环节使用非对称密钥加密来保证传输过程的安全性,之后的建立通信交换报文阶段则使用对称密钥加密方式来保证通信过程的效率。
TLS 握手过程
握手阶段涉及四次通信,需要注意的是,握手阶段的所有通信都是明文的。
1. 客户端发出请求(ClientHello)
首先,客户端先向服务器发出加密通信的请求,这被叫做 ClientHello 请求。
这一步客户端主要向服务器提供以下信息:
(1)支持的协议版本,如 TLS 1.2;
(2)一个客户端生成的随机数,稍后用于生成“会话密钥”;
(3)支持的加密方法,如 RSA 公钥加密;
(4)支持的压缩方法。
2. 服务器回应(ServerHello)
服务器收到客户端请求后,向客户端发出回应,叫做 ServerHello 。
服务器的回应包含以下内容:
(1)确认使用的加密通信协议版本,如 TLS 1.2。如果浏览器与服务器支持的版本不一致,服务器关闭加密通信;
(2)一个服务器生成的随机数,稍后用于生成“会话密钥”;
(3)确认使用的加密方法,比如 RSA 公钥加密;
(4)服务器证书。
除了上面这些信息,如果服务器需要确认客户端的身份,就会再包含一项请求,要求客户端提供“客户端证书”。比如,金融机构往往只允许认证客户连入自己的网络,向正式客户提供的 USB 密钥,里面就包含了一个客户端证书。
3. 客户端回应
客户端收到服务器回应后,首先验证服务器证书是否有效。如果证书不是可信机构颁布、或者证书中的域名与实际域名不一致、或者证书已经过期,就会向访问者显示一个警告,由其选择是否还要继续通信。
如果证书没有问题,客户端就会从证书中取出服务器的公钥,然后向服务器发送以下信息:
(1)一个随机数,会用服务器公钥加密以防被窃听;
(2)编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送;
(3)客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的 hash 值,用来供服务器校验。
上面第一项的随机数,是整个握手阶段出现的第三个随机数,又称“pre-master key”。这样客户端和服务器就同时又三个随机数,接着双方就用事先商定的加密方法各自生成本次会话所用的同一把”会话密钥“。
如果上一步服务器要求客户端证书,客户端会在这一步发送证书及相关信息。
4. 服务器的最后回应
服务器收到客户端的第三个随机数 pre-master key 后,计算生成本次会话所用的”会话密钥“,然后向客户端发送下面的信息。
(1)编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送;
(2)服务器握手结束通知。这一项也是前面发送的所有内容的 hash 值,用来供客户端校验。
到此,整个握手阶段全部结束。接下来,客户端与服务器进入加密通信,就是完全使用普通的 HTTP 协议,只不过用”会话密钥“加密内容。
通过以上步骤可知,在 TLS 握手阶段,两端使用非对称密钥加密的方式来通信,但是因为非对称密钥加密损耗的性能比对称密钥加密大,所以在正式传输数据时,两端使用对称加密的方式通信。
七、HTTP/2.0
HTTP/1.x 实现简单是以牺牲性能为代价的:
- HTTP/1.x 客户端需要使用多个连接才能实现并发和缩短延迟;
- HTTP/1.x 不会压缩请求和响应头,从而导致不必要的网络流量;
- HTTP/1.x 不支持有效的资源优先级,致使底层 TCP 连接的利用率低下。
HTTP/2.0 相比于 HTTP/1.x,可以说是大幅度提高了 web 的性能。
二进制传输
HTTP/2.0 所有性能增强的核心在于新的二进制分帧层,它定义了如何封装 HTTP 消息并在客户端与服务器之间传输。HTTP/1.x 协议通过文本的方式传输数据,以换行符作为纯文本的分隔符;而 HTTP/2.0 将传输的信息分割为更小的消息和帧,并采用二进制格式编码。
多路复用
在 HTTP/2.0 中,有两个非常重要的概念:帧(frame)和流(stream)。
帧代表最小的数据单位,每个帧会标识出该帧属于哪个流,流是多个帧组成的数据流。
多路复用,就是在一个 TCP 连接中可以存在多条流,即可以发送多个请求,对端可以通过帧中的标识知道属于哪个请求。通过这个技术,可以避免 HTTP/1.x 中的队首阻塞问题,极大的提高传输性能。
首部压缩
在 HTTP/1.x 中,使用纯文本的形式传输首部,通常会给每个传输增加 500-800 字节的开销。如果使用 HTTP Cookie,增加的开销有时会达到上千字节。
为了减少这样的开销和提升性能,HTTP/2.0 使用 HPACK 压缩格式对传输的首部进行编码,减少了首部的大小。并在两端维护了索引表,用于记录出现过的首部,后面在传输过程中就可以传输已经记录过的首部的键名,对端收到数据后就可以通过键名找到对应的值。
服务端推送
在 HTTP/2.0 中,服务端可以对一个客户端请求发送多个响应。换句话说,除了对最初请求的响应外,服务端还可以向客户端推送额外资源,而无需客户端明确地请求。
可以想象以下情况,某些资源客户端是一定会请求的,这时就可以采用服务端推送,提前给客户端推动必要的资源,这样就可以相对减少一点延迟时间。当然在浏览器兼容的情况下也可以使用prefetch。
八、GET 和 POST 的区别
1. 作用
GET 用于获取数据,POST 用于提交数据。
2. 参数
GET 传递的参数只能以 QueryString 的格式带在 URL 后面,POST 参数存储在实体主体中。
GET /test/demo_form.asp?name1=value1&name2=value2
POST /test/demo_form.asp HTTP/1.1
Host: w3schools.com
name1=value1&name2=value2
因为 URL 只支持 ASCII 码,所以 GET 参数中如果存在中文等字符需要先进行编码。POST 参数支持标准字符集。
GET 参数对长度有限制,可以传输的数据量较小。实际上 HTTP 协议对 URL 长度没有限制,限制 URL 长度的大多数是浏览器或服务器的配置参数,一般最好不要超过 IE 的最大长度限制 2083 字节(2K+35)。
POST 可以传输的数据量较大。理论上是没有大小限制的,HTTP 协议规范也没有进行大小限制,但实际上 POST 所能传递的数据量大小取决于服务器的设置和内存大小。
3. 安全
这里的「安全」和通常理解的「安全」意义不同,如果一个方法是「只读」的,也就是说它不会改变服务器的状态,那么这个方法就是安全的。
GET 方法是安全的,而 POST 不是。因为 POST 的目的是传输实体主体内容,服务器可能把这个数据存储到数据库中,因此状态也就发生了改变。
安全的方法除了 GET,还有 HEAD,OPTIONS。
不安全的方法除了 POST,还有 PUT,DELETE。
4. 幂等性
幂等的 HTTP 方法,同样的请求执行一次和连续执行多次的效果是一样的,服务器的状态也是一样的。也就是说,幂等方法不应该具有副作用(统计用途除外)。
所有安全方法也都是幂等的。
在正确实现的条件下, GET、HEAD、PUT、DELETE 等方法都是幂等的,而 POST 不是。
引入幂等主要是为了处理同一个请求重复发送的情况,比如在请求响应前失去连接,如果方法是幂等的,就可以放心地重发一次请求。这也是浏览器在后退/刷新是遇到 POST 会给用户提示的原因:POST 不是幂等的,重复发送请求可能会带来意想不到的后果。
5. 可缓存
如果要对响应进行缓存,需要满足一下条件:
- 请求报文的 HTTP 方法本身是可缓存的。GET 和 HEAD 是可缓存的,PUT 和 DELETE 不可缓存,POST 在多数情况下不可缓存。
- 响应报文的状态码是可缓存的。包括:200,203,204,206,300,301,404,405,410,414,501。
- 响应报文的 Cache-Control 首部字段没有指定不进行缓存。
总结一下:
GET 方法用于获取数据,参数通过 URL 传递、长度有限制,可传输的数据量较小,安全,幂等,可缓存。
POST 方法用于提交数据,参数存储在实体主体中,可传输的数据量取决于服务器设置和内存大小,不安全,不幂等,不可缓存。
参考资料
- 上野宣. 图解 HTTP[M]. 人民邮电出版社, 2014.
- 彻底弄懂HTTP缓存机制及原理
- SSL/TLS协议运行机制的概述
- HTTP/2 简介 | Web | Google Developers
- URL最大长度问题