1 基础篇
1.1 定义
HTTP,英文全称为 HyperText Transfer Protocol,翻译过来就是 超文本传输协议。
HTTP 是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范。
我们将这句话按 协议、传输、超文本 拆分开来理解,得到以下内容:
协议:
HTTP 是一个用在计算机世界里的协议,它确立了一种计算机之间交流通信的规范,以及相关的各种控制和错误处理方式。
传输:
HTTP 专门用来在两点之间传输数据,不能用于广播、寻址或路由。
超文本:
HTTP 传输的是文字、图片、音频、视频等超文本数据。
1.2 HTTP报文
报文结构
HTTP 协议的请求报文和响应报文的结构基本相同,由三大部分组成:
-
起始行(start line):描述请求或响应的基本信息;
-
头部字段集合(header):使用 key-value 形式更详细地说明报文;
-
消息正文(entity/body):也叫"实体",实际传输的数据,它不一定是纯文本,可以是图片、视频等二进制数据。
HTTP 协议规定报文必须有 header,但可以没有 body,而且在 header 之后必须要有一个“空行”,也就是“CRLF”,十六进制的“0D0A”。
所以,一个完整的 HTTP 报文就像是下图的这个样子,注意在 header 和 body 之间有一个“空行”。我们可以想象一下,起始行对应的是小人的头发,头部对应的是小人的头,空行对应的是小人的脖子,实体对应的是小人的身体。是不是还挺好记的
看看下面这张没有 body 的图,理解会更深刻一点。
起始行
因为请求报文和响应报文的起始行有一定区别,所以我们分开介绍。
请求行
HTTP 请求报文里的起始行,也称请求行,它简要地描述了客户端想要如何操作服务器端的资源。
举个栗子:
GET / HTTP/1.1
我们拆开来看,请求行由三部分构成:
- 请求方法:是一个动词,如 GET/POST,表示对资源的操作;
- 请求目标:通常是一个 URI,标记了请求方法要操作的资源;
- 版本号:表示报文使用的 HTTP 协议版本。
这三个部分通常使用空格(space)来分隔,最后要用 CRLF 换行表示结束。
状态行
HTTP 响应报文里的起始行,不叫响应行,而叫状态行,意思是服务器响应的状态。
举个栗子
HTTP/1.1 200 OK
我们发现,状态行同样有三部分组成:
- 版本号:表示报文使用的 HTTP 协议版本;
- 状态码:一个三位数,用代码的形式表示处理的结果,比如 200 是成功,500 是服务器错误;
- 原因:作为数字状态码补充,是更详细的解释文字,帮助人理解原因。
头部字段
如下图,我们可以看到请求头和响应头。
请求头和响应头的结构基本是一样的,所以我把请求头和响应头里的字段放在一起介绍。
头部字段是 key-value 的形式,key 和 value 之间用“:”分隔,最后用 CRLF 换行表示字段结束。比如在“Host: 127.0.0.1”这一行里 key 就是“Host”,value 就是“127.0.0.1”。
使用头字段需注意以下几点:
- 字段名不区分大小写;
- 字段名里不允许出现空格,可以使用连字符“-”,但不能使用下划线“_”;
- 字段名后面必须紧接着“:”,不能有空格,而“:”后的字段值前可以有多个空格;
- 字段的顺序是没有意义的,可以任意排列不影响语义;
- 字段原则上不能重复,除非这个字段本身的语义允许,例如 Set-Cookie。
实体
就是具体的数据了,也就是body部分。请求报文对应请求体, 响应报文对应响应体。
1.3 HTTP请求方法
对请求方法的理解
请求方法是客户端发出的、要求服务器执行的、对资源的一种操作;请求方法是对服务器的“指示”,真正应如何处理由服务器来决定;
有什么请求方法
目前 HTTP/1.1 规定了八种方法,单词都必须是大写的形式:
-
GET:获取资源,可以理解为读取或者下载数据;
-
HEAD:获取资源的元信息;
-
POST:向资源提交数据,相当于写入或上传数据;
-
PUT:类似 POST;
-
DELETE:删除资源;
-
CONNECT:建立特殊的连接隧道;
-
OPTIONS:列出可对资源实行的方法;
-
TRACE:追踪请求 - 响应的传输路径。
GET 和 POST 有什么区别
GET 在浏览器回退时是无害的,而 POST 会再次提交请求;
GET 请求会被浏览器主动缓存,而 POST 不会,除非手动设置;
GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留;
GET 请求在 URL 中传送的参数是有长度限制的,而 POST 没有限制;
GET 参数通过 URL 传递,POST 放在 Request body 中。
1.4 HTTP 响应状态码
状态码在响应报文里表示了服务器对请求的处理结果;状态码是十进制的三位数,分为五类,从 100 到 599;这五类的具体含义是:
-
1××:提示信息,表示目前是协议处理的中间状态,还需要后续的操作;
-
2××:成功,报文已经收到并被正确处理;
-
3××:重定向,资源位置发生变动,需要客户端重新发送请求;
-
4××:客户端错误,请求报文有误,服务器无法处理;
-
5××:服务器错误,服务器在处理请求时内部发生了错误。
接下来挑一些实际开发中比较有价值的状态码逐个介绍。
1xx
- 101 Switching Protocols 偶尔能够见到,客户端要求在 HTTP 协议的基础上改成其他的协议继续通信,比如 WebSocket。而如果服务器也同意变更协议,就会发送状态码 101,但这之后的数据传输就不会再使用 HTTP 了。
2xx
-
200 OK 是最常见的成功状态码,表示一切正常,服务器如客户端所期望的那样返回了处理结果,如果是非 HEAD 请求,通常在响应头后都会有 body 数据。
-
204 No Content 是另一个很常见的成功状态码,它的含义与“200 OK”基本相同,但响应头后没有 body 数据。所以对于 Web 服务器来说,正确地区分 200 和 204 是很必要的。
-
206 Partial Content 是 HTTP 分块下载或断点续传的基础,在客户端发送“范围请求”、要求获取资源的部分数据时出现,它与 200 一样,也是服务器成功处理了请求,但 body 里的数据不是资源的全部,而是其中的一部分。
3xx
-
301 Moved Permanently 俗称“永久重定向”,含义是此次请求的资源已经不存在了,需要改用新的 URI 再次访问。
-
302 Found 曾经的描述短语是“Moved Temporarily”,俗称“临时重定向”,意思是请求的资源还在,但需要暂时用另一个 URI 来访问。
举个栗子说明两者的差别
比如,你的网站升级到了 HTTPS,原来的 HTTP 不打算用了,这就是“永久”的,所以要配置 301 跳转,把所有的 HTTP 流量都切换到 HTTPS。
再比如,今天夜里网站后台要系统维护,服务暂时不可用,这就属于“临时”的,可以配置成 302 跳转,把流量临时切换到一个静态通知页面,浏览器看到这个 302 就知道这只是暂时的情况,不会做缓存优化,第二天还会访问原来的地址。 -
304 Not Modified 是一个比较有意思的状态码,它用于 If-Modified-Since 等条件请求,表示资源未修改,用于缓存控制。它不具有通常的跳转含义,但可以理解成“重定向已到缓存的文件”(即“缓存重定向”)。
4xx
-
400 Bad Request 是一个通用的错误码,表示请求报文有错误,但也只是一个笼统的错误。
-
403 Forbidden 实际上不是客户端的请求出错,而是表示服务器禁止访问资源,原因可能多种多样,例如信息敏感、法律禁止等。
-
404 Not Found 可能是我们最常看见也是最不愿意看到的一个状态码,它的原意是资源在本服务器上未找到,所以无法提供给客户端。
-
405 Method Not Allowed:不允许使用某些方法操作资源,例如不允许 POST 只能 GET;
-
406 Not Acceptable:资源无法满足客户端请求的条件,例如请求中文但只有英文;
-
408 Request Timeout:请求超时,服务器等待了过长的时间;
-
409 Conflict:多个请求发生了冲突,可以理解为多线程并发时的竞态;
-
413 Request Entity Too Large:请求报文里的 body 太大;
-
414 Request-URI Too Long:请求行里的 URI 太大;
-
429 Too Many Requests:客户端发送了太多的请求,通常是由于服务器的限连策略;
-
431 Request Header Fields Too Large:请求头某个字段或总体太大;
5xx
-
500 Internal Server Error 与 400 类似,也是一个通用的错误码,服务器究竟发生了什么错误我们是不知道的。
-
501 Not Implemented 表示客户端请求的功能还不支持,
-
502 Bad Gateway 通常是服务器作为网关或者代理时返回的错误码,表示服务器自身工作正常,访问后端服务器时发生了错误,但具体的错误原因也是不知道的。
-
503 Service Unavailable 表示服务器当前很忙,暂时无法响应服务,我们上网时有时候遇到的“网络服务正忙,请稍后重试”的提示信息就是状态码 503。
1.5 HTTP 优点及缺点
优点
-
简单、灵活和易于扩展
HTTP 协议是很"简单"的,基本的报文格式是"header + body",头部信息也是简单的文本格式,挺容易看懂的。
HTTP 协议里的各个核心组成要素都没有被"写死",允许开发者任意定制、扩充和解释,如果你缺了什么功能,那你可以自己加上去。
-
应用广泛、环境成熟
HTTP 目前已经遍布世界的每一个角落,不管是台式机还是移动端,不管是浏览器还是各种app,都有它的身影。
HTTP 广泛应用的背后还有许多硬件设施的支持,各个公司纷纷购买服务器开办网站,让 HTTP 可以运行地更流畅。
-
无状态
无状态是一把"双刃剑"。
从单台服务器的角度来说,无状态相当于没有"记忆能力",所以就不需要额外的资源来记录状态信息,不仅实现上会简单一些,而且还能减轻服务器的负担;
从服务器集群的角度来说,无状态也表示每台服务器都是相同的,都可以处理请求,那么就可以轻松实现负载均衡和高并发。
-
明文
明文传输也是一把"双刃剑"。
对开发者友好:对比 TCP、UDP 这样的二进制协议,开发者可以直接在浏览器上用肉眼去查看,为我们的开发调试工作带来极大的便利。
缺点
-
无状态
对于服务器来说,无状态相当于没有"记忆能力",那么用户在购物的时候,从添加购物车、下单到付款,这一系列的操作,服务器都需要去跟用户验证身份才行,不仅麻烦,而且还造成很多不必要的数据传输。
-
明文
信息安全问题:因为是明文,所以黑客在数据传输链路上的某个环节截取到报文的话,就可以轻松查看报文信息,毫无隐私可言。
-
不安全
机密性:不能让别人知道你传输的是什么,"明文"就是这方面的一个缺点;
完整性:黑客可以修改报文里面的信息,但 HTTP 协议无法验证是否被修改;
身份认证:你可以发送请求,黑客也可以发送请求,那么怎么确认这个请求不是黑客发的呢?这个 HTTP 无法给出答案。
2 进阶篇
2.1 HTTP实体数据
-
数据类型表示实体数据的内容是什么,使用的是 MIME type,相关的头字段是 Accept 和 Content-Type;
-
数据编码表示实体数据的压缩方式,相关的头字段是 Accept-Encoding 和 Content-Encoding;
-
语言类型表示实体数据的自然语言,相关的头字段是 Accept-Language 和 Content-Language;
-
字符集表示实体数据的编码方式,相关的头字段是 Accept-Charset 和 Content-Type;
-
客户端需要在请求头里使用 Accept 等头字段与服务器进行“内容协商”,要求服务器返回最合适的数据;
-
Accept 等头字段可以用“,”顺序列出多个可能的选项,还可以用“;q=”参数来精确指定权重。
2.2 HTTP传输大文件的方法
数据压缩
请求报文:“Accept-Encoding”,值可以是 gzip、deflate、br 等
相应报文:“Content-Encoding”,值是服务器选择的压缩算法
缺点:通常只对文本文件有较好的压缩率,而图片、音频视频等本身就是已经高度压缩的,此法没什么卵用
分块传输
压缩是把大文件整体变小,我们可以反过来思考,如果大文件整体不能变小,那就把它“拆开”,分解成多个小块,把这些小块分批发给浏览器,浏览器收到后再组装复原。
响应报文:“Transfer-Encoding: chunked”
分块传输也可用于“流式数据”,例如由数据库动态生成的表单页面,这种情况下 body 数据的长度是未知的,无法在头字段“Content-Length”里给出确切的长度,所以也只能用 chunked 方式分块发送。
注意:“Transfer-Encoding: chunked”和“Content-Length”这两个字段是互斥的,一个是长度未知,一个是长度已知。
编码规则:
采用明文方式,类似响应头
-
每个分块包含两个部分,长度头和数据块;
-
长度头是以 CRLF(回车换行,即\r\n)结尾的一行明文,用 16 进制数字表示长度;
-
数据块紧跟在长度头后,最后也用 CRLF 结尾,但数据不包含 CRLF;
-
最后用一个长度为 0 的块表示结束,即“0\r\n\r\n”。
范围请求
“范围请求”允许客户端在请求头里使用专用字段来表示只获取文件的一部分,不仅看视频的拖拽进度需要范围请求,常用的下载工具里的多段下载、断点续传也是基于它实现的。
服务器:
“Accept-Ranges: bytes”:我支持范围请求
“Accept-Ranges: none”:我不支持范围请求
没有“Accept-Ranges”字段:我不支持范围请求
“范围请求”的请求头字段:**Range,**格式为 “bytes=x-y”,其中x和y是以字节为单位的数据范围,和字符串的位置类似。
比如: “0-9”表示前10个字节,“0-”表示从文档起点到文档终点,“-1”表示文档的最后一个字节。
服务器收到 Range 字段后,需要做四件事。
第一,它必须检查范围是否合法,比如文件只有 100 个字节,但请求“200-300”,这就是范围越界了。服务器就会返回状态码416,意思是“你的范围请求有误,我无法处理,请再检查一下”。
第二,如果范围正确,服务器就可以根据 Range 头计算偏移量,读取文件的片段了,返回状态码“206 Partial Content”,和 200 的意思差不多,但表示 body 只是原数据的一部分。
第三,服务器要添加一个响应头字段Content-Range,告诉片段的实际偏移量和资源的总大小,格式是“bytes x-y/length”,与 Range 头区别在没有“=”,范围后多了总长度。例如,对于“0-10”的范围请求,值就是“bytes 0-10/100”。
最后就是发送数据了,直接把片段用TCP发给客户端,一个范围请求就算是处理完了
多段数据
请求报文:Range头里支持使用多个"x-y",一次性获取多个片段数据
响应报文:“Content-Type: multipart/byteranges; boundary=xxx(分隔标记,示例:00000000001)”
多段数据结构如下:
2.3 HTTP的连接管理及队头阻塞
短连接
1. 理解
客户端每次发送请求前需要先与服务器建立连接,收到响应报文后会立即关闭连接。整个连接过程很短暂,所以就被称为“短连接”(short-lived connections)。
2. 缺点
每次的请求都会造成无谓的TCP连接建立和断开,导致传输效率很低。
长连接
1. 理解
针对短连接暴露出的缺点,HTTP协议提出了“长连接”的通信方式,也叫“持久连接”(persistent connections)、“连接保活”(keep alive)、“连接复用”(connection reuse)。
特点是:只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。
2. 好处
对服务器端:减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载。
对客户端:减少开销的那部分时间,使 HTTP 请求和响应能够更早地结束,这样 Web 页面的显示速度也就相应提高了。
连接相关的头字段
HTTP/1.1 中的连接都会默认启用长连接。
启用长连接:
请求报文:“Connection: keep-alive”,可有可无
相应报文:“Connection: keep-alive”,支持即有
关闭长连接:
客户端:请求报文加上"Connection: close",告诉服务器:“这次通信通信后就关闭连接”
服务器:通常不会主动关闭连接,但也可以使用一些策略。拿 Nginx 来举例,有 “keepalive_timeout” 和 “keepalive_requests” 两种方式。
队头阻塞
理解
HTTP 采用了“请求 - 应答”模式进行通信,而且 HTTP 规定了报文必须是“一发一收”,这就形成了一个先进先出的“串行”队列,如果队首的请求处理太慢,后面的请求就会一直处于等待状态。这就是著名的“队头阻塞”(Head-of-line blocking,也叫“队首阻塞”)了。
并发连接
同时对一个域名发起多个长连接,就好像蓄水池里多了几条管子用于排水,如果一根管子不幸堵了,那么其他管子还可以继续排水。
RFC2616里明确限制每个客户端最多并发2个连接,不过浏览器把这个上限提高到了6~8。后来修订的 RFC7230 也就取消了这个“2”的限制。
这是用数量来解决质量的问题,只不过它所提供的性能也跟不上互联网告诉发展所带来的需求。
域名分片
既然一个域名只能并发连接6~8个,那我索性就多开几个域名,比如 shard1.chrono.com、shard2.chrono.com,而这些域名都指向同一台服务器 www.chrono.com,这样实际长连接的数量就又上去了。这就是域名分片。
2.4 HTTP的Cookie机制
什么是 Cookie
Cookie 是服务器委浏览器存储在客户端里的一些数据,而这些数据通常都会记录用户的关键识别信息。
Cookie 的作用
身份识别
Cookie 最基本的一个用途就是 身份识别,保存用户的登录信息,实现会话事务。
比如你用账号密码登录了某宝,登录成功后服务器会给浏览器发一个Cookie,内容大概是"name=yourid",这样子你再次发送请求,浏览器就会自动把 Cookie 发给服务器,服务器就知道这个人是你,而不是张三,从而实现个性化服务。一方面是不用重复登录,另一方面也能记录你的浏览记录。
广告跟踪
Cookie 是怎么使用的
首先我们需要知道 Cookie 使用的两个字段:
响应头字段:Set-Cookie
请求头字段:Cookie
然后我们看看流程:
-
浏览器第一次访问服务器,服务器那肯定不知道他的身份,就创建了一个独特的身份标识数据,格式呢是 “key=value”,然后放到 Set-Cookie 里面,发送给浏览器(注意可以有多个 Set-Cookie);
-
这么一来一回之后,浏览器就能看到相应报文里面的 Set-Cookie,*知道这是服务器给的身份标识,于是呢就保存起来,*下次发送请求的时候把这个值放到 Cookie 字段里面发给服务器。
-
服务器看到第二次请求里面有 Cookie,就晓得这不是新人,*就可以拿出 Cookie 里的值,识别出其身份,*然后对其进行个性化服务。
流程具体如下图:
注意 Cookie 是由浏览器负责存储的,所以如果换了浏览器或者电脑,那新的浏览器里面没有对应的 Cookie,就需要再来一遍 Set-Cookie 流程了。
最后我们来了解一下常见的 Cookie 属性,也就是 Set-Cookie 里面除了 "key=value"外的一些手段,主要用于保护用户关键信息,防止外泄或窃取。先看看下图:
按手段的作用,属性分为以下几类:
-
设置 Cookie 的生命周期
(1)“Expires” 俗称 “过期时间”,用的是绝对时间,可以理解为 “截止日期”(deadline);
(2)“Max-Age” 用的是相对时间,单位是秒。浏览器用收到报文的时间点再加上 Max-Age,就可以得到失效的绝对时间。
这俩可以同时出现,但浏览器会优先采用 “Max-Age” 计算失效期。
-
设置 Cookie 的作用域
(1)“Domain” 指定 Cookie 所属的域名;
(2)“Path” 指定 Cookie 所属的路径。
浏览器在发送 Cookie 前会从 URI 中提取出 host 和 path 部分,对比 Cookie 的属性。如果不满足条件,就不会在请求头里发送 Cookie。
-
保证 Cookie 的安全性
(1)“HttpOnly” 会告诉浏览器,此 Cookie 只能通过浏览器 HTTP 协议传输,禁止其他方式访问,浏览器的 JS 引擎就会禁用 document.cookie 等一切相关的 API,“跨站脚本”(XSS)攻击也就无从谈起了。
(2)“SameSite” 可以防范“跨站请求伪造”(XSRF)攻击,设置成“SameSite=Strict”可以严格限定 Cookie 不能随着跳转链接跨站发送,而“SameSite=Lax”则略宽松一点,允许 GET/HEAD 等安全方法,但禁止 POST 跨站发送。
(3)“Secure” 表示这个 Cookie 仅能用 HTTPS 协议加密传输,明文的 HTTP 协议会禁止发送。但 Cookie 本身不是加密的,浏览器里还是以明文的形式存在。
2.5 HTTP 代理
理解
我们知道 HTTP 是基于 “请求 - 应答
” 模型的协议,一般由客户端发请求,由服务器响应。
实际上,在实际情况中,会有代理的存在。它呢,就是在客户端和服务器原本的通信链路中插入的一个中间环节,也是一台服务器,但提供的是 “代理服务
”。对于客户端来说,表现为服务器进行响应;对于源服务器来说,又表现为客户端发起请求,具有双重身份
。
作用
负载均衡
简单点理解,代理服务器收到客户端的请求后,可以决定把请求分发给众多源服务器中的哪一台。这样子一来,代理服务器可以把请求直接发给相对来说比较空闲的源服务器,这样子源服务器们就都有活干了,达到负载均衡的效果。
健康检查
使用“心跳”等机制监控后端服务器,发现有故障就及时“踢出”集群,保证服务高可用。
内容缓存
暂存、复用服务器响应。
相关头字段
Via
Via
用来标明代理的身份,是一个通用字段,在请求头或响应头里都可以出现。每当报文经过一个代理节点,代理服务器就会把自身的信息追加到字段的末尾,就像是经手人盖了一个章。如下图:
X-Forwarded-For
“X-Forwarded-For”的字面意思是“为谁而转发”,形式上和“Via”差不多,也是每经过一个代理节点就会在字段里追加一个信息。但“Via”追加的是代理主机名(或者域名),而“X-Forwarded-For”追加的是请求方的 IP 地址。所以,在字段里最左边的 IP 地址就客户端的地址。
X-Real-IP
“X-Real-IP”是另一种获取客户端真实 IP 的手段,它的作用很简单,就是记录客户端 IP 地址,没有中间的代理信息,相当于是“X-Forwarded-For”的简化版。如果客户端和源服务器之间只有一个代理,那么这两个字段的值就是相同的。
X-Forwarded-Host 和 X-Forwarded-Proto
它们的作用与“X-Real-IP”类似,只记录客户端的信息,分别是客户端请求的原始域名和原始协议名。
代理协议
对于代理服务器来说,通过 “X-Forwarded-For
” 操作代理信息,一个是必须要解析 HTTP 报文头,这对于代理来说成本比较高;另一个是 “X-Forwarded-For
” 等头必须要修改原始报文,而有些情况下是不允许甚至不可能的(比如使用 HTTPS 通信被加密)
所以出现了一个专门的 “代理协议
”,用来拿到准确的客户端信息。以下是 “代理协议” v1 版本的示例,它在 HTTP 报文前增加了一行 ASCII 码文本,相当于多了一个头。
// PROXY TCP4/TCP6 请求方地址 应答方地址 请求方端口号 应答方端口号 \r\n
PROXY TCP4 1.1.1.1 2.2.2.2 55555 80\r\n
GET / HTTP/1.1\r\n
Host: www.xxx.com\r\n
\r\n
2.6 HTTP 的缓存控制
2.6.1 缓存
缓存是什么
Web 缓存是可以自动保存常见文档副本的 HTTP 设备。当 Web 请求抵达缓存时,如果本地有“已缓存的”副本,就可以从本地存储设备而不是原始服务器中提取这个文档(来自HTTP权威指南,不过一般我都会把缓存直接理解成数据)
缓存的优点
-
缓存减少了冗余的数据传输,节省了你的网络费用。
-
缓存缓解了网络瓶颈的问题。不需要更多的带宽就能够更快地加载页面。
-
缓存降低了对原始服务器的要求。服务器可以更快地响应,避免过载的出现。
-
缓存降低了距离时延,因为从较远的地方加载页面会更慢一些。
以上也是从《HTTP权威指南》抄的,不过我总结起来也就是:
-
对网络来说:避免多次请求 - 应答的通信成本,节约网络带宽;
-
对源服务器来说:减轻了对源服务器的压力;
-
对客户端来说:网页的响应速度变得更快了。
缓存的缺点
- 缓存中的数据可能与服务器的数据不一致;
- 消耗内存。
2.6.2 浏览器缓存
浏览器缓存分为强缓存和协商缓存,当客户端请求某个资源时,获取缓存的流程如下
首先通过 Cache-Control
头字段验证强缓存是否可用
-
如果强缓存可用,直接使用;
-
否则进入协商缓存,即发送 HTTP 请求,服务器通过请求头中的
If-Modified-Since
或者If-None-Match
这些条件请求字段检查资源是否更新- 若资源更新,返回资源和200状态码
- 否则,返回304状态码,告知浏览器直接从缓存获取数据
强缓存相关头字段
Cache-Control: max-age=30; no_store/no_cache/must-revalidate
Cache-Control
头字段有以下几个属性:
-
max-age:HTTP 缓存控制最常用属性,标记资源的有效期,也就是"生存时间",时间的计算起点是响应报文的创建时刻,而不是客户端收到报文的时刻;
-
no_store:不允许缓存,用于某些变化非常频繁的数据,例如秒杀页面;
-
no_cache:这是一个容易引起误会的属性,它不是不可以缓存,实际上可以缓存,只不过需要在使用缓存之前要向服务器验证是否过期;
-
must-revalidate:这个可以根据语义来理解(revalidate,使重新生效),它的意思是如果缓存不过期就可以继续使用,但过期了如果还想用就必须去服务器验证。
协商缓存相关头字段
条件请求,或者说协商缓存,一共有 5 个头字段,我们最常用的是“If-Modified-Since”和“If-None-Match”这两个。需要第一次的响应报文预先提供“Last-modified”和“ETag”,然后第二次请求时就可以带上缓存里的原值,验证资源是否是最新的。
如果资源没有变,服务器就回应一个“304 Not Modified”,表示缓存依然有效,浏览器就可以更新一下有效期,然后放心大胆地使用缓存了。
“If-Modified-Since” 对应的是 “Last-modified”(文件的最后修改时间),其实根据语义就能明白大概意思
“If-None-Match” (Match 是匹配的意思)对应的是 “ETag”,这什么意思呢?ETag 是“实体标签”(Entity Tag)的缩写,是资源的一个唯一标识,主要是用来解决修改时间无法准确区分文件变化的问题。
比如,一个文件在一秒内修改了多次,但因为修改时间是秒级,所以这一秒内的新版本无法区分。
再比如,一个文件定期更新,但有时会是同样的内容,实际上没有变化,用修改时间就会误以为发生了变化,传送给浏览器就会浪费带宽。
使用 ETag 就可以精确地识别资源的变动情况,让浏览器能够更有效地利用缓存。
ETag 还有“强”“弱”之分。
强 ETag 要求资源在字节级别必须完全相符,弱 ETag 在值前有个“W/”标记,只要求资源在语义上没有变化,但内部可能会有部分发生了改变(例如 HTML 里的标签顺序调整,或者多了几个空格)。
3 安全篇
3.1 HTTPS
HTTPS
简单来说,HTTPS = HTTP + SSL/TLS
,也就是在 HTTP 下加入了 SSL/TLS 层,让 HTTP 运行在了安全
的 SSL/TLS
协议上,所以在安全性上面全靠 SSL/TLS
;
HTTPS 的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。
SSL/TCL
SSL/TLS 是信息安全领域中的权威标准,采用多种先进的加密技术保证通信安全;
SSL 即安全套接层(Secure Sockets Layer),在 OSI 模型中处于第 5 层(会话层);SSL 发展到 v3 版本时被正式标准化,并改名为 TLS(传输层安全,Transport Layer Security),版本号从 1.0 重新算起,所以 TLS1.0 = SSLv3.1。目前应用的最广泛的 TLS 是 1.2 版本。
OpenSSL 是著名的开源密码学工具包,是 SSL/TLS 的具体实现。
HTTPS 最大的特点和缺点
HTTP 最大的特点就是安全,在机密性、完整性、身份认证方面都有保障。
HTTP的缺点:
1、证书费用以及更新维护。
2、HTTPS 降低一定用户访问速度(实际上优化好就不是缺点了)。
3、HTTPS 消耗 CPU 资源,需要增加大量机器。
HTTP 和 HTTPS 的区别
-
https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
-
http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
-
http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
-
http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
3.2 HTTP 如何保证安全性
-
对称加密和非对称加密,以及两者结合起来的混合加密,实现了机密性。 目前 TLS 使用的就是混合加密。
-
实现完整性的手段主要是摘要算法(Digest Algorithm),也就是常说的散列函数、哈希函数(Hash Function)。
-
使用私钥再加上摘要算法,就能够实现“数字签名”,同时实现身份认证和不可否认
补充:
CA:
由于谁都可以发布公钥,所以还有“公钥的信任”问题。由此引入了**“第三方”CA**( Certificate Authority,证书认证机构 )。 它就像网络世界里的*局、教育部、公证中心,具有极高的可信度,由它来给各个公钥签名,用自身的信誉来保证公钥无法伪造,是可信的。
数字证书:
CA 对公钥的签名认证也是有格式的,不是简单地把公钥绑定在持有者身份上就完事了,还要包含序列号、用途、颁发者、有效时间等等,把这些打成一个包再签名,完整地证明公钥关联的各种信息,形成“数字证书”(Certificate)。
4 飞翔篇
4.1 HTTP/2
由于 HTTPS 通过引入 SSL/TLS 在安全上达到了"极致",但在性能提升方面做得不多,所以 HTTP/2 主要在性能优化上做了提升,和 HTTP/1 相比,主要有以下几点区别:
- 头部压缩:HTTP/2 使用“HPACK”算法压缩头部信息,消除冗余数据节约带宽;
- 二进制格式:HTTP/2 的消息不再是“Header+Body”的形式,而是分散为多个二进制“帧”;
- 数据流:HTTP/2 使用了数据流的概念,因为 HTTP/2 的数据包是不按顺序发送的,同一个连接里面连续的数据包,可能属于不同的请求。因此,必须要对数据包做标记,指出它属于哪个请求。HTTP/2 将每个请求或回应的所有数据包,称为一个数据流。每个数据流都有一个独一无二的编号。数据包发送时,都必须标记数据流 ID ,用来区分它属于哪个数据流。
-
多路复用: HTTP/2 实现了多路复用,HTTP/2 仍然复用 TCP 连接,但是在一个连接里,客户端和服务器都可以同时发送多个请求或回应,而且不用按照顺序一一发送,这样就避免了"队头堵塞"的问题。
- 服务器推送:服务器可以新建数据流主动向客户端发送消息。比如,在浏览器请求 HTML 的时候就提前把可能会用到的 CSS、JS 文件发送给浏览器,减少等待的时间,这就是"服务器推送"。
附:
键入网址按下回车,后面发生了什么?
简单版本:
-
浏览器从地址栏的输入中获得服务器的 IP 地址和端口号;
-
浏览器用 TCP 的三次握手与服务器建立连接;
-
浏览器向服务器发送拼好的请求报文;
-
服务器收到报文后处理请求,同样拼好响应报文再发给浏览器;
-
浏览器解析报文,渲染输出页面。
对URI的理解
先举个栗子
http://www.chrono.com:8080/11-1?uid=1234&name=mario&referer=xxx
-
URI 是用来唯一标记服务器上资源的一个字符串,通常也称为 URL;
-
URI 通常由 scheme、host:port、path 和 query 四个部分组成,有的可以省略;
-
scheme 叫“方案名”或者“协议名”,表示资源应该使用哪种协议来访问;
-
“host:port”表示资源所在的主机名和端口号;
-
path 标记资源所在的位置;
-
query 表示对资源附加的额外要求;
-
在 URI 里对“@&/”等特殊字符和汉字必须要做编码,否则服务器收到 HTTP 报文后会无法正确处理。
参考文献:
- 透视 HTTP 协议 – Chrono
- (建议精读)HTTP灵魂之问,巩固你的 HTTP 知识体系 – 神三元
- 你应该知道的https知识 – Winty
- 「2021」高频前端面试题汇总之计算机网络篇 – GUGGZ
- 前端面试指南
- 《HTTP权威指南》