10 | 应该如何理解请求方法?

  • Q:有哪些请求方法
      1. GET:获取资源,可以理解为读取或者下载数据
      1. HEAD:获取资源的元信息
      1. POST:向资源提交数据,相当于写入或上传数据
      1. PUT:类似 POST
      1. DELETE:删除资源
      1. CONNECT:建立特殊的连接隧道
      1. OPTIONS:列出可对资源实行的方法
      1. TRACE:追踪请求 - 响应的传输路径

12 | 响应状态码该怎么用?

  • Q:有哪些状态码
    • 1××:提示信息,表示目前是协议处理的中间状态,还需要后续的操作
      • 101 Switching Protocols:客户端使用 Upgrade 头字段,要求在 HTTP 协议的基础上改成其他的协议继续通信,比如 WebSocket。而如果服务器也同意变更协议,就会发送状态码 101,但这之后的数据传输就不会再使用 HTTP 了
    • 2××:成功,报文已经收到并被正确处理
      • 200 OK:最常见的成功状态码,表示一切正常,服务器如客户端所期望的那样返回了处理结果,如果是非 HEAD 请求,通常在响应头后都会有 body 数据
      • 204 No Content:是另一个很常见的成功状态码,它的含义与“200 OK”基本相同,但响应头后没有 body 数据。所以对于 Web 服务器来说,正确地区分 200 和 204 是很必要的
      • 206 Partial Content:是 HTTP 分块下载或断点续传的基础,在客户端发送“范围请求”、要求获取资源的部分数据时出现,它与 200 一样,也是服务器成功处理了请求,但 body 里的数据不是资源的全部,而是其中的一部分。状态码 206 通常还会伴随着头字段“Content-Range”, 表示响应报文里 body 数据的具体范围,供客户端确认,例如“Content-Range: bytes 0-99/2000”,意思是此次获 取的是总计 2000 个字节的前 100 个字节
    • 3××:重定向,资源位置发生变动,需要客户端重新发送请求
      • 301 Moved Permanently:即「永久重定向」,含义是此次请求的资源已经不存在了,需要改用新的 URI 再次访问
      • 302 Found:曾经的描述短语是“Moved Temporarily”,俗称“临时重定向”,意思是请求的资源还在,但需要暂时用另一个 URI 来访问
      • 301 和 302 都会在响应头里使用字段Location指明后续要 跳转的 URI,最终的效果很相似,浏览器都会重定向到新的 URI。两者的根本区别在于语义,一个是“永久”,一个 是“临时”,所以在场景、用法上差距很大
      • 比如,你的网站升级到了 HTTPS,原来的 HTTP 不打算用了,这就是“永久”的,所以要配置 301 跳转,把所有的 HTTP 流量都切换到 HTTPS
      • 再比如,今天夜里网站后台要系统维护,服务暂时不可用, 这就属于“临时”的,可以配置成 302 跳转,把流量临时切换到一个静态通知页面,浏览器看到这个 302 就知道这只是暂时的情况,不会做缓存优化,第二天还会访问原来的地址
      • 304 Not Modified:是一个比较有意思的状态码,它用 于 If-Modified-Since 等条件请求,表示资源未修改,用于缓存控制。它不具有通常的跳转含义,但可以理解成“重定向已到缓存的文件”(即“缓存重定向”)
    • 4××:客户端错误,请求报文有误,服务器无法处理
      • 400 Bad Request:是一个通用的错误码,表示请求报文有错误,但具体是数据格式错误、缺少请求头还是 URI 超长它没有明确说,只是一个笼统的错误,客户端看到 400 只会是“一头雾水”“不知所措”。所以,在开发 Web 应用时应当尽量避免给客户端返回 400,而是要用其他更有明确含义的状态码
      • 403 Forbidden:实际上不是客户端的请求出错,而是表示服务器禁止访问资源。原因可能多种多样,例如信息敏感、法律禁止等,如果服务器友好一点,可以在 body 里详细说明拒绝请求的原因,不过现实中通常都是直接给一个「闭门羹」
      • 404 Not Found:可能是我们最常看见也是最不愿意看到的一个状态码,它的原意是资源在本服务器上未找到,所 以无法提供给客户端。但现在已经被“用滥了”,只要服务器“不高兴”就可以给出个 404,而我们也无从得知后面到底是真的未找到,还是有什么别的原因,某种程度上它比 403 还要令人讨厌
      • 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:请求头某个字段或总体太大
    • 5××:服务器错误,服务器在处理请求时内部发生了错误
      • 500 Internal Server Error:与 400 类似,也是一个通用的错误码,服务器究竟发生了什么错误我们是不知道的。不过对于服务器来说这应该算是好事,通常不应该把服务器内部的详细信息,例如出错的函数调用栈告诉外界。虽然不利于调试,但能够防止黑客的窥探或者分析
      • 501 Not Implemented:表示客户端请求的功能还不支持,这个错误码比 500 要“温和”一些,和「即将开业, 敬请期待」的意思差不多,不过具体什么时候“开业”就不好说了
      • 502 Bad Gateway:通常是服务器作为网关或者代理时返回的错误码,表示服务器自身工作正常,访问后端服务器时发生了错误,但具体的错误原因也是不知道的
      • 503 Service Unavailable:表示服务器当前很忙,暂时无法响应服务,我们上网时有时候遇到的“网络服务正忙,请稍后重试”的提示信息就是状态码 503。503 是一个“临时”的状态,很可能过几秒钟后服务器就不那么忙了,可以继续提供服务,所以 503 响应报文里通常还会有一个「Retry-After」字段,指示客户端可以在多久以后再次尝试发送请求

