[详解《图解HTTP》+ 拓展]第二章:了解HTTP/1.1的结构

341 阅读16分钟

该系列博客是记录自己的学习笔记,若有错误请大佬们指出

狗头声明,本文适用于:新手学习进步,老鸟回顾消遣
该系列以《图解HTTP》为基本路线,该书有11章节,所以本系列也决定编写11篇水文技术博客进行适当的拓展和补充,希望可以帮助大家。

1、简单了解各版本HTTP/0.9、HTTP/1.0、HTTP/1.1、HTTP/2、HTTP/3

由于《图解HTTP》中是直接讲解HTTP/1.1,在这里先对其他版本做个简单介绍

HTTP/0.9

由于早期的web,是在互联网上构建 超链接文档系统 ,所以结构比较简单
HTTP/0.9只允许用“GET”动作从服务器上获取 HTML 文档,并且在响应请求之后立即关闭连接,功能非常有限。\

0.9并不是一个版本,1.0之前的统称为0.9,也就是早期的HTTP版本号都没有,更别说标准。

HTTP/1.0

后续计算机多媒体技术有了新的发展:1992 年发明了 JPEG 图像格式,1995 年发明了 MP3 音乐格式。
所以HTTP/1.0
1、增加了 HEAD、POST 等新方法;
2、增加了响应状态码,标记可能的错误原因;
3、引入了协议版本号概念;
4、引入了 HTTP Header(头部)的概念,让 HTTP 处理请求和响应更加灵活;
5、传输的数据不再仅限于文本(图片 视频 音频都是超文本,HTML5)。

HTTP/1.0是第一个正式标准,版本1.0,编号RFC1945(HTTP0.9没有RFC)

HTTP/1.1

微软的 IE与网景的“浏览器”斗争推动了HTTP发展,发布1.1版本

HTTP1.1 1、增加了 PUT、DELETE 等新的方法;
2、增加了缓存管理和控制;
3、明确了连接管理,允许持久连接;
4、允许响应数据分块(chunked),利于传输大文件;
5、强制要求 Host 头,让互联网主机托管成为可能。

HTTP/1.1 发布 RFC 文档,编号为 2616

是目前互联网上使用最广泛的协议,功能也非常完善

再简单说下HTTP/2、HTTP/3

HTTP/2 基于 Google 的 SPDY 协议,注重性能改善,但还未普及;
HTTP/3 基于 Google 的 QUIC 协议,是将来的发展方向。
2018 年,HTTP/3 正式进入了标准化制订阶段,所以很可能会跳过 HTTP/2 直接进入 HTTP/3。

我的看法就是既然HTTP2没普及,HTTP1.1目前依然是使用最广泛,那就先学1.1咯,等3.0版本出来再看。(技术狂热爱好者大佬们息怒,这个建议只是我等只是普通人[dog])

总结:互联网的用户推动协议的发展。刚刚开始只有文本,都只是文字;后来有了超文本,不仅仅是文字;后来嫌弃速度慢,有了持久连接,缓存机制;后来为了安全,有了加密通信。 一切都是以用户的需求为导向的,用户的需求越来越高,协议就越来越高级,越来越完善。


2、简单了解Web是如何建立在HTTP协议上通信的

(后续会详解,这里先了解流程)

Web浏览器和Web服务器之间通过请求响应来达成通信;浏览器发送一个请求报文给服务器,服务器收到后,返回一个响应报文给浏览器。

下面看《图解HTTP》的示例:
(1)、请求报文

image.png

翻译:浏览器发了一个请求报文,用了POST方法,想获取URI定位的这个互联网上的资源,遵循的协议是HTTP协议的1.1版本(报文的首部和实体后续第三章详解)

请求报文是由请求方法、请求URI、协议版本、可选的请求首部字段和内容实体构成的。

(2)、响应报文

服务器收到请求报文后,处理完毕然后返回一个响应报文

image.png

翻译:我服务器遵循的也是HTTP1.1,状态码是200,原因短句是“OK”。

响应报文基本上由协议版本、状态码(表示请求成功或失败的数字代码)、用以解释状态码的原因短语、可选的响应首部字段以及实体主体构成。


3、HTTP的请求方法

由本文第二节可知,请求报文中第一行第一个就是HTTP请求方法;这节对HTTP1.1的请求方法进行介绍。

