在大话http(一)中,我们学习了http的基本内容,下面这一篇文章将带你更加深入的了解http。
http的实体数据
TCP、UDP 因为是传输层的协议,它们不会关心 body 数据是什么,只要把数据发送到对方就算是完成了任务。
数据类型与编码
但是HTTP 协议则不同,它是应用层的协议。他除了传送数据之外,还必须告诉对方这个数据类型是什么。所以使用多用途互联网邮件扩展(Multipurpose Internet Mail Extensions),简称为 MIME。
MIME 把数据分成了八大类,每个大类下再细分出多个子类,形式是“type/subtype”的字符串。
-
text:即文本格式的可读数据,我们最熟悉的应该就是 text/html 了,表示超文本文档,此外还有纯文本 text/plain、样式表 text/css 等。
-
image:即图像文件,有 image/gif、image/jpeg、image/png 等。
-
audio/video:音频和视频数据,例如 audio/mpeg、video/mp4 等。
-
application:数据格式不固定,可能是文本也可能是二进制,必须由上层应用程序来解释。常见的有 application/json,application/javascript、application/pdf 等,另外,如果实在是不知道数据是什么类型,像刚才说的“黑盒”,就会是 application/octet-stream,即不透明的二进制数据。
但仅有 MIME type 还不够,因为 HTTP 在传输时为了节约带宽,有时候还会压缩数据,为了不要让浏览器继续“猜”,还需要有一个“Encoding type”,告诉数据是用的什么编码格式,这样对方才能正确解压缩,还原出原始的数据。
-
gzip:GNU zip 压缩格式,也是互联网上最流行的压缩格式;
-
deflate:zlib(deflate)压缩格式,流行程度仅次于 gzip;
-
br:一种专门为 HTTP 优化的新压缩算法(Brotli)。
对应的头部字段
HTTP 协议为此定义了两个 Accept 请求头字段和两个 Content 实体头字段,用于客户端和服务器进行“内容协商”。也就是说,客户端用 Accept 头告诉服务器希望接收什么样的数据,而服务器用 Content 头告诉客户端实际发送了什么样的数据。
-
Accept 字段标记的是客户端可理解的 MIME type,可以用“,”做分隔符列出多个类型,让服务器有更多的选择余地。
-
服务器会在响应报文里用头字段 Content-Type 告诉实体数据的真实类型。并且如果需要发送请求体,那么也需要指定Content-Type字段。
-
Accept-Encoding 字段标记的是客户端支持的压缩格式,例如上面说的 gzip、deflate 等,同样也可以用“,”列出多个。
-
服务器可以选择其中一种来压缩数据,实际使用的压缩格式放在响应头字段 Content-Encoding 里。 Accept-encoding, Content-Encoding不是必须的。不指定表示不支持压缩数据,没有进行压缩。
内容协商机制
有了上面的mine encoding type,我们可以让浏览器知道如何解析数据。有时候,我们需要根据用户返回不同语言版本的网页。
内容协商方式
服务器驱动(常用)
近似匹配(通过q权重)
内容协商对应的头部字段
-
Accept-Language 字段标记了客户端可理解的自然语言。
-
服务器应该在响应报文里用头字段 Content-Language 告诉客户端实体数据使用的实际语言类型。
-
字符集在 HTTP 里使用的请求头字段是 Accept-Charset,但响应头里却没有对应的 Content-Charset,而是在 Content-Type 字段的数据类型后面用“charset=xxx”来表示,这点需要特别注意。 在http头部字段中。
,
表示每一个字段值的分割,而;
表示该字段值的分割。
Accept: text/html,application/xml;q=0.9,*/*;q=0.8
内容协商的过程是不透明的,每个 Web 服务器使用的算法都不一样。但有的时候,服务器会在响应头里多加一个 Vary 字段,记录服务器在内容协商时参考的请求头字段,给出一点信息。
http大文件传输方式
数据压缩
我们知道,http请求报文中可以指定accept-encoding字段来指定可以接收的压缩方式。然后服务器选择一种支持的压缩方式,并在响应报文中指定content-encoding字段,将元数据压缩后返回给浏览器。
gzip 等压缩算法通常只对文本文件有较好的压缩率,而图片、音频视频等多媒体数据本身就已经是高度压缩的,再用 gzip 处理也不会变小(甚至还有可能会增大一点),所以它就失效了。
分块传输
压缩是把大文件整体变小,我们可以反过来思考,如果大文件整体不能变小,那就把它“拆开”,分解成多个小块,把这些小块分批发给浏览器,浏览器收到后再组装复原。
这种“化整为零”的思路在 HTTP 协议里就是“chunked”分块传输编码,在响应报文里用头字段 Transfer-Encoding: chunked 来表示,意思是报文里的 body 部分不是一次性发过来的,而是分成了许多的块(chunk)逐个发送。
分块传输可以流式收发数据,节约内存和带宽,使用响应头字段“Transfer-Encoding: chunked”来表示,分块的格式是 16 进制长度头 + 数据块。
Transfer-Encoding: chunked
和 Content-Length
这两个字段是互斥的, 也就是说响应报文里这两个字段不能同时出现,一个响应报文的传输要么是长度已知,要么是长度未知(chunked)。
下面我们来看一下分块传输的编码规则,其实也很简单,同样采用了明文的方式,很类似响应头。
- 每个分块包含两个部分,长度头和数据块;
- 长度头是以 CRLF(回车换行,即\r\n)结尾的一行明文,用 16 进制数字表示长度;
- 数据块紧跟在长度头后,最后也用 CRLF 结尾,但数据不包含 CRLF;
- 最后用一个长度为 0 的块表示结束,即“0\r\n\r\n”。
服务器响应报文中的头部字段默认就是
Transfer-Encoding: chunked
。
范围请求
比如,你在看当下正热播的某穿越剧,想跳过片头,直接看正片,或者有段剧情很无聊,想拖动进度条快进几分钟,这实际上是想获取一个大文件其中的片段数据,而分块传输并没有这个能力。所以就需要范围请求了。
有了范围请求之后,HTTP 处理大文件就更加轻松了,看视频时可以根据时间点计算出文件的 Range,不用下载整个文件,直接精确获取片段所在的数据内容。
范围请求不是 Web 服务器必备的功能,可以实现也可以不实现,所以服务器必须在响应头里使用字段Accept-Ranges: bytes明确告知客户端:“我是支持范围请求的”。
请求头 Range 是 HTTP 范围请求的专用字段,格式是bytes=x-y, 其中的 x 和 y 是以字节为单位的数据范围。格式非常灵活。x, y也可以省略。下面以100个字节的文件举例:
- “0-”表示从文档起点到文档终点,相当于“0-99”,即整个文件;
- “10-”是从第 10 个字节开始到文档末尾,相当于“10-99”;
- “-1”是文档的最后一个字节,相当于“99-99”;
- “-10”是从文档末尾倒数 10 个字节,相当于“90-99”。 服务器收到 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 发给客户端,一个范围请求就算是处理完了。
断点续传的实现要点
- 先发个HEAD请求,看服务器是否支持范围请求,同时获取文件的大小。主要是查看响应头中是否有
accept-ranges: bytes
。 - 开 N 个线程,每个线程使用 Range 字段划分出各自负责下载的片段,发请求传输数据;
- 下载意外中断也不怕,不必重头再来一遍,只要根据上次的下载记录,用 Range 请求剩下的那一部分就可以了。
多段数据
我们再一次http请求中,也可以指定range的多个字段。表示请求多个数据片段。
这种情况需要使用一种特殊的 MIME 类型:multipart/byteranges,表示报文的 body 是由多段字节序列组成的,并且还要用一个参数“boundary=xxx”给出段之间的分隔标记。
每一个分段必须以“- -boundary”开始(前面加两个“-”),之后要用“Content-Type”和“Content-Range”标记这段数据的类型和所在范围。
HTTP/1.1 206 Partial Content
Content-Type: multipart/byteranges; boundary=00000000001
Content-Length: 189
Connection: keep-alive
Accept-Ranges: bytes
Content-Type: text/plain
Content-Range: bytes 0-9/96
http长链接和短连接
短连接的缺点相当严重,因为在 TCP 协议里,建立连接和关闭连接都是非常“昂贵”的操作。TCP 建立连接要有“三次握手”,发送 3 个数据包,需要 1 个 RTT;关闭连接是“四次挥手”,4 个数据包需要 2 个 RTT。
在http启动长链接时。TCP 连接长时间不关闭,服务器必须在内存里保存它的状态,这就占用了服务器的资源。所以,长连接也需要在恰当的时间关闭,不能永远保持与服务器的连接,这在客户端或者服务器都可以做到。
- 客户端,可以再请求头中加上
Connection: close
。 - 服务器端通常不会主动关闭连接,但也可以使用一些策略。
- 设置长连接的超时时间,如果在一段时间内连接上没有任何数据收发就主动断开连接,避免空闲连接占用系统资源。
- 设置长连接上可发送的最大请求次数。当在这次长链接中请求次数达到了,将主动关闭连接。
http的重定向
Location字段属于响应字段,必须出现在响应报文里。但只有配合 301/302 状态码才有意义,它标记了服务器要求重定向的 URI。
浏览器收到 301/302 报文,会检查响应头里有没有Location。如果有,就从字段值里提取出 URI,发出新的 HTTP 请求,相当于自动替我们点击了这个链接。
在Location里的 URI 既可以使用绝对 URI,也可以使用相对 URI。相对URI是省略scheme, host:port,只有path, query部分。但是可以相对于当前上下文中找到对应的scheme, host,port。
注意,在重定向时如果只是在站内跳转,你可以放心地使用相对 URI。但如果要跳转到站外,就必须用绝对 URI。
301/302 是最常用的重定向状态码,在 3××里剩下的几个还有:
- 303 See Other:类似 302,但要求重定向后的请求改为 GET 方法,访问一个结果页面,避免 POST/PUT 重复操作;
- 307 Temporary Redirect:类似 302,但重定向后请求里的方法和实体不允许变动,含义比 302 更明确;
- 308 Permanent Redirect:类似 307,不允许重定向后的请求变动,但它是 301“永久重定向”的含义。
重定向的选择
301 的含义是“永久”的。如果域名、服务器、网站架构发生了大幅度的改变,比如启用了新域名、服务器切换到了新机房、网站目录层次重构,这些都算是“永久性”的改变。原来的 URI 已经不能用了,必须用 301“永久重定向”,通知浏览器和搜索引擎更新到新地址,这也是搜索引擎优化(SEO)要考虑的因素之一。
302 的含义是“临时”的。原来的 URI 在将来的某个时间点还会恢复正常,常见的应用场景就是系统维护,把网站重定向到一个通知页面,告诉用户过一会儿再来访问。另一种用法就是“服务降级”,比如在双十一促销的时候,把订单查询、领积分等不重要的功能入口暂时关闭,保证核心服务能够正常运行。
重定向需要注意的问题
- 性能损耗。重定向的机制决定了一个跳转会有两次请求 - 应答,比正常的访问多了一次。 虽然 301/302 报文很小,但大量的跳转对服务器的影响也是不可忽视的。站内重定向还好说,可以长连接复用,站外重定向就要开两个连接,如果网络连接质量差,那成本可就高多了,会严重影响用户的体验。
- 循环跳转。如果重定向的策略设置欠考虑,可能会出现“A=>B=>C=>A”的无限循环。
HTTP状态管理
HTTP 是“无状态”的,这既是优点也是缺点。优点是服务器没有状态差异,可以很容易地组成集群,而缺点就是无法支持需要记录状态的事务操作。
http是无状态的,无记忆的。所以说每个请求都是独立的。如果其他请求处理需要以前请求的信息,那么就需要重新发送请求。导致每次连接传送的数据量增大。所以就需要cookie,session来保存http连接状态。
Cookie 就是服务器委托浏览器存储在客户端里的一些数据,而这些数据通常都会记录用户的关键识别信息。
cookie是如何进行传递的呢?就需要使用响应头字段 Set-Cookie 和请求头字段 Cookie。
用户第一次访问服务器时,服务器时不知道他的身份的,所以就创建一个key=value格式的文本。然后放在set-cookie字段中,返回给浏览器。浏览器看到set-cookie,就知道服务器给了身份标识。然后就将其保存到本地。下次再想服务器发送请求时,就把这些信息放在cookie字段中一同发送。
cookie的属性
Cookie 就是服务器委托浏览器存储在客户端里的一些数据,而这些数据通常都会记录用户的关键识别信息。
过期时间
- max-age: 相对时间,单位是秒。浏览器用收到报文的时间点再加上 Max-Age,就可以得到失效的绝对时间。 这个优先级较高。
- expires: 用的是绝对时间点,可以理解为截止日期。JMP格式。 如果不指定这两个字段,那么他们将是会话级别的。浏览器关闭,cookie就失效了。
作用域
让浏览器仅发送给特定的服务器和 URI,避免被其他网站盗用。
浏览器在发送 Cookie 前会从 URI 中提取出 host 和 path 部分,对比 Cookie 的属性。如果不满足条件,就不会在请求头里发送 Cookie。
- domain:表示那些域名可以被发送cookie。
- path:表示该域名下的那些路径可以发送cookie。如果为
/
则度可以接收到cookie。
安全性
在 JS 脚本里可以用 document.cookie 来读写 Cookie 数据,这就带来了安全隐患,有可能会导致“跨站脚本”(XSS)攻击窃取数据。
-
httpOnly:表示cookie只能通过浏览器 HTTP 协议传输,禁止其他方式访问。
-
SameSite 可以防范“跨站请求伪造”(XSRF)攻击,设置成“SameSite=Strict”可以严格限定 Cookie 不能随着跳转链接跨站发送,而“SameSite=Lax”则略宽松一点,允许 GET/HEAD 等安全方法,但禁止 POST 跨站发送。 这里你可能会有这种疑问。他这个是下面这种图片的意思。是你点击了超链接跳转的,而不是主动访问。然后就限制cookie发送给该服务器。
-
secure:表示这个 Cookie 仅能用 HTTPS 协议加密传输,明文的 HTTP 协议会禁止发送。但 Cookie 本身不是加密的,浏览器里还是以明文的形式存在。
cookie的用处
- 身份标识。保存用户的登陆状态。
- 广告跟踪。就是你在这个网页访问一个广告,那么该广告的服务器就下发了一个cookie,然后你再去别的页面,好巧,也有这个服务器的无广告,在请求这个广告的时候,就把以前的cookie放松到该广告的服务器。那么将推送很多的广告给你。
session
如何传递sessionId呢?
- cookie。当再次发送请求时,会扫描符合条件的cookie,并自动携带到服务端。 有些浏览器禁用了cookie。所以我们就可以通过
- url重写。将sessionId拼接到url后面进行传递。
- 隐藏表单。表单提交时,增加一个字段,将sessionId加入。
session过期时间
cookie和session的比较
http缓存
缓存应该只缓存更新频率不高的静态资源(css, js, image等资源)。
几种缓存字段
expires:值是一个JMP格式的时间。
max-age 是“生存时间”(又叫“新鲜度”“缓存寿命”,类似 TTL,Time-To-Live),时间的计算起点是响应报文的创建时刻(即 Date 字段,也就是离开服务器的时刻),而不是客户端收到报文的时刻,也就是说包含了在链路传输过程中所有节点所停留的时间。
no-cache。是把资源进行了本地缓存,在浏览器使用缓存之前,会使用last-Modified和Etag往返浏览器进行对比,判断时间和唯一标识符和服务器的是否一致,一致的话304使用缓存,不一致的话请求服务器。
其实不止服务器可以发“Cache-Control”头,浏览器也可以发“Cache-Control”,也就是说请求 - 应答的双方都可以用这个字段进行缓存控制,互相协商缓存的使用策略。
当你点“刷新”按钮的时候,浏览器会在请求头里加一个“Cache-Control: max-age=0”。Ctrl+F5 的“强制刷新”,其实是发了一个“Cache-Control: no-cache”,含义和“max-age=0”基本一样,就看后台的服务器怎么理解,通常两者的效果是相同的。但只会在请求网页HTML时添加,HTML解析渲染时遇到的图片等资源还是会使用本地缓存资源。
那何时才使用缓存的资源呢?
在“前进”“后退”“跳转”这些重定向动作中浏览器不会“夹带私货”,只用最基本的请求头,没有“Cache-Control”,所以就会检查缓存,直接利用之前的资源,不再进行网络通信。
Etag也有强弱之分。
- 强 ETag 要求资源在字节级别必须完全相符。
- 弱 ETag 在值前有个“W/”标记,只要求资源在语义上没有变化,但内部可能会有部分发生了改变(例如 HTML 里的标签顺序调整,或者多了几个空格)。
缓存的工作原理
但是如果cache-control和expires未到过期时间,那么将不会发送请求。可能资源已经更新,那么这时候将如何发送请求获取更新的资源呢?
浏览器操作对于缓存的影响
http协议之身份认证
- BASIC认证(基本认证)
明文传输(base64编码),不安全。
- DIGEST认证(摘要认证)
可能会被伪装。
- SSL 客户端认证
- FormBase 认证(基于表单认证)
http中介之代理
代理最基本的一个功能是负载均衡。 因为在面向客户端时屏蔽了源服务器,客户端看到的只是代理服务器,源服务器究竟有多少台、是哪些 IP 地址都不知道。于是代理服务器就可以掌握请求分发的“大权”,决定由后面的哪台服务器来响应请求。
代理服务器需要用字段“Via”标明代理的身份。Via 是一个通用字段,请求头或响应头里都可以出现。 每当报文经过一个代理节点,代理服务器就会把自身的信息追加到字段的末尾。
Via 字段只解决了客户端和源服务器判断是否存在代理的问题,还不能知道对方的真实信息。
通常客户端不应该知道服务器的ip地址。但是服务器需要知道客户端真实的ip地址,方便做访问控制、用户画像、统计分析。
X-Forwarded-For的字面意思是“为谁而转发”,形式上和“Via”差不多,也是每经过一个代理节点就会在字段里追加一个信息。但 “Via”追加的是代理主机名(或者域名),而“X-Forwarded-For”追加的是请求方的 IP 地址。 所以,在字段里最左边的 IP 地址就是客户端的地址。
X-Real-IP是另一种获取客户端真实 IP 的手段,它的作用很简单,就是记录客户端 IP 地址,没有中间的代理信息。直接保存的就是客户端的ip地址。
也可以通过专门的代理协议在不改变原始报文的情况下传递客户端的真实ip地址。
http中介之网关
扮演着协议转换器角色。
一些思考题
- 试着解释一下这个请求头“Accept-Encoding: gzip, deflate;q=1.0, *;q=0.5, br;q=0”,再模拟一下服务器的响应头。 我这个请求最希望服务器给我返回的编码方式是gzip和deflate,他们俩在我这是最优的地位,我不接受br的编码方式,如果还有其他的编码方式的话对我来说权重0.5。
服务器可能的响应头是
HTTP/1.1 200 OK
Content-Encoding: gzip
- 假设你要使用 POST 方法向服务器提交一些 JSON 格式的数据,里面包含有中文,请求头应该是什么样子的呢? 请求头可能是
POST /serv/v1/user/auth HTTP/1.1
Content-Type: application/json
Accept-Language: zh-CN, zh
Accept-Charset: gbk, utf-8
Content-language: zh-cn, zh;
但是我经过测试发现,即使传入的数据有中文,并且指定的content-language为en,服务端也能正确的编码获取body。但是服务端最好在解析body是给出encoding(charset)。
以nodejs为例。默认情况下,没有分配编码,流数据将作为 Buffer
对象返回。 设置编码会导致流数据作为指定编码的字符串而不是 Buffer
对象返回。
- 试着用快递发货收货比喻一下 MIME、Encoding 等概念。 MIME类比快递的话就是你要快递的物品(衣服,食物等),Encoding就是快递你这个物品的包装方式,如果是衣服可能就随意一点一个袋子,如果是食物担心腐烂给你放个冰袋进去 不知道回答的对不对,请老师指正
- 分块传输数据的时候,如果数据里含有回车换行(\r\n)是否会影响分块的处理呢? 分块传输中数据里含有回车换行(\r\n)不影响分块处理,因为分块前有数据长度说明。
- 如果对一个被 gzip 的文件执行范围请求,比如“Range: bytes=10-19”,那么这个范围是应用于原文件还是压缩后的文件呢? range是针对原文件的。
- 在开发基于 HTTP 协议的客户端时应该如何选择使用的连接模式呢?短连接还是长连接? 根据请求的频繁程度来选择连接模式。一次性的请求用短链接,频繁与服务端交互的用长连接。
- 应当如何降低长连接对服务器的负面影响呢? 长连接会长期占用服务器资源,根据服务器性能设置连接数和长连接超时时间,保证服务器TCP资源使用处于正常范围。
- 外部重定向和内部重定向的区别? 外部重定向,服务器会把重定向的地址给浏览器,然后浏览器再次的发起请求,地址栏的地址变化了。
内部重定向,服务器会直接把重定向的资源返给浏览器,不需要再次在浏览器发起请求,地址栏的地址不变。
- 如果 Cookie 的 Max-Age 属性设置为 0,会有什么效果呢? max-age=0是指不能缓存,但在会话期间是可用的,浏览器会话关闭之前可以用cookie记录用户的信息。但是经过测试,他确实不会保存在浏览器上啊。并且再次发送请求,也不会携带cookie。
- 你觉得cookie有什么缺点呢? (1) 不安全。如果被中间人获取到 Cookie,完全将它作为用户凭证冒充用户。解决方案是使用 https 进行加密。 (2)有数量和大小限制。另外 Cookie 太大也不好,传输的数据会变大。 (3)客户端可能不会保存 Cookie。比如用 telnet 收发数据,用户禁用浏览器 Cookie 保存功能的情况。
- Cache 和 Cookie 都是服务器发给客户端并存储的数据,你能比较一下两者的异同吗?
- Cookie 会随请求报文发送到服务器,而 Cache 不会,但可能会携带 if-Modified-Since(保存资源的最后修改时间)和 If-None-Match(保存资源唯一标识) 字段来验证资源是否过期。
- Cookie 在浏览器可以通过脚本获取(如果 cookie 没有设置 HttpOnly),Cache 则无法在浏览器中获取(出于安全原因)。
- Cookie 通过响应报文的 Set-Cookie 字段获得,Cache 缓存的是完整的报文。
- 用途不同。Cookie 常用于身份识别,Cache 则是由浏览器管理,用于节省带宽和加快响应速度。
- Cookie 的 max-age 是从浏览器拿到响应报文时开始计算的,而 Cache 的 max-age 是从响应报文的生成时间(Date 头字段)开始计算。
- 即使有“Last-modified”和“ETag”,强制刷新(Ctrl+F5)也能够从服务器获取最新数据(返回 200 而不是 304)。 强制刷新是因为请求头里的 If-Modified-Since 和 If-None-Match 会被清空所以会返回最新数据。
- http缓存详答? 浏览器拿到一个网址的时候,先判断是否允许缓存,允许会先查看本地缓存:1.有缓存并在缓存可用期那直接拿来用。2.缓存不存在或者不可用 那需要请求。 浏览器拿到host,判断:1.ip+port 那直接请求对应的服务器 2.域名 那开展一系列的dns递归查询:先拿dns缓存,没有缓存->本地dns服务器->根dns服务器->顶级dns服务器->权威dns服务器->GSLB,查到ip返回最优ip组实现负载均衡,浏览器随机或者轮询取一个ip开始它的http请求之旅。 浏览器判断该网页是否允许缓存,然后添加Cache-Control的各种字段no-store是否允许缓存/no-cache缓存必须进行验证/noly-if-cached只接受代理的缓存等,max-age最大生存时间 max-stale 短时间过期可用 min-fresh 最短有效时间等。If-Modified-Since/if-None-Match/Last-modified/ETag等字段用于判断服务端是否有更新。然后将请求发给代理服务器。请求代理服务器,如果是第一次,要经历浏览器和代理服务器的3次tcp握手进行连接,连接成功,发送http请求。 代理服务器拿到请求,首先查看是否允许缓存,允许那就查看自己本地缓存有没有,通过查看max-age/max-stale/min-fresh等信息判断是否过期,没有过期直接拿来用,将数据返回给客户端。如果过期了,代理服务器将用客户端的请求,再次像真实服务器进行请求。如果也是第一次连接,需要经历代理服务器和真实服务器的3次tcp握手,连接成功,发送请求。 真实服务器收到请求之后,通过if-Modified-Since/Last-Modified/if-None-Match/ETag等字段判断是否有更新,没有更新,直接返回304。如果有更新,则将数据打包http response 返回。返回头字段会添加Cache-Control字段,用来判断缓存的控制策略以及生存周期,no-store不允许缓存/no-cache使用缓存必须先验证/must-revalidate缓存不过期可用过期必须重新请求验证/proxy-revalidate缓存过期只要求代理进行请求验证 private不能在代理层保存只能在客户端保存/public缓存完全开放 s-maxage缓存在代理上可以缓存的时间 no-transform不允许代理对缓存做任何的改动。然后根据业务需求判断该地址是不是需要重定向,如果需要是短期的重定向还是永久的重定向,按需将状态码修改为301或者302。最后真实服务器将数据打包成http相应 回给代理服务器。 代理服务器收到真实服务器的回应数据,首先会查看Cache-Control里的字段,是否允许它进行缓存,如果是private,代理服务器不进行缓存,直接返回给客户端。public则根据s-maxage/no-transform进行缓存,如果可以优化并且代理服务器需要优化,那可能会先优化数据,否则同时将数据回发给客户端。 客户端收到数据,如果是304,则直接拿缓存数据进行渲染,并修改相关缓存变量,比如时间,以及缓存使用策略。如果收到了301或者302,那么客户端会再次发起新的url请求,进行跳转到最终的页面。 最后,底层tcp 经过4次挥手,完成关闭连接。