15 | 海纳百川:HTTP的实体数据

  • Q:在 HTTP 里经常遇到的数据类型「MIME type」
    • 1.text:即文本格式的可读数据,最熟悉的应该就是 text/html 了,表示超文本文档,此外还有纯文本 text/plain、样式表 text/css 等
    • 2.image:即图像文件,有 image/gif、image/jpeg、image/png 等
    • 3.audio/video:音频和视频数据,例如 audio/mpeg、video/mp4 等
    • 4.application:数据格式不固定,可能是文本也可能是二进制,必须由上层应用程序来解释。常见的有 application/json,application/javascript、application/pdf 等,另外,如果实在是不知道数据是什么类型,像刚才说的“黑盒”,就会是 application/octet-stream,即不透明的二进制数据
  • Q:HTTP压缩数据的编码格式「Encoding type」有哪些
    • 1.gzip:GNU zip 压缩格式,也是互联网上最流行的压缩格式
    • 2.deflate:zlib(deflate)压缩格式,流行程度仅次于 gzip
    • 3.br:一种专门为 HTTP 优化的新压缩算法(Brotli)
  • Q:HTTP 协议定义的客户端和服务端进行内容协商的字段是什么
    • 客户端用 Accept 头告诉服务器希望接收什么样的数据,如Accept: text/html,application/xml,image/webp,image/png
    • 服务器用 Content 头告诉客户端实际发送了什么样的数据,如Content-Type: text/html
    • Accept-Encoding字段标记的是客户端支持的压缩格式,如Accept-Encoding: gzip, deflate, br
    • 服务器可以选择客户端支持的压缩格式来压缩数据,实际使用的压缩格式放在响应头字段Content-Encoding里,如Content-Encoding: gzip
    • 如果请求报文里没有 Accept-Encoding 字段,就表示客户端不支持压缩数据;如果响应报文里没有 Content-Encoding 字段,就表示响应数据没有被压缩
  • Q:HTTP 协议对语言与编码协商的字段是什么
    • Accept-Language字段标记了客户端可理解的自然语言,也允许用「,」做分隔符列出多个类型,如Accept-Language: zh-CN, zh, en
    • 服务器应该在响应报文里用头字段Content-Language告诉客户端实体数据使用的实际语言类型,如Content-Language: zh-CN
    • 字符集在 HTTP 里使用的请求头字段是Accept-Charset,如浏览器请求 GBK 或 UTF-8 的字符集,即Accept-Charset: gbk, utf-8
    • 字符集的响应头是在Content-Type字段的数据类型后面用“charset=xxx”来表示,如服务器返回的是 UTF-8 编码,即Content-Type: text/html; charset=utf-8
    • 不过现在的浏览器都支持多种字符集,通常不会发送 Accept-Charset,而服务器也不会发送 Content-Language,因为使用的语言完全可以由字符集推断出来,所以在请求头里一般只会有 Accept-Language 字段,响应头里只会有 Content-Type 字段
  • Q:内容协商的质量值是什么
    • 在 HTTP 协议里用 Accept、Accept-Encoding、Accept-Language 等请求头字段进行内容协商的时候,还可用「q」参数(即quality factor)表示权重来设定优先级
    • 权重的最大值是 1,最小值是 0.01,默认值是 1,如果值是 0 就表示拒绝。具体的形式是在数据类型或语言代码后面加一个「;」,然后是「q=value」
    • 注意在 HTTP 的内容协商里“;”的意义是小于“,”的
    • Accept: text/html,application/xml;q=0.9,*/*;q=0.8
    • 表示浏览器最希望使用的是 HTML 文件,权重是 1,其次是 XML 文件,权重是 0.9,最后是任意数据类型,权重 是 0.8。服务器收到请求头后,就会计算权重,再根据自己的实际情况优先输出 HTML 或者 XML
  • Q:内容协商的结果
    • 内容协商的过程是不透明的,每个 Web 服务器使用的算法 都不一样。但有的时候,服务器会在响应头里多加一个Vary字段,记录服务器在内容协商时参考的请求头字段,给出一点信息,如:Vary: Accept-Encoding,User-Agent,Accept
    • 这个 Vary 字段表示服务器依据了 Accept-Encoding、User-Agent 和 Accept 这三个头字段,然后决定了发回的 响应报文
    • Vary 字段可以认为是响应报文的一个特殊的“版本标 记”。每当 Accept 等请求头变化时,Vary 也会随着响应报文一起变化。也就是说,同一个 URI 可能会有多个不同的“版本”,主要用在传输链路中间的代理服务器实现缓存服务

16 | 把大象装进冰箱:HTTP传输大文件的方法

  • Q:HTTP如何分块传输?
    • 在响应报文里用头字段「Transfer-Encoding: chunked」来表示
    • 注意:「Transfer-Encoding: chunked」和「Content-Length」两个字段时互斥的
  • Q:分块传输的编码规则是怎样?
    • 1.每个分块包含两个部分,长度头和数据块
    • 2.长度头以CRLF(回车换行,即\r\n)结尾的一行明文,用16进制数字表示长度
    • 3.数据块紧跟在长度头后,最后也用CRLF结尾,但数据不包含CRLF
    • 4.最后用一个长度为0的块表示结束,即「0\r\n\r\n」
  • Q:HTTP如何实现范围请求(range requests)
    • 范围请求不是 Web 服务器必备的功能,可以实现也可以不实现,所以服务器必须在响应头里使用字段「Accept-Ranges: bytes」明确告知客户端
    • 如果不支持的话服务器可以发送「AcceptRanges: none」,或者干脆不发送“Accept-Ranges”字段,这样客户端就只能收发整块文件了
  • Q:请求头Range的格式是怎样
    • 「bytes=x-y」,其中的 x 和 y 是以字节为单位的数据范围,它们表示的是「偏移量」,范围必须从0计数,如前10个字节表示为「0-9」,第二个10字节表示「10-19」
    • Range 的格式也很灵活,起点 x 和终点 y 可以省略,能够很方便地表示正数或者倒数的范围
    • 假设文件是 100 个字节,那么:
      • 「0-」表示从文档起点到文档终点,相当于「0-99」,即整个文件
      • 「10-」是从第 10 个字节开始到文档末尾,相当于「10-99」
      • 「-1」是文档的最后一个字节,相当于「99-99」
      • 「-10」是从文档末尾倒数 10 个字节,相当于「90-99」
  • Q:服务器收到 Range 字段后,需要做哪四件事?
    • 1.检查范围是否合法,比如文件只有 100 个字节,但请求“200-300”,这就是范围越界了。服务器就会返回状态码416
    • 2.如果范围正确,服务器就可以根据 Range 头计算偏移量,读取文件的片段了,返回状态码“206 Partial Content”,和 200 的意思差不多,但表示 body 只是原数据的一部分
    • 3.服务器要添加一个响应头字段Content-Range,告诉片段的实际偏移量和资源的总大小,格式是「bytes xy/length」,与 Range 头区别在没有「=」,范围后多了总长度。例如,对于「0-10」的范围请求,值就是「bytes 0-10/100」
    • 4.发送数据,直接把片段用 TCP 发给客户端,一个范围请求就算是处理完了
  • Q:下载工具里的多段下载、断点续传的实现要点是什么?
    • 1.先发HEAD,看服务器是否支持范围请求,同时获取文件的大小
    • 2.开N个线程,每个线程使用Range字段划分出各自负责下载的片段,发请求传输数据
    • 3.下载意外中断也不怕,不必重头再来一遍,只要根据上次的下载记录,用 Range 请求剩下的那一部分就可以了
  • Q:如何一次性获取多个片段数据?
    • 在请求头中的Range里使用多个「x-y」
    • 响应头中使用「multipart/byteranges」,表示报文的body是由多段字节序列组成的,并且还要用一个参数「boundary=xxx」给出段之间的分隔标记
    • 每一个分段必须以「--boundary」开始,之后要用「Content-Type」和「Content-Range」标记这段数据的类型和所在范围,然后以回车换行结束,再加上分段数据,最后用一个「--boundary--」表示所有的分段结束

17 | 排队也要讲效率:HTTP的连接管理

  • Q:如何在请求头中明确要求使用长连接?
    • 使用字段Connection: keep-alive
    • 不管客户端是否显式要求长连接,如果服务器支持长连接,它总会在响应报文里放一个“Connection: keepalive”字段
  • Q:客户端如何关闭长连接?
    • 在客户端,可以在请求头里加上「Connection: close」字段,告诉服务器:「这次通信后就关闭连接」。服务器看到这个字段,就知道客户端要主动关闭连接,于是在响应报文里也加上这个字段,发送之后就调用 Socket API 关闭 TCP 连接
    • 客户端和服务器都可以在报文里附加通用头字段「Keep-Alive: timeout=value」,限定长连接的超时时间。但这个字段的约束力并不强,通信的双方可能并不会遵守,所以不太常见
  • Q:不会主动关闭连接的服务器端关闭连接的策略是什么?
    • 1.使用「keepalive_timeout」指令,设置长连接的超时时间,如果在一段时间内连接上没有任何数据收发就主动断开连接,避免空闲连接占用系统资源
    • 2.使用「keepalive_requests」指令,设置长连接上可发送的最大请求次数。比如设置成 1000,那么当 Nginx 在这个连接上处理了 1000 个请求后,也会主动断开连接
  • Q:队头阻塞(Head-of-line blocking)是什么?
    • 这是由 HTTP 基本的「请求 - 应答」模型所导致的
    • 因为HTTP规定报文必须是「一发一收」,这就形成了一个先进先出的「串行」队列。队列里的请求没有轻重缓急的优先级,只有入队的先后顺序,排在最前面的请求被最优先处理
    • 如果队首的请求因为处理的太慢耽误了时间,那么队列里后面的所有请求也不得不跟着一起等待,结果就是其他的请求承担了不应有的时间成本
  • Q:在HTTP/1.1里如何缓解「队头阻塞」的问题?
    • 1.并发连接(concurrent conncetions):同时对一个域名发起多个长连接, 用数量来解决质量的问题
    • 2.域名分片(domain sharding):因HTTP 协议和浏览器限制并发连接数量,就多开几个域名,而这些域名都指向同一台服务器,这样实际长连接的数量就又上去了

18 | 四通八达:HTTP的重定向和跳转

  • Q:301/302重定向的过程
    • 「重定向」实际上发送了两次 HTTP 请求,第一个请求返回了 302,然后第二个请求就被重定向到了「Location」所指的位置
    • 「Location」字段属于响应字段,必须出现在响应报文里。但只有配合 301/302 状态码才有意义,它标记了服务器要求重定向的 URI
    • 浏览器收到 301/302 报文,会检查响应头里有没有「Location」。如果有,就从字段值里提取出 URI,发出新的 HTTP 请求,相当于自动替我们点击了这个链接,这个URI可使用绝对URI和相对URI
  • Q:重定向状态码
    • 301 永久重定向 Moved Permanently:原 URI 已经「永久」性地不存在了,今后的所有请求都必须改用新的 URI,浏览器看到 301,就知道原来的 URI“过时”了,就会做适当的优化。比如历史记录、更新书签,下次可能就会直接用新的 URI 访问,省去了再次跳转的成本。搜索引擎的爬虫看到 301,也会更新索引库,不再使用老的 URI
    • 302 临时重定向 Moved Temporarily:原 URI 处于「临时维护」状态,浏览器或者爬虫看到 302,会认为原来的 URI 仍然有效,但暂时不可用,所以只会执行简单的跳转页面,不记录新的 URI,也不会有其他的多余动作,下次访问还是用原 URI
    • 303 See Other:类似 302,但要求重定向后的请求改为 GET 方法,访问一个结果页面,避免 POST/PUT 重复操作
    • 307 Temporary Redirect:类似 302,但重定向后请求里的方法和实体不允许变动,含义比 302 更明确
    • 308 Permanent Redirect:类似 307,不允许重定向后的请求变动,但它是 301「永久重定向」的含义
    • 303、307、308这三个状态码的接受程度较低,有的浏览器和服务器可能不支持,开发时应当慎重,测试确认浏览器的实际效果后才能使用

19 | 让我知道你是谁:HTTP的Cookie机制

  • Q:如何使用cookie?
    • 响应头字段Set-Cookie和请求头字段Cookie,格式是「key=value」
    • 服务器有时会在响应头里添加多个 Set-Cookie,浏览器这边发送时只要在Cookie中的一行里用「;」隔开就行
  • Q:如何设置 Cookie 的生存周期
    • 可以使用 Expires 和 Max-Age 两个属性
    • 「Expires」即过期时间:用的绝对时间点,即截止日期
    • 「Max-Age」用的相对时间,单位是秒,浏览器用收到报文的时间点再加上 Max-Age,就可以得到失效的绝对时间
    • Expires 和 Max-Age 可以同时出现,两者的失效时间可以一致,也可以不一致,但浏览器会优先采用 Max-Age 计算失效期
  • Q:如何设置 Cookie 的作用域,让浏览器仅发送给特定的服务器和 URI,避免被其他网站盗用
    • 「Domain」和「Path」指定了 Cookie 所属的域名和路径,浏览器在发送 Cookie 前会从 URI 中提取出 host 和 path 部分,对比 Cookie 的属性。如果不满足条件,就不会在请求头里发送 Cookie
    • 使用这两个属性可以为不同的域名和路径分别设置各自的 Cookie,比如“/19-1”用一个 Cookie,“/19-2”再用另外一个 Cookie,两者互不干扰。不过现实中为了省事,通常 Path 就用一个“/”或者直接省略,表示域名下的任意路径都允许使用 Cookie,让服务器自己去挑
  • Q:Cookie的安全性
    • 「HttpOnly」:告诉浏览器,此 Cookie 只能通过浏览器 HTTP 协议传输,禁止其他方式访问,浏览器的 JS 引擎就会禁用 document.cookie 等一切相关的 API,脚本攻击也就无从谈起了
    • 「SameSite」:防范「跨站请求伪造」(XSRF),设置成「SameSite=Strict」可以严格限定Cookie不能随着跳转链接跨站发送,而「SameSite=Lax」略宽松一点,允许GET/HEAD等安全防范,但禁止POST跨站发送
    • 「Secure」:这个 Cookie 仅能用 HTTPS 协议加密传输,明文的 HTTP 协议会禁止发送。但 Cookie 本身不是加密的,浏览器里还是以明文的形式存在

20 | 生鲜速递:HTTP的缓存控制

  • Q:缓存的运行机制是什么?
    • 1.浏览器发现缓存无数据,于是发送请求,向服务器获取资源
    • 2.服务器响应请求,返回资源,同时标记资源的有效期
    • 3.浏览器缓存资源,等待下次重用
  • Q:服务器的缓存控制是怎样
    • 服务器标记资源有效期使用的头字段是“Cache-Control”,里面的值“max-age=30”就是资源的有效时间,相当于告诉浏览器,“这个页面只能缓存 30 秒,之后就算是过期,不能用。”
    • max-age:“生存时间”,时间的计算起点是响应报文的创建时刻(即 Date 字段,也就是离开服务器的时刻),而不是客户端收到报文的时刻,也就是说包含了在链路传输过程中所有节点所停留的时间,单位是秒
    • no_store:不允许缓存,用于某些变化非常频繁的数据,例如秒杀页面
    • no_cache:可以缓存,但在使用前必须要去服务器验证是否过期,是否有最新版本
    • must-revalidate:如果缓存不过期可以继续使用,但过期了如果还想用就必须去服务器验证
  • Q:客户端的缓存控制
    • 浏览器也可以发“Cache-Control”,也就是说请求 - 应答的双方都可以用这个字段进行缓存控制,互相协商缓存的使用策略
    • 当点「刷新」按钮的时候,浏览器会在请求头里加一个「Cache-Control: maxage=0」,浏览器就不会使用缓存,而是向服 务器发请求。服务器看到 max-age=0,也就会用一个最新生成的报文回应浏览器
    • 当使用Ctrl+F5的「强制刷新」时,其实是发了「Cache-Control: no-cache」,含义和“max-age=0”基本一样,就 看后台的服务器怎么理解,通常两者的效果是相同的
  • Q:浏览器的缓存在什么时候生效呢?
    • 点一下浏览器的“前进”“后退”按钮,会看到Status Code中有(from disk cache)的
  • Q:浏览器做缓存控制怎么去服务器验证是否为最新版?
    • HTTP 协议就定义了一系列「If」开头的「条件请求」字段,专门用来检查验证资源是否过期,把验证缓存有效性和获取新版本内容两个请求才能完成的工作合并在一个请求里做。而且,验证的责任也交给服务器,浏览器只需“坐享其成”
    • 条件请求一共有5个头字段,最常使用的是「If-Modified-Since」和「If-None-Match」这两个。需要第一次的响应报文预先提供「Last-modified」和「ETag」,然后第二次请求时就可以带上缓存里的原值,验证资源是否是最新的
    • 如果资源没有变,服务器就回应一个“304 Not Modified”,表示缓存依然有效,浏览器就可以更新一下有效期,然后放心大胆地使用缓存了
    • 「Last-modified」:文件的最后修改时间
    • 「ETag」:即实体标签(Entity Tag),是资源的一个唯一标识,主要用来解决修改时间无法准确区分文件变化的问题。
      • 如一个文件在一秒内修改了多次,但因为修改时间是秒级,所以这一秒内的新版本无法区分
      • 再比如,一个文件定期更新,但有时会是同样的内容,实际上没有变化,用修改时间就会误以为发生了变化,传送给浏览器就会浪费带宽
    • 使用 ETag 就可以精确地识别资源的变动情况,让浏览器能够更有效地利用缓存
    • 强 ETag 要求资源在字节级别必须完全相符,弱 ETag 在值前有个“W/”标记,只要求资源在语义上没有变化,但内部可能会有部分发生了改变(例如 HTML 里的标签顺序调整,或者多了几个空格)

21 | 良心中间商:HTTP的代理服务

  • Q:代理隐藏了真实客户端和服务器,如果双方想要获得这些“丢失”的原始信息,该怎么办呢?
    • 1.代理服务器需要用字段「Via」标明代理的身份,Via 是一个通用字段,请求头或响应头里都可以出现。每当报文经过一个代理节点,代理服务器就会把自身的信息追加到字段的末尾。如果通信链路中有很多中间代理,就会在 Via 里形成一个链表,这样就可以知道报文究竟走过了多少个环节才到达了目的地。Via 字段只解决了客户端和源服务器判断是否存在代理的问题,还不能知道对方的真实信息
    • 2.「X-Forwarded-For」字段,和「Via」差不多,每经过一个代理节点就会在字段里追加一个信息。但「Via」追加的是代理主机名(或者域名),而「X-Forwarded-For」追加的是请求方的 IP 地址。所以,在字段里最左边的 IP 地址就客户端的地址
    • 3.「X-Real_IP」字段,记录客户端 IP 地址,没有中间的代理信息
  • Q:代理协议(The PROXY protocol)v1是怎样
    • 在 HTTP 报文前增加了一行 ASCII 码文本
    • 这一行文本其实非常简单,开头必须是“PROXY”五个大写字母,然后是“TCP4”或者“TCP6”,表示客户端的 IP 地址类型,再后面是请求方地址、应答方地址、请求方端口号、应答方端口号,最后用一个回车换行(\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
- 服务器看到这样的报文,只要解析第一行就可以拿到客户端地址,不需要再去理会后面的 HTTP 数据,省了很多事情
- 不过代理协议并不支持“X-Forwarded-For”的链式地址形式,所以拿到客户端地址后再如何处理就需要代理服务器与后端自行约定

22 | 冷链周转:HTTP的缓存代理

  • Q:源服务器的缓存控制有哪些?
    • 1.使用两个新属性「private」和「public」来区分客户端上的缓存和代理上的缓存
      • private:只能在客户端保存,不能放在代理上与别人共享
      • public:缓存完全开放,谁都可以存,谁都可以用
    • 2.缓存失效后的重新验证也要区分开(即使用条件请求「Last-modified」和「ETag」)
      • must-revalidate:只要过期就必须回源服务器验证
      • proxy-revalidate:只要求代理的缓存过期后必须验证,客户端不必回源,只验证到代理这个环节就行
    • 3.缓存的生存时间可以使用新的「s-maxage」,只限定在代理上能够存多久,而客户端仍然使用「max-age」
    • 4.代理专用的属性「no-transform」:代理有时候会对缓存下来的数据做一些优化,比如把图片生成 png、webp 等几种格式,方便今后的请求处理,而「no-transform」就会禁止这样做
  • Q:可以同时控制客户端和代理的完整服务器端缓存控制策略的流程图
  • Q:客户端的缓存控制
    • max-stale:如果代理上的缓存过期了也可以接受,但不能过期太多,超过 x 秒也会不要
    • min-fresh:缓存必须有效,而且必须在 x 秒后依然有效,即必须处于过期前 x 秒
    • only-if-cached:只接受代理缓存的数据,不接受源服务器的响应。如果代理上没有缓存或者缓存过期,就应该给客户端返回一个504(Gateway Timeout)
Last Updated:
Contributors: Shiqi Lu