为什么要有请求方法?因为对于同一个资源,是有不同的需求的:获取、删除、提交等...。所以客户端发出了一个“动作指令”,要求服务器端对 URI 定位的资源执行这个动作。

HTTP/1.1 中规定了八种请求方法,字符区分大小写,必须是大写:

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

其实就是“增删改查”操作,只不过这些动作操作的目标不是本地资源,而是远程服务器上的资源,所以只能由客户端“请求”或者“指示”服务器来完成。

注意:既然请求方法是一个“指示”,那么客户端自然就没有决定权,服务器掌控着所有资源,也就有绝对的决策权力。它收到 HTTP 请求报文后,看到里面的请求方法,可以执行也可以拒绝,或者改变动作的含义,毕竟 HTTP 是一个“协议”,两边都要“商量着来”。

(1)、GET/HEAD

GET 方法是诞生最早,用的最多的请求方法,含义是请求从服务器获取资源,这个资源既可以是静态的文本、页面、图片、视频,也可以是由 PHP、Java 动态生成的页面或者其他格式的数据。

GET 方法虽然基本动作比较简单,但搭配 URI 和请求首部字段就能实现对资源更精细的操作。
(例如:使用 If-Modified-Since 字段就变成了“有条件的请求”,仅当资源被修改时才会执行获取动作;使用 Range 字段就是“范围请求”,只获取资源的一部分数据。这些后面详解请求首部字段的章节再学习)

HEAD 方法与 GET 方法类似,也是请求从服务器获取资源,服务器的处理机制也是一样的,但服务器不会返回请求的实体数据,只会传回响应头。

HEAD 方法可以看做是 GET 方法的一个“简化版”。因为它的响应头与 GET 完全相同,所以可以用在很多并不真正需要资源的场合,避免传输 body 数据的浪费。

(例如,想要检查一个文件是否存在,或者是要检查文件是否有最新版本等操作,这种情况只要发个 HEAD 请求就可以了,没有必要用 GET 把整个文件都取下来。)

(2)、POST/PUT

GET 和 HEAD 方法是从服务器获取数据,而 POST 和 PUT 方法则是相反操作,向 URI 指定的资源提交数据,数据就放在报文的 body 里。

PUT 的作用与 POST 类似,也可以向服务器提交数据,但与 POST 存在微妙的不同,通常 POST 表示的是“新建”“create”的含义,而 PUT 则是“修改”“update”的含义。

注意:目前以我的工作经历,是有过使用PUT的,往往是“新增”使用POST,“修改”使用PUT。但是PUT与 POST 的语义、功能太过近似,有的服务器甚至就直接禁止使用 PUT 方法,只用 POST 方法上传数据。

另外四个不常用的请求方法有兴趣去自行了解吧,咱们也精力有限,几乎用不着的知识,那就别费工夫去吭哧吭哧的学了,用到了再查阅。


4、HTTP的特点

(1)、短链接

HTTP 协议是基于 TCP/IP 的,因为需要 TCP 的“可靠”。

但是HTTP在每次通信结束后(即一次 请求 - 响应 结束)就会断开连接,包括断开TCP连接

缺点:消耗性能严重

因为在 TCP 协议里,建立连接和关闭连接都是消耗性能的,需要“三次握手”、“四次挥手”。

所以后续使用的持久链接(也叫长链接)技术来解决

(2)、无状态

HTTP是不保存状态的协议

即每次通信都是新的请求报文和新的响应报文,并不会保留之前的一切请求或响应报文的信息。

优点:减轻服务器的负担,因为服务器没有“记忆能力”,所以就不需要额外的资源来记录状态信息

缺点:既然服务器没有“记忆能力”,它就无法支持需要连续多个步骤的“事务”操作。例如电商购物,首先要登录,然后添加购物车,再下单、结算、支付,这一系列操作都需要知道用户的身份才行,但“无状态”服务器是不知道这些请求是相互关联的,每次都得问一遍身份信息,不仅麻烦,而且还增加了不必要的数据传输量。

后续使用Cookie技术来解决这个问题。

(3)、不安全

因为HTTP协议的明文传输,即协议里的报文(准确地说是 header 部分)不使用二进制数据,而是用简单可阅读的文本形式。

  • 明文的优点:查看或者修改直接就是明文,为我们的开发调试工作带来极大的便利。
  • 明文的缺点:HTTP 报文的所有信息都会暴露在漫长的传输链路的每一个环节。

