HTTP实用指南(上) | 青训营笔记
🍏前言
这是我参与「第四届青训营」笔记创作活动的的第8天😺
这是一篇记录HTTP协议相关内容的笔记,将从基本介绍、协议的具体分析、常见场景、实际应用以及拓展五个方面对HTTP进行学习,同时也对青训营课程中学到的知识进行了整理。
由于笔记篇幅较长,所以将内容调整为上下两部分分开发布。在HTTP实用指南(上)中,包含基本介绍、协议分析,并且参考一些文章对笔记内容进行了补充。
👨初识HTTP
背景知识
当我们在浏览器的搜索栏中输入 "toutiao.com" 会发生什么?
首先,浏览器有专门处理输入信息的部分,然后得到一个完整的请求地址。
随后会交由浏览器中专门负责发起请求的内核部分,发起一个真实请求,经过网络后到达所部署的服务器。
服务器的处理结果获得后,又会经过互联网回到我们的浏览器中,经过读取响应、渲染的一系列操作,最后页面便会加载完成,呈现给用户浏览。
提到网络请求,容易联想到TCP/IP网络模型的四层结构:从下往上分别为数据链路层(Network Interface)、网络层(Network)、传输层(Transport) 和 应用层(Application)。而今天侧重于介绍应用层的HTTP协议。
概念及特点
HTTP (Hyper Text Transfer Protocol,超文本传输协议)
- 应用层协议,基于TCP协议
超文本可以承载多种体材,包括处理页面所需要的HTML\CSS\JavaScript,以及和server通信所需要的一些API。
- 分为请求和响应两部分
HTTP请求实例:
HTTP响应实例:
-
简单可扩展
允许自定义头部(header),只需要client和server彼此能了解自定义的内容是什么含义即可。 -
无状态
每个请求之间都是孤立的,每个请求不知道之前携带过什么信息、做了什么事情。但我们也会想办法让无状态协议带上一些信息。
👨🎨协议分析
发展历程
为了解决client和server之间通信的诉求:
HTTP/1.1 被公认为是标准化协议的版本,目前也是使用时间最久的一个版本。
报文解析
以 HTTP/1.1 为例,
请求报文示例:
/* Requests */
POST / HTTP/1.1 //startline
Host: localhost:8000 //HTTP headers
User-Agent: Mozilla/5.0 (Macintosh;...)... Firefox/51.0
Accept: text/html,application/xhtml+xml,...,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-date; boundary=-12656974
Content-Length: 345
//empty line
-12656974 //body
(more data)
响应报文示例:
/* Responses */
HTTP/1.1 403 Forbidden //startline
Server: Apache //HTTP headers
Content-Type: text/html; charset=iso-8859-1
Date: Wed, 10 Aug 2016 09:23:25 GMT
Keep-Alive: timeout=5, max=1000
Connection: keep-alive
Age: 3464
Date: Wed, 10 Aug 2016 09:46:25 GMT
X-Cache-Info: caching
Content-Length: 220
//empty line
<!DOCTYPE HTML PUBLIC //body
"-//IETF//DTD HTML2.0//EN">
(more data)
各结构及其相关说明:
| 结构 | 作用 |
|---|---|
| startline | Request例:POST / HTTP/1.1 (Method + Path + Version) Response例: HTTP/1.1 403 Forbidden (Version + StatusCode + StatusMessage) |
| HTTP headers | 部分语义二者是一样的,有些是各自独有的。 |
| empty line | 空行作为分隔。 |
| body | 可以附带上一些实体信息。 |
Method
- safe (安全的): 不会修改服务器的数据的方法。
- idempotent (幂等): 同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的。所有safe的方法都是idempotent的。
| method | 描述 | safe / idempotent |
|---|---|---|
| GET | 请求一个指定资源的表示形式。使用GET的请求应该只被用于获取数据。 | safe |
| POST | 用于将实体提交到指定的资源,通常导致在服务器上的状态变化或副作用。 | / |
| PUT | 用请求有效载荷替换目标资源的所有当前表示,即已有载体的替换。 | idempotent |
| DELETE | 删除指定的资源。 | idempotent |
| HEAD | 请求一个与GET请求的响应相同的响应,但没有响应体。 | safe |
| CONNECT | 建立一个到由目标资源标识的服务器的隧道。 | / |
| OPTIONS | 用于描述目标资源的通信选项。 | safe |
| TRACE | 沿着到目标资源的路径执行一个消息环回测试。 | / |
| PATCH | 用于对资源应用部分修改。 | / |
StatusCode
| StatusCode | 含义 | 示例 |
|---|---|---|
| 1xx | 指示信息,表示请求已接收,继续处理 | / |
| 2xx | 成功,表示请求已被成功接收、理解、接受 | 200 OK - 客户端请求成功 |
| 3xx | 重定向,要完成请求必须进行更进一步的操作 | 301 - 资源(网页等)被永久转移到其他URL302 - 临时跳转 |
| 4xx | 客户端错误,请求有语法错误或请求无法实现 | 404 - 请求资源不存在,可能是输入了错误的URL |
| 5xx | 服务端错误,服务器未能实现合法的请求 | 500 - 服务器内部发生了不可预期的错误504 Gateway Timeout - 网管或者代理的服务器无法在规定的时间内获得想要的响应 |
RESTful API
REST (Representational State Transfer, 表现层状态转换)
RESTful API : 一种API设计风格
- 每一个URL代表一种资源
- 客户端和服务器之间,传递这种资源的某种表现层
- 客户端通过HTTP method,对服务器端资源进行操作,实现“表现层状态转化”
以下引用内容来自一文搞懂什么是RESTful API - 知乎 (zhihu.com):
REST架构特征(之一) - 无状态服务器不能保存客户端的信息, 每一次从客户端发送的请求中,要包含所有必须的状态信息,会话信息由客户端保存, 服务器端根据这些状态信息来处理请求。当客户端可以切换到一个新状态的时候发送请求信息, 当一个或者多个请求被发送之后, 客户端就处于一个状态变迁过程中。 每一个应用的状态描述可以被客户端用来初始化下一次的状态变迁。
| 请求 | 返回码 | 含义 |
|---|---|---|
| GET /zoos | 200 OK | 列出所有动物园,服务器成功返回了 |
| POST /zoos | 201 CREATED | 新建一个动物园,服务器创建成功 |
| PUT /zoos/ID | 400 INVALID REQUEST | 请求更新某个指定动物园的信息(提供该动物园的全部信息),用户发出的请求有错误,服务器没有进行新建或修改数据的操作 |
| DELETE /zoos/ID | 204 NO CONTENT | 删除某个动物园,删除数据成功 |
常用请求头
| 请求头 | 描述 |
|---|---|
| Accept | 接受类型,表示浏览器支持的MIME类型(对标服务端返回的Content-Type) |
| Cotent-Type | 客户端发送出去实体内容的类型 |
| Cache-Control | 指定请求和响应遵循的缓存机制,如no-cache |
| If-Modified-Since | 对应服务端的Last-Modified,用来匹配看文件是否变动,只能精确到1s之内 |
| Expires | 缓存控制,在这个时间内不会请求,直接使用缓存,服务端时间 |
| Max-age | 代表资源在本地缓存多少秒,有效时间内不会请求,而是使用缓存 |
| If-None-Match | 对应服务端的ETag,用来匹配文件内容是否改变(非常精确) |
| Cookie | 有cookie并且同域访问时会自动带上 |
| Referer | 该页面的来源URL(适用于所有类型的请求,会精确到详细页面地址,csrf拦截常用到这个字段) |
| Origin | 最初的请求是从哪里发起的(只会精确到端口),Origin比Referer更尊重隐私 |
| User-Agent | 用户客户端的一些必要信息,如UA头部等 |
常用响应头
| 响应头 | 描述 |
|---|---|
| Content-Type | 服务端返回的实体内容的类型 |
| Cache-Control | 指定请求和响应遵循的缓存机制。如no-cache |
| Last-Modified | 请求资源的最后修改时间 |
| Expires | 应该在什么时候认为文档已经过期,从而不再缓存它 |
| Max-age | 客户端的本地资源应该缓存多少秒,开启了Cache-Control后有效 |
| ETag | 资源的特定版本标识符,ETags类似于指纹 |
| Set-Cookie | 设置和页面相关的cookie,服务器通过这个头部把cookie传给客户端 |
| Server | 服务器的一些相关信息 |
| Access-Control-Allow-Origin | 服务器端允许的请求Origin头部(譬如为*) |
缓存
该部分笔记中引用块及引用内容来自文章:http面试必会的:强制缓存和协商缓存 - 掘金 (juejin.cn)
强缓存
强缓存:浏览器不会向服务器发送任何请求,直接从本地缓存中读取文件并返回。
- Expires
时间戳
Expires:过期时间,如果设置了时间,则浏览器会在设置的时间内直接读取缓存,不再请求
-
Cache-Control
功能 说明 可缓存性 no-cache: 协商缓存验证
no-store: 不使用任何缓存到期 max-age: 单位是秒,存储的最大周期,相当于请求的时间重新验证 * 重新加载 must-revalidate: 一旦资源过期,在成功向原始服务器验证之前,不能使用
cache-control是http1.1的头字段,expires是http1.0的头字段,如果expires和cache-control同时存在,cache-control会覆盖expires,建议两个都写。
协商缓存
协商缓存: 向服务器发送请求,服务器会根据这个请求的request header的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header通知浏览器从缓存中读取资源。
ETag / If-None-Match:
资源的特定版本的标识符,类似于指纹
Etag是属于HTTP 1.1属性,它是由服务器(Apache或者其他工具)生成返回给前端,用来帮助服务器控制Web端的缓存验证。 Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。
当资源过期时,浏览器发现响应头里有Etag,则再次向服务器请求时带上请求头if-none-match(值是Etag的值)。服务器收到请求进行比对,决定返回200或304。
Last-Modified / If-Modified-Since:
最后修改时间
当资源过期时(浏览器判断Cache-Control标识的max-age过期),发现响应头具有Last-Modified声明,则再次向服务器请求时带上头if-modified-since,表示请求时间。
服务器收到请求后发现有if-modified-since则与被请求资源的最后修改时间进行对比(Last-Modified),若最后修改时间较新(大),说明资源又被改过,则返回最新资源,HTTP 200 OK;若最后修改时间较旧(小),说明资源无新修改,响应HTTP 304 走缓存。
Last-Modifed/If-Modified-Since的时间精度是秒,而Etag可以更精确。
Etag优先级是高于Last-Modifed的,所以服务器会优先验证Etag
Last-Modifed/If-Modified-Since是http1.0的头字段
cookie
对于响应头中的 Set-Cookie:
| 属性 | 作用与含义 |
|---|---|
| Name=value | 各种cookie的名称和值 |
| Expires=Date | cookie的有效期,缺省时cookie仅在浏览器关闭之前有效 |
| Path=path | 限制指定cookie的发送范围的文件目录,默认为当前 |
| Domain=domain | 限制cookie生效的域名,默认为创建cookie的服务域名 |
| secure | 仅在HTTPS安全连接时,才可以发送cookie |
| HttpOnly | JavaScript脚本无法获得cookie |
| SameSite=None/Strict/Lax |
|
与SameSite属性相关的内容见笔记:WEB开发的安全之旅 (防御篇) | 青训营笔记 - 掘金 (juejin.cn)
协议发展
HTTP/2 - 更快、更简单、更稳定
帧 (frame)
HTTP/2通信的最小单位,每个帧都包含帧头,至少也会标识出当前帧所属的数据流。二进制表示。
如下图所示,每一帧中包含frame header和frame body,并且在frame header中对该帧的类型进行了标识,包含HEADERS\CONTINUATION\DATA等。
消息与数据流
消息交错发送,接收方重组织。
消息:与逻辑请求或响应消息对应的完整的一系列帧。
数据流:已建立的连接内的双向字节流,可以承载一条或多条消息。
连接与流控制
- HTTP/2连接都是永久的,而且仅需要每个来源一个连接
- 流控制:阻止发送方向接收方发送大量数据的机制
服务器推送 (Server Push)
以下引用块内容来自文章:HTTP/2 服务器推送(Server Push)教程 - 阮一峰的网络日志 (ruanyifeng.com)
头信息(header)原来是直接传输文本,现在是压缩后传输。原来是同一个 TCP 连接里面,上一个回应(response)发送完了,服务器才能发送下一个,现在可以多个回应一起发送。
对于传统的网页请求方式,每轮HTTP通信都只能请求一个资源,比如对于一个包含了一张样式表demo.css和一张图片demo.png的HTML文件demo.html,需要先发送一个对demo.html的请求,浏览器收到该资源后发现了其中的样式表和图片,还会再发送两个分别对demo.css和demo.png资源的请求。
这就是传统的网页请求方式。它有两个问题,一是至少需要两轮 HTTP 通信,二是收到样式文件之前,网页都会显示一片空白,这个阶段一旦超过2秒,用户体验就会非常不好。
而服务器推送便是对传统网页请求方式的改进:
服务器推送(server push)指的是,还没有收到浏览器的请求,服务器就把各种资源推送给浏览器。比如,浏览器只请求了
index.html,但是服务器把index.html、style.css、example.png全部发送给浏览器。这样的话,只需要一轮 HTTP 通信,浏览器就得到了全部资源,提高了性能。
HTTPS
HTTP协议通过明文传输,容易因为受到“中间人攻击”而降低了WEB安全。为了提升安全度,我们可以采用HTTPS协议,HTTPS的可靠性、完整性和不可抵赖性能够帮助我们防御中间人攻击。
概述
HTTPS (Hypertext Transfer Protocol Secure)
其在HTTP协议的基础上加上了SSL/TSL协议的加密,可以看作是HTTP协议的安全版。
HTTP默认端口80,HTTPS默认端口443。
以下引用块内容来自: 一篇文章让你彻底弄懂SSL/TLS协议 - 知乎 (zhihu.com)
SSL/TLS是一种密码通信框架,他是世界上使用最广泛的密码通信方法。SSL/TLS综合运用了密码学中的对称密码,消息认证码,公钥密码,数字签名,伪随机数生成器等,可以说是密码学中的集大成者。SSL(Secure Socket Layer)安全套接层,是1994年由Netscape公司设计的一套协议,并与1995年发布了3.0版本。
TLS(Transport Layer Security)传输层安全是IETF在SSL3.0基础上设计的协议,实际上相当于SSL的后续版本。
| HTTP | HTTPS | ||
|---|---|---|---|
| HTTP | 应用层 | HTTP | 应用层 |
| / | / | SSL/TLS | 安全层 |
| TCP | 传输层 | TCP | 传输层 |
| IP | 网络层 | IP | 网络层 |
| 网络接口 | 数据链路层 | 网络接口 | 数据链路层 |
对称加密 与 非对称加密
HTTPS传输数据的大致流程:
- 对称加密: 加密和解密都是使用同一个密钥
- 非对称加密: 加密和解密需要使用两个不同的密钥,公钥(public key) 和 私钥(private key)
🍊对于HTTPS下数据的传输,非对称加密只运用在证书验证阶段,而内容传输时使用的是对称加密。
🍎小结
在HTTP实用指南(上)中,对HTTP协议进行了基本的介绍,并对协议进行了分析。HTTP实用指南(下)将承接上部分的内容,继续介绍HTTP协议的常见场景、实际应用以及拓展。
2022/8/1