tags:
- Networks
Hyper-Text Transfer Protocol
HTTP 可以说是迄今为止诞生过最伟大的 Web 应用。它是应用层的核心之一, HTTP 根据资源的请求/分发将host分为 HTTP 客户端和 HTTP 服务器。HTTP 客户端和 HTTP 服务器通过 HTTP 报文互相通信。HTTP 就规定了应用报文的格式和报文传输的格式。
一个网页包含许多对象,可能有 HTML、JS、CSS、JPEG等不同类型的文件资源。网页上的每个资源都通过唯一的 URL 所标记,其格式可能是这样的:https://congzhi.wiki/index.html
。
如果你在浏览器中请求 HTTP 获取该资源的服务,你就会向 HTTP 服务器发送这样的请求报文:
GET /index.html HTTP/2
:authority: congzhi.wiki
:method: GET
:path: /index.html
:scheme: https
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cache-Control: max-age=0
DNT: 1
If-Modified-Since: Tue, 01 Apr 2025 16:47:42 GMT
If-None-Match: W/"67ec18ae-b162"
Priority: u=0, i
Sec-Ch-Ua: "Not(A:Brand";v="99", "Microsoft Edge";v="133", "Chromium";v="133"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Sec-Gpc: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36 Edg/133.0.0.0
当 HTTP 服务器收到 index.html
的资源请求,它就会向客户端做出响应会返回客户端所请求的报文。格式可能是这样的:
HTTP/1.1 304 Not Modified
Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Age: 523
Ali-Swift-Global-Savetime: 1743859648
Cache-Control: max-age=600
Date: Sat, 05 Apr 2025 13:27:28 GMT
EagleId: 71c87a9517438601710581137e
ETag: W/"67ec18ae-b162"
Expires: Sat, 05 Apr 2025 07:58:31 GMT
Last-Modified: Tue, 01 Apr 2025 16:47:42 GMT
Server: Tengine
Strict-Transport-Security: max-age=31556952
Timing-Allow-Origin: *
Vary: Accept-Encoding
Via: 1.1 varnish, cache74.l2cn3160[2114,2114,304-0,H], cache36.l2cn3160[2116,0], kunlun9.cn5851[0,0,304-0,H], kunlun1.cn5851[4,0]
X-Cache: HIT TCP_IMS_HIT dirn:-2:-2
X-Cache-Hits: 0
X-Fastly-Request-Id: a1b6c4d81d0a58ae6581806f0518f0a8ce43ce6c
X-Github-Request-Id: 919B:0918:21ED50:23892F:67F11F13
X-Proxy-Cache: MISS
X-Served-By: cache-itm1220032-ITM
X-Swift-Cachetime: 1296000
X-Swift-Savetime: Sat, 05 Apr 2025 12:53:37 GMT
X-Timer: S1743857617.296110,VS0,VE182
下面,我们通过这里的请求和响应报文来了解一下 HTTP 。
在请求头中,我们首先有:
GET /index.html HTTP/2
这表示我们的 HTTP GET
请求是一个 HTTP/2 的请求,夹杂在中间的 /index.html
表示我们想要从服务器获取的资源。
之后,我们有一些伪头字段用来传递请求和响应的元信息。
之后,我们就来到了 HTTP 的头部行字段。我们先来看看前三个头部行:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
这三个头部行表示客户端可以接收的相应内容类型、客户端字长的内容编码、客户端偏好的语言。
这个字段标识用户并不希望被跟踪。( 1
表示不希望)
DNT: 1
之后,我们有 HTTP 缓存相关的控制头:
Cache-Control: max-age=0
这里表示客户端希望从服务器中返回最新的资源,而不是从缓存中提供。 max-age
表示资源在缓存中的最长存活时间(以秒为单位)。一般,你可能会看到 Cache-Control: max-age=3600
表示最长缓存时间为 1 小时和 Cache-Control: no-cache
的禁用缓存。
如果你观察了服务器的响应报文,你会观察到服务器实际上给出了 304 的状态码,告诉客户端你缓存的资源并没有被修改。这时候客户端就会使用之前缓存的数据。
这里需要补充的是,HTTP 是通过键值对来缓存资源的。我们知道,每个资源都通过唯一的 URL 进行标识,这样,我们实际上就有一对键值(URL,资源)了。但我们仍然不能保证资源是唯一的,资源的 URL 可能是固定不变的,即便修改过资源,URL 依然不会改变。所以我们用 ETag
作为资源的唯一标识符。
再后面,我们还要一些缓存相关的头部行:
If-Modified-Since: Tue, 01 Apr 2025 16:47:42 GMT
If-None-Match: W/"67ec18ae-b162"
通过头部行的名称,你能大概知道它们表达的含义。 If-Modified-Since
表示客户端缓存了资源的时间戳,也就是资源缓存的时间。
If-None-Match
是资源的实体标签(ETag)。通过往请求报文中添加 ETag 信息,我们可以做到如果服务器的资源没有被修改,即 ETag 值并不会改变,与这个值相匹配,则单单返回 304 状态码来表示客户端缓存的资源仍然有效。
在学习 HTTP 协议时,教科书中往往会着重介绍 HTTP/1.1 之后的持久化连接(Connection: Keep-Alive
)。但是在我们的请求和响应报文中,我们并没有看到响应的头部行。
这是因为我们的请求报文是 HTTP/2 的,在 HTTP/2 中移除了对 Connection
的支持,在 HTTP/2 及后续的版本中,默认使用持久化连接和多路复用,不需要再通过相关的头部行来显式声明了。
再者,即使 HTTP/1.1 才引入持久化连接,但如果不显式声明 Connection: Keep-Alive
字段,默认启用持久连接。
在 HTTP 请求报文发送到 HTTP 服务器后,服务器随之给出响应和请求数据。我们来分析上面的 HTTP 响应报文。
因为在请求时,我们用 Conditional GET 来请求资源。所以我们得到了一个 304 状态码,告知客户端请求的资源未被修改,可以直接使用本地缓存。
HTTP/1.1 304 Not Modified
在响应报文中,也有许多和 HTTP 缓存相关的字段。比如:
Cache-Control: max-age=600
ETag: W/"67ec18ae-b162"
Last-Modified: Tue, 01 Apr 2025 16:47:42 GMT
Cache-Control
和 HTTP 请求报文中的 Cache-Control
的作用一样,表示缓存的有效期。这里 max-age=600
表示资源在缓存中的有效期是 10 分钟。在这 10 分钟内,客户端可以从缓存中直接读取资源(返回 200 状态码),过了这 10 分钟,客户端就需要向服务器验证资源的有效性。
ETag
字段是资源的实体标签,我们前面提到过。
Last-Modified
表示资源上次被修改的时间。
如果你使用了内容分发服务,那么你就会在HTTP 响应报文中看到许多 CDN 相关的头部字段。如下:
Age: 523
Ali-Swift-Global-Savetime: 1743859648
Cache-Control: max-age=600
EagleId: 71c87a9517438601710581137e
Server: Tengine
Strict-Transport-Security: max-age=31556952
Timing-Allow-Origin: *
Via: 1.1 varnish, cache74.l2cn3160[2114,2114,304-0,H], cache36.l2cn3160[2116,0], kunlun9.cn5851[0,0,304-0,H], kunlun1.cn5851[4,0]
X-Cache: HIT TCP_IMS_HIT dirn:-2:-2
X-Cache-Hits: 0
X-Fastly-Request-Id: a1b6c4d81d0a58ae6581806f0518f0a8ce43ce6c
X-Github-Request-Id: 919B:0918:21ED50:23892F:67F11F13
X-Proxy-Cache: MISS
X-Served-By: cache-itm1220032-ITM
X-Swift-Cachetime: 1296000
X-Swift-Savetime: Sat, 05 Apr 2025 12:53:37 GMT
X-Timer: S1743857617.296110,VS0,VE182
HTTP 是无状态的应用层协议,它并不记录客户端的状态,即每次请求都是独立的,服务器无法保存前一次请求的信息。而 Cookie 机制可以帮助服务器在后续的请求中识别用户的状态。
Cookie 最主要的作用就是跟踪用户行为,提供个性化的推送。比如说,当你登录社交媒体后,浏览不同的网页仍然保持登录状态,当你浏览购物网站时,网站根据你的偏好推送个性化的商品。
简单地说,Cookie 就是用来存储数据的键值对。
1xx:表示请求已经被接收并继续处理:
2xx:表示请求已成功被服务器处理。
3xx:表示需要客户端采取进一步操作才能完成请求。
4xx:表示客户端发出的请求有问题。
5xx:表示服务器在处理请求时出现问题。