安全有很多的方面,明文只是“机密”方面的一个缺点,在“身份认证”和“完整性校验”这两方面 HTTP 也是欠缺的。

所以后续使用了HTTPS技术来解决安全问题,在本系列第七章会详解。

(4)、可拓展性

因为HTTP很简单,很优秀,但也有很多不足,所以需要拓展(例如上述的Cookie技术就是HTTP协议的拓展)而HTTP的拓展性很好,这也意味着HTTP即使有不足也可以长久发展,因为可以通过拓展新技术补充给HTTP协议从而解决。


5、持久链接

HTTP最开始是短链接,缺点在于每次发送请求前需要先与服务器建立连接,收到响应报文后会立即关闭连接。

因为早期网页就是一个展示文本,一个页面并没有很多资源需要加载,所以请求后立即断开没问题

但是页面逐渐发展复杂,单个页面会有图片、音频、视频等等更多的资源,这个时候每个资源依然用短链接的话,加载速度就很慢了。 image.png

短连接的缺点相当严重,因为在 TCP 协议里,建立连接和关闭连接都是非常“昂贵”的操作。TCP 建立连接要有“三次握手”,发送 3 个数据包,需要 1 个 RTT;关闭连接是“四次挥手”,4 个数据包需要 2 个 RTT。

而 HTTP 的一次简单“请求 - 响应”通常只需要 4 个包,如果不算服务器内部的处理时间,最多是 2 个 RTT。这么算下来,浪费的时间就是“3÷5=60%”,有三分之二的时间被浪费掉了,传输效率低得惊人。

长链接:

image.png

由图可知,使用长链接来传输,多个资源只进行了一次TCP的“三次握手”和“四次挥手”,减少了数据包的数量,使得复杂网页的加载速度变快。

长链接很优秀,如何开启?

(1)、由于长链接对性能的改善效果非常显著,所以在** HTTP/1.1 中的连接都会默认启用长链接**。不需要用什么特殊的头字段指定,只要向服务器发送了第一次请求,后续的请求都会重复利用第一次打开的 TCP 连接,也就是长链接,在这个连接上收发数据。(所以现在的网页一般都使用长连接,除非明确知道只会发送一个请求,比如游戏内连接兑换码服务进行礼包兑换)

(2)、也可以在请求头里明确地要求使用长链接机制,使用的字段是 Connection,值是“keep-alive”。请求头里加上“Connection: close”字段,告诉服务器:“这次通信后就关闭连接”。

(3)、另外,只要服务器支持长链接的话,不管客户端是否主动要求长链接,它总会在响应报文里放一个“Connection: keep-alive”字段,告诉客户端:服务器是支持长链接的

拓展:管线化

在长链接的基础上,请求以管线化(pipelining)方式发送成为可能。

即从前发送请求后需等待并收到响应,才能发送下一个请求。管线化技术出现后,不用等待响应亦可直接发送下一个请求。

这样就能够做到同时并行发送多个请求,而不需要一个接一个地等待响应了。

所以管线化技术则比持久连接还要快。请求数越多,时间差就越明显。


6、Cookie

上述提到,为了解决HTTP是无状态协议的缺点,所以HTTP拓展了Cookie技术来解决。

Cookie的本质就是让原本无“记忆能力”的服务器拥有“记忆能力”

HTTP 的 Cookie 机制就是:既然服务器记不住,那就在外部想办法记住。相当于是服务器给每个客户端都贴上一张小纸条,上面写了一些只有服务器才能理解的数据,需要的时候客户端把这些信息发给服务器,服务器看到 Cookie,就能够认出对方是谁了。

(1)、Cookie 的工作过程:

响应头字段 Set-Cookie请求头字段 Cookie

<1>、第一次访问

当用户通过浏览器第一次访问服务器的时候,服务器肯定是不知道他的身份的。所以,就要创建一个独特的身份标识数据,格式是“key=value”,然后放进 Set-Cookie 字段里,随着响应报文一同发给浏览器。浏览器收到响应报文,看到里面有 Set-Cookie,知道这是服务器给的身份标识,于是就保存起来,下次再请求的时候就自动把这个值放进 Cookie 字段里发给服务器。

