【HTTP】04_进阶

前言

前面说到HTTP的组成是起始行 + 头部 + 空行 + 实体。头部由key: value组成,这些内容有很大的意义。进阶篇中主要讲述了头部中常见的字段。

Accept和Content-Type

数据类型使用的头字段

之前提到过,HTTP之前只支持文本数据,后来随着互联网的发展,数据类型越来越多。于是参考MIME将数据分为了几大类:

  • text: 文本格式的数据,常见的有text/htmltext/plain等;
  • image: 图像格式,常见的有image/gifimage/jpeg等;
  • audio/video: 音频或者视频数据,常见的有audio/mpegvideo/mp4等;
  • application:数据格式不固定,常见的有application/jsonapplication/javascriptapplication/pdf等。

另外在传输的过程中,会将数据进行压缩,压缩方式有多种:

  • gzipGNU zip压缩格式,也是互联网上最流行的压缩格式;
  • deflatezlib(deflate)压缩格式,流行程度仅次于gzip
  • br: 一种专门为 HTTP 优化的新压缩算法。

头部中使用了AcceptContent字段,用于客户端和服务器进行内容协商。客户端用Accept头告诉服务器希望接收什么样的数据,而服务器用Content头告诉客户端实际发送了什么数据。

请求头设置Accept告诉服务器,我(浏览器)可以接收application/jsontext/plain或者任意数据类型,允许的数据压缩类型为gzip、deflate和br

在这里插入图片描述

响应头设置Content告诉浏览器,自己发送了什么类型的数据:

在这里插入图片描述

还可以通过Content-Encoding告诉传递的内容是用什么方式来压缩的:

Content-Encoding: gzip

不过这两个字段是可以省略的,如果请求报文里没有 Accept-Encoding 字段,就表示客户端不支持压缩数据;如果响应报文里没有 Content-Encoding字段,就表示响应数据没有被压缩。

语言类型与编码

语言类型指的是人类使用的自然语言,例如英语、汉语和日语等。

在计算机发展的早期,各个国家发明了许多种字符编码,比如英语世界用的ASCII、汉语世界用的GBK、BIG5,日语世界用的Shift_JIS等,后来就出现了 UnicodeUTF-8,把世界上所有的语言都容纳在一种编码方案里,UTF-8 也成为了互联网上的标准字符集。

HTTP中可以使用Accept-Language标记客户端可以理解的自然语言;用Content-Language来指定服务器响应报文中的实际语言类型:

Accept-Language: zh-CN, zh, en
Content-Language: zh-CN

对于字符集,客户端可以使用Accept-Charset,但是服务器的响应头需要使用Content-Type

Accept-charset: gbk, utf-8
Content-Type: text/html; charset=utf-8

不过现在的浏览器都支持多种字符集,通常不会发送Accept-Charset,而服务器也不会发送Content-Language,因为使用的语言完全可以由字符集推断出来,所以在请求头里一般只会有Accept-Language字段,响应头里只会有Content-Type字段。

内容协商的权重

用字母q代表权重,最大值是1,最小值是0.01:

Accept: text/html,application/xml;q=0.9,*/*;q=0.8

它表示浏览器最希望使用的是 HTML 文件,权重是 1,其次是 XML 文件,权重是 0.9,最后是任意数据类型,权重是 0.8。

内容协商的结果

内容协商的过程是不透明的,每个 Web 服务器使用的算法都不一样。但有的时候,服务器会在响应头里多加一个Vary字段,记录服务器在内容协商时参考的请求头字段,给出一点信息,例如:

Vary: Accept-Encoding,User-Agent,Accept

这个 Vary 字段表示服务器依据了 Accept-Encoding、User-Agent 和 Accept 这三个头字段,然后决定了发回的响应报文。

有关传输大文件的字段

随着互联网的发展,数据传输的种类越来越多,大小也越来越大。

Accept-Encoding和Content-Encoding进行压缩

前面我们提到了Accept-EncodingContent-Encoding,这两个字段一个是客户端允许接收的数据压缩类型,一个是服务器可以响应的数据压缩类型。通过数据压缩,我们可以将数据压缩。

使用Gzip压缩有个缺点就是,只对文本文件有较好的压缩率,而图片、音频等多媒体数据本身就已经是高度压缩的,再使用Gzip处理也不会变小。

Transfer-Encoding分块传输

除了数据压缩之外,可以将大文件拆分成小文件传递给客户端。

Transfer-Encoding: chunked

由于拆分的长度是未知的,所以无法跟Content-Length一起使用。

举个例子,从GitHub上下载源码包,GitHub要实时压缩实时发送,而不是一下子压缩好再发送,这样body的长度一开始就是未知的。所以就要用分块编码,压缩一部分,就发一部分,这部分的长度是已知的,但总长度只有压缩完才能知道。chunked编码用在“流式”收发数据的时候,通常数据是即时生成的,也就是动态数据。

Range、Accept-Ranges和Content-Range范围请求

除了压缩、分块,对于大文件处理我们还可以使用范围请求。在看电视剧的适合,我们想跳过片头直接看正片,这实际上就是想获取大文件其中的片段数据。

想跟服务器请求某范围的数据的,我们需要先让服务器支持范围请求:

# 响应头
HTTP/1.1 206 Partial Content
Content-Length: 32
Accept-Ranges: none      # 不支持
Accept-Ranges: bytes     # 支持

然后客户端发起一个请求,告诉浏览器要请求多少数据,可以借助Range

# 请求头
GET /16-2 HTTP/1.1
Host: www.chrono.com
Range: bytes=0-31

服务器接收到Range之后,需要做4件事情:

  • 检查范围是否合法,如果越界,则返回416,意思是你的范围请求有无,我无法再处理;

  • 如果范围正确,则返回206 Partial Content,返回了0-31这部分的数据;

  • 服务器要添加一个响应头字段Content-Range,告诉片段的实际偏移量和资源的总大小,格式是“bytes x-y/length”:

    HTTP/1.1 206 Partial Content
    Content-Length: 32
    Accept-Ranges: bytes
    Content-Range: bytes 0-31/96
    

不仅看视频的拖拽进度需要范围请求,常用的下载工具的多段下载、断点续传也是基于它实现的,要点是:

  • 先发HEAD,看服务器是否支持范围请求,同时获取文件的大小;
  • 开N个线程,每个线程用Range字段划分各自负责下载的片段,发请求传输数据;
  • 下载意外中断也不怕,不必重头再来一遍,只要根据上次的下载记录,用Range请求剩下的那一部分就行。

多段数据

范围请求一般只获得一次数据,如果需要支持获取多个片段,可以在Range中使用多个x-y。这时响应报文中需要使用特殊的MIMT类型,还需要使用boundary=xxx给出段直接分隔标记。

# 请求头
GET /16-2 HTTP/1.1
Host: www.chrono.com
Range: bytes=0-9, 20-29
# 响应头
HTTP/1.1 206 Partial Content
Content-Type: multipart/byteranges; boundary=00000000001
Content-Length: 189
Connection: keep-alive
Accept-Ranges: bytes

--00000000001
Content-Type: text/plain
Content-Range: bytes 0-9/96
 
// this is
--00000000001
Content-Type: text/plain
Content-Range: bytes 20-29/96
 
ext json d
--00000000001--

报文里的“- -00000000001”就是多段的分隔符,使用它客户端就可以很容易地区分出多段 Range 数据。

跟链接有关的Connection

前面提到过HTTP/1.1HTTP/1.0,明确了链接管理,允许持久链接。

HTTP/1.0时使用的是短链接,在发起请求时,客户端和服务端需要先建立起TCP链接,收到响应报文之后会立即关闭。这种客户端与服务器不会保持长时间的链接状态成为短链接。另外TCP协议还有慢启动和拥塞窗口等特性,新建立的冷链接会比打开了一段时间的热链接要更慢一些。这些原因导致了HTTP的链接性能问题。

后来HTTP/1.1采取了长连接,也叫持久链接。在 HTTP/1.1 中的连接都会默认启用长连接。不需要用什么特殊的头字段指定,只要向服务器发送了第一次请求,后续的请求都会重复利用第一次打开的 TCP 连接。

在这里插入图片描述

我们也可以使用Connection字段:

Connection: keep-alive      # 使用长连接
Connection: close           # 断开链接

重定向

当返回的数据的响应头的状态码是301(永久重定向)或者302(临时重定向),会在响应头的首部返回一个Location字段,告诉浏览器,应该重定向到哪里:

Location: /index.html

在“Location”里的 URI 既可以使用绝对 URI,也可以使用相对 URI。所谓“绝对 URI”,就是完整形式的 URI,包括scheme、host:port、path等。所谓“相对 URI”,就是省略了 scheme 和 host:port,只有path 和 query部分,是不完整的,但可以从请求上下文里计算得到。

重定向是服务器发起的跳转,要求客户端改用新的 URI 重新发送请求,通常会自动进行,用户是无感知的。

补充:

301 的含义是“永久”的。

如果域名、服务器、网站架构发生了大幅度的改变,比如启用了新域名、服务器切换到了新机房、网站目录层次重构,这些都算是“永久性”的改变。原来的 URI 已经不能用了,必须用 301“永久重定向”,通知浏览器和搜索引擎更新到新地址,这也是搜索引擎优化(SEO)要考虑的因素之一。

302 的含义是“临时”的。

原来的 URI 在将来的某个时间点还会恢复正常,常见的应用场景就是系统维护,把网站重定向到一个通知页面,告诉用户过一会儿再来访问。另一种用法就是“服务降级”,比如在双十一促销的时候,把订单查询、领积分等不重要的功能入口暂时关闭,保证核心服务能够正常运行。

记住用户身份的Cookie

由于HTTP是无状态的,所以不会记住访问的用户是谁,所以每次发送一个请求,得先判断是哪个用户再执行相应的操作。借助Cookie可以让服务器认出对方是谁。

在响应头上我们可以根据Set-Cookie设置Cookie,这样在请求头中就会自动携带Cookie

在这里插入图片描述

如过服务器响应时设置了两个Cookie

Set-Cookie: sid=9436
Set-Cookie: system=Windows_x64

那么下一次客户端发送请求时,浏览器就会在请求头中自动送出Cookie

Cookie: sid=9436;system=Windows_x64

由于Cookie服务器委托浏览器存储在客户端的一些数据,所以需要一些手段来保护,防止外泄或者窃取:

在这里插入图片描述

  • 设置Cookie的生命周期:使用Expires设置截止日期,使用Max-Age得到失效的秒数,ExpiresMax-Age 可以同时出现,两者的失效时间可以一致,也可以不一致,但浏览器会优先采用Max-Age计算失效期。
  • 设置Cookie的作用域,使用DomainPath指定了域名和路径,浏览器在发送 Cookie 前会从 URI 中提取出 host 和 path 部分,对比 Cookie 的属性。如果不满足条件,就不会在请求头里发送 Cookie;
  • 设置HttpOnly,禁止JS脚本使用document.cookie来读写Cookie数据,阻止XSS攻击(即注入恶意脚本);
  • 设置SameSite=Strict严格限制Cookie不能随着跳转链接跨站发送,防范CSRF(跨站请求伪造)。

Cookie可以用域身份识别,保存用户的登录信息;另一个作用是广告追踪。

你上网的时候肯定看过很多的广告图片,这些图片背后都是广告商网站(例如 Google),它会“偷偷地”给你贴上 Cookie 小纸条,这样你上其他的网站,别的广告就能用 Cookie 读出你的身份,然后做行为分析,再推给你广告。

跟缓存相关的Cache-Control

由于浏览器使用HTTP获取资源的成本较高,所以将数据缓存起来的话,下次再请求就可以尽可能地服用。这样,就可以避免多次请求 - 应答的通信成本,节约网络带宽,也可以加快响应速度。所以发起HTTP请求前,会先做出这样的行为:

  • 浏览器发现缓存无数据,则发起请求,向服务器获取资源,否则直接从缓存中获得数据;
  • 服务器响应请求,返回资源,同时标记资源的有效期;
  • 浏览器缓存资源,等待下次重用。

服务器标记资源缓存用的是Cache-Control字段:

描述
max-age从响应被创建的时间起,资源的生存时间。
no_store不允许缓存。
no_cache在使用缓存之前,先去服务器验证一下资源是否过期,是否有最新版本。
must-revalidate如果缓存不过期就继续使用,否则就去跟服务器验证,

不止服务器可以设置Cache-Control,浏览器也可以:

# 刷新
Cache-Control: max-age=0

## 强制刷新
Cache-Control: no-cache

如果我们在请求资源的时候先去询问资源是否过期,然后获得返回结果,再根据结果进行下一步操作,比如过期,则去请求服务器,那么就发送了两个请求,对此HTTP定义了条件请求字段。

# 响应报文
Last-Modified: Wed, 10 Mar 2021 02:28:56 GMT
ETag: "60482ee8-19f"

Last-Modified是文件最后的修改时间;ETag是实体标签,资源的唯一标识。两者相辅相成,ETag主要用来解决修改时间无法精确区分文件变化的问题;而如果对一些图片文件的修改每次都扫描内容生成ETAG来比较,显然比修改时间慢很多。

接下来每次请求同一个资源的时候,就会携带这个:

# 请求报文
# 与Last-Modified对应
if-Modified-Since: Wed, 10 Mar 2021 02:28:56 GMT
# 与ETag对应
If-None-Match: "60482ee8-19f"

此时如果能找到缓存的话,当前返回的状态码是304 Not Modified

跟代理相关的字段

在这里插入图片描述

所谓的“代理服务”就是指服务本身不生产内容,而是处于中间位置转发上下游的请求和响应,具有双重身份:面向下游的用户时,表现为服务器,代表源服务器响应客户端的请求;而面向上游的源服务器时,又表现为客户端,代表客户端发送请求。

代理最基本的一个功能是负载均衡。因为在面向客户端时屏蔽了源服务器,客户端看到的只是代理服务器,源服务器究竟有多少台、是哪些 IP 地址都不知道。于是代理服务器就可以掌握请求分发的“大权”,决定由后面的哪台服务器来响应请求。

在负载均衡的同时,代理服务还可以执行更多的功能,比如:

  • 健康检查:使用“心跳”等机制监控后端服务器,发现有故障就及时“踢出”集群,保证服务高可用;
  • 安全防护:保护被代理的后端服务器,限制IP地址或流量,抵御网络攻击和过载;
  • 加密卸载:对外网使用SSL/TLS加密通信认证,而在安全的内网不加密,消除加解密成本;
  • 数据过滤:拦截上下行的数据,任意指定策略修改请求或者响应;
  • 内容缓存:暂存、复用服务器响应。

HTTP中跟代理相关的字段我们一般接触不到,只有代理服务器转发发起的请求才会携带:

  • via: 标明代理的身份,添加的是代理主机名(或者域名);
  • X-Forwarded-For:形式上和“Via”差不多,也是每经过一个代理节点就会在字段里追加一个信息,追加的是请求方的 IP 地址,所以,在字段里最左边的 IP 地址就客户端的地址;
  • X-Real-IP:只记录客户端 IP 地址。

缓存代理

前面提到了客户端缓存,代理也是可以做缓存的。在没有缓存的时候,代理服务器每次都是直接转发客户端和服务端的报文,中间不会存储任何数据,只有最简单的中转功能。加入缓存之后,代理服务受到源服务器发来的响应数据后需要做两件事,第一个是将报文转发给客户端,另一个就是把报文存入自己的Cache中。

前面提到了Cache-Control其实还有两个属性值:privatepublicprivate表示只能在客户端上保存,不能放在代理上跟其他人共享;而public表示缓存完全开放,谁都可以存可以使用。

Cache-Control上还有几个跟代理缓存相关的字段:

描述
s-maxage缓存生存时间
no-transform禁止对缓存数据进行转换
proxy-revalidate只要代理的缓存过期后就必须验证,客户端不必回源,只验证到这个环节就行。

总结

本节主要讲述了HTTP头部中主要的几个字段:

  • 浏览器可以提供Accept告诉服务端自身可以接受的文件类型,而服务端在发送时可以在响应头使用Content-Type告知浏览器具体发送了什么文件。Content-Type也可用于浏览器提交数据时告知服务器提交了什么类型的数据。
  • Accept-Encoding可以指定浏览器可接受的数据压缩类型,不提供则不支持;Content-Encoding则是服务器提供的数据压缩类型,不提供则代表没有压缩数据;
  • 由于互联网发展迅猛,传输的文件类型越来越复杂,大小越来越大,为了提高传输效率,可以使用Accept-EncodingContent-Encoding来指定接受/发送的压缩格式、Transfer-Encoding: chunked来进行分块传输、RangeAccept-RangesContent-Range来进行范围传输和多端传输;
  • 浏览器只返回一部分传输内容时,状态码应为206;
  • HTTP/1.0中使用的是短链接,HTTP/1.1使用和默认的是长连接,字段为Connection: keep-alive
  • 301是永久重定向,302是临时重定向,当响应头中指定Location时,浏览器会自动跳转到指定页面;
  • HTTP是无状态的,可以借助Cookie来记录用户身份并进行广告追踪;
  • Cookie是服务器生成并传送给客户端的,客户端接受到之后会在下一次请求中携带(只要Cookie不过时);
  • 使用Cache-Control可以将资源进行缓存,如果设置max-age,则跟强缓存有关,在未过期之前直接读取缓存,如果设置是no_cache则跟协商缓存有关,先请求服务器再根据是否过期进行下一步操作,no_store则表示不缓存;
  • 协商缓存服务器会返回Last-ModifiedETag来表面资源的缓存修改时间和唯一标志,下次请求时,则会在请求头中加入If-Modified-SinceIf-None-Match来判断是否要请求新资源;
  • 强缓存不用请求服务器,直接根据max-age判断是否读取浏览器缓存;而协商缓存需要请求服务器,询问缓存是否过期再判断是否读取浏览器缓存;
  • 代理服务指服务本身不生产内容,而是处于中间位置转发上下游的请求和响应。代理最基本的一个功能是负载均衡;
  • CDN就是一种代理服务;
  • 代理也是可以做缓存的,使用Cache-Control可以控制资源是否可以被代理缓存,priviate表示资源只能被客户端缓存,public表示客户端和代理都能对该资源进行缓存,另外还可以设置代理缓存的过期时间等字段。

参考

透视HTTP协议

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值