image.png

<2>、第二次访问

第二次请求里面有了 Cookie 字段,服务器就知道这个用户不是新人,之前来过,就可以拿出 Cookie 里的值,识别出用户的身份,然后提供个性化的服务。

image.png

注意:服务器有时会在响应头里添加多个 Set-Cookie,存储多个“key=value”。但浏览器这边发送时不需要用多个 Cookie 字段,只要在一行里用“;”隔开就行。

看看第一、二次的请求头和响应头的信息就一目了然了:

image.png

注意:从这张图中我们也能够看到,Cookie 是由浏览器负责存储的,而不是操作系统。所以,它是“浏览器绑定”的,只能在本浏览器内生效。如果你换个浏览器(即使是同一个电脑)或者换台电脑,新的浏览器里没有服务器对应的 Cookie,服务器也就认不出来了,只能再走一遍 Set-Cookie 流程。

(2)、Cookie 的属性

Cookie 本质就是服务器委托浏览器存储在客户端里的一些数据,而这些数据通常都会记录用户的关键识别信息。所以,就需要在“key=value”外再用一些手段来保护信息,防止外泄或窃取,这些手段就是 Cookie 的属性。

<1>、设置 Cookie 的生存周期

Cookie 的生存周期由ExpiresMax-Age 两个属性来设置。

即在这个时间内,这个Cookie有效,可以证明用户身份。一旦超过这个期限,浏览器就认为是 Cookie 失效,会在存储里删除,也不会发送给服务器。这样防止Cookie长时间有效,造成外泄或窃取。

Expires”是“过期时间”,用的是绝对时间点,可以理解为“截止日期”(deadline)。
Max-Age”用的是相对时间,单位是,浏览器用收到报文的时间点再加上 Max-Age,就可以得到失效的绝对时间

注意:Expires 和 Max-Age 可以同时出现,两者的失效时间可以一致,也可以不一致,但浏览器会优先采用 Max-Age 计算失效期。

image.png

<2>、设置 Cookie 的作用域

即让浏览器仅发送给特定的服务器和 URI,避免被其他网站盗用,造成外泄或窃取。

作用域的设置比较简单,“Domain”和“Path”指定了 Cookie 所属的域名路径,浏览器在发送 Cookie 前会从 URI 中提取出 hostpath 部分,对比 Cookie 的属性。如果不满足条件,就不会在请求头里发送 Cookie。

<3>、Cookie安全

即Cookie只应该给目标服务器看,防范其他的获取方式

Cookie属性“HttpOnly”、“SameSite”、

“跨站脚本”(XSS)攻击: JS 脚本里可以用 document.cookie 来读写 Cookie 数据,这就带来了安全隐患,有可能会导致“跨站脚本”(XSS)攻击窃取数据。
解决方法:Cookie属性“HttpOnly”会告诉浏览器,此 Cookie 只能通过浏览器 HTTP 协议传输,禁止其他方式访问,浏览器的 JS 引擎就会禁用 document.cookie 等一切相关的 API,脚本攻击也就无从谈起了。

“跨站请求伪造”(XSRF)攻击: 另一个属性“SameSite”可以防范“跨站请求伪造”(XSRF)攻击,设置成“SameSite=Strict”可以严格限定 Cookie 不能随着跳转链接跨站发送,而“SameSite=Lax”则略宽松一点,允许 GET/HEAD 等安全方法,但禁止 POST 跨站发送。

属性叫“Secure” 表示这个 Cookie 仅能用 HTTPS 协议加密传输,明文的 HTTP 协议会禁止发送。但 Cookie 本身不是加密的,浏览器里还是以明文的形式存在。

(3)、Cookie的应用

首先就是通过了记录用户信息,解决了上诉HTTP无状态的缺陷。

但终究是群众里面有坏人,既然Cookie记录了用户信息,所以Cookie 的另一个常见用途是广告跟踪。广告商势力大的一批,无论你走到哪里它都会通过 Cookie 认出你来,实现广告“精准打击”。

拓展:如果 Cookie 的 Max-Age 属性设置为 0,会有什么效果?

0秒就让Cookie失效,即立即失效,不存Cookie。但是和不设置还是有区别,max-age=0是指不能缓存,但在会话期间是可用的,浏览器会话关闭之前可以用cookie记录用户的信息。