与 URI(Uniform Resource Identifier,统一资源标识符) 相比,我们更熟悉 URL(UniformResource Locator,统一资源定位符)。URL正是使用 Web 浏览器等访问 Web 页面时需要输入的网页地址。
每个URL地址由两部分组成:存放对象的服务器主机名和对象的路径名。
URI 是 Uniform Resource Identifier 的缩写。RFC2396 分别对这 3 个单词进行了如下定义。
URI 可以用字符串标识某一互联网资源,而 URL表示资源的地点(互联网上所处的位置)。可见 URL是 URI 的子集。
HTTP 报文是在 HTTP 应用程序之间发送的数据块。这些数据块以一些文本形式的元信息(meta-information)开头,这些信息描述了报文的内容及含义,后面跟着可选的数据部分。这些报文在客户端、服务器和代理之间流动。
HTTP 报文是简单的格式化数据块。每条报文都包含一条来自客户端的请求,或者一条来自服务器的响应。它们由三个部分组成:对报文进行描述的起始行(start line)、包含属性的首部(header)块,以及可选的、包含数据的主体(body)部分。
所有的 HTTP 报文都以一个起始行作为开始。
请求行
响应行
跟在起始行后面的就是零个、一个或多个 HTTP 首部字段,HTTP 首部字段向请求和响应报文中添加了一些附加信息。本质上来说,它们只是一些名 / 值对(键值对)的列表。
HTTP 规范定义了几种首部字段。应用程序也可以随意发明自己所用的首部。HTTP首部可以分为以下五类。
通用首部
:既可以出现在请求报文中,也可以出现在响应报文中。 不论报文是何类型,都为其提供一些有用信息。
请求首部
:提供更多有关请求的信息。
响应首部
:提供更多有关响应的信息。
实体首部
:描述主体的长度和内容,或者资源自身。
扩展首部
:规范中没有定义的新首部。
HTTP 报文的第三部分是可选的实体主体部分,她与首部之间有一个空行。实体的主体是 HTTP 报文的负荷。就是 HTTP 要传输的内容。
HTTP 报文可以承载很多类型的数字数据:图片、视频、HTML 文档、软件应用程序、信用卡事务、电子邮件等。
GET
:获取资源
POST
:传输实体主体
PUT
:传输文件
HEAD
:获得报文首部
DELETE
:删除文件
OPTIONS
:询问支持的方法
TRACE
:追踪路径
CONNECT
:要求用隧道协议连接代理
GET 和 POST 的区别是什么?
在 HTTP 协议层面
:
状态码的职责是当客户端向服务器端发送请求时,描述返回的请求结果。借助状态码,用户可以知道服务器端是正常处理了请求,还是出现了错误。
2XX 成功
:2XX 的响应结果表明请求被正常处理了。
3XX 重定向
:3XX 响应结果表明浏览器需要执行某些特殊的处理以正确处理请求。
4XX 客户端错误
:4XX 的响应结果表明客户端是发生错误的原因所在。
5XX 服务器错误
:5XX 的响应结果表明服务器本身发生错误。
大部分状态码
HTTP 连接的性能很大程度上取决于TCP连接的性能,此处暂不考虑TCP的种种具体实现,只要知道TCP的连接和断开连接都存在时延。如果只对连接进行简单的管理,TCP 的性能时延可能会叠加起来。
例如, 假设有一个包含了 3 个嵌入图片的 Web 页面。浏览器需要发起 4 个 HTTP 事务来显示此页面:1 个用于顶层的 HTML 页面,3 个用于嵌入的图片。如果每个事务都需要(串行地建立)一条新的连接,那么连接时延和慢启动时延就会叠加起来。
所以提供了一些方法可以提高HTTP的连接性能:
以下会进行详细介绍:
HTTP 允许客户端打开多条连接,并行地执行多个 HTTP 事务。在这个例子中,并行加载了四幅嵌入式图片,每个事务都有自己的 TCP 连接。
并行连接可能会提高页面的加载速度
并行连接不一定更快
实际上,浏览器确实使用了并行连接,但它们会将并行连接的总数限制为一个较小的值(通常是 4 个)。服务器可以随意关闭来自特定客户端的超量连接。
Web 客户端经常会打开到同一个站点的连接。因此,初始化了对某服务器 HTTP 请求的应用程序很可能会在不久的将来对那台服务器发起更多的请求,这种性质被称为站点局部性(site locality)
因此,HTTP/1.1(以及 HTTP/1.0 的各种增强版本)允许 HTTP 设备在事务处理结束之后将 TCP 连接保持在打开状态,以便为未来的 HTTP 请求重用现存的连接。在事务处理结束之后仍然保持在打开状态的 TCP 连接被称为持久连接。非持久连接会在每个事务结束之后关闭。持久连接会在不同事务之间保持打开状态,直到客户端或服务器决定将其关闭为止。
重用已对目标服务器打开的空闲持久连接,就可以避开缓慢的连接建立阶段。而且,已经打开的连接还可以避免慢启动的拥塞适应阶段,以便更快速地进行数据的传输。
并行连接的缺点
持久连接的缺点
持久连接与并行连接配合使用可能是最高效的方式。现在,很多 Web 应用程序都会打开少量的并行连接,其中的每一个都是持久连接。
HTTP/1.1 逐渐停止了对 keep-alive 连接的支持,用一种名为持久连接(persistentconnection)的改进型设计取代了它。
与 HTTP/1.0+ 的 keep-alive 连接不同,HTTP/1.1 持久连接在默认情况下是激活的。如果要在事务处理结束之后将连接关闭,HTTP/1.1 应用程序必须向报文中显式地添加一个 Connection:close 首部。
HTTP/1.1 允许在持久连接上可选地使用请求管道。这是相对于 keep-alive 连接的又一性能优化。
在响应到达之前,可以将多条请求放入队列。当第一条请求通过网络流向地球另一端的服务器时,第二条和第三条请求也可以开始发送了。在高时延网络条件下,这样做可以降低网络的环回时间,提高性能。
所有 HTTP 客户端、服务器或代理都可以并且可能在任意时刻关闭一条 TCP 传输连接。
每条 HTTP 响应都应该有精确的 Content-Length 首部,用以描述响应主体的尺寸。客户端或代理收到一条随连接关闭而结束的 HTTP 响应之后就应该根据 Content-Length 和接收到的主题校验数据的正确性。
即使在非错误情况下,连接也可以在任意时刻关闭。HTTP 应用程序要做好正确处理非预期关闭的准备。如果在客户端执行事务的过程中,传输连接关闭了,那么,除非事务处理会带来一些副作用,否则客户端就应该重新打开连接,并重试一次。
有些事务,比如 GET 一个静态的 HTML 页面,可以反复执行多次,也不会有什么变化。而其他一些事务,比如向一个在线书店 POST 一张订单,就不能重复执行,不然会有下多张订单的危险。
如果一个事务,不管是执行一次还是很多次,得到的结果都相同,这个事务就是幂等的。
代理是一种有转发功能的应用程序,它扮演了位于服务器和客户端“中间人”的角色,接收由客户端发送的请求并转发给服务器,同时也接收服务器返回的响应并转发给客户端。
使用代理服务器的理由:
缓存代理:代理转发响应时,缓存代理(Caching Proxy)会预先将资源的副本(缓存)保存在代理服务器上。当代理再次接收到对相同资源的请求时,就可以不从源服务器那里获取资源,而是将之前缓存的资源作为响应返回。
透明代理:转发请求或响应时,不对报文做任何加工的代理类型被称为透明代理(Transparent Proxy)。反之,对报文内容进行加工的代理被称为非透明代理。
网关的工作机制和代理十分相似。而网关能使通信线路上的服务器提供非 HTTP 协议服务。网关类似提供了一个翻译器的功能。
网关是资源和应用程序之间的粘合剂。应用程序可以(通过 HTTP 或其他已定义的接口)请求网关来处理某条请求,网关可以提供一条响应。网关可以向数据库发送查询语句,或者生成动态的内容,就像一个门一样:进去一条请求,出来一个响应。
利用网关能提高通信的安全性,因为可以在客户端与网关之间的通信线路上加密以确保连接的安全。然后由网关解密之后向服务器发送HTTP请求。
Web 隧道可以通过 HTTP 应用程序访问使用非 HTTP 协议的应用程序。
Web 隧道允许用户通过 HTTP 连接发送非 HTTP 流量,这样就可以在 HTTP 上捎带其他协议数据了,使用 Web 隧道最常见的原因就是要在 HTTP 连接中嵌入非 HTTP流量,这样,这类流量就可以穿过只允许 Web 流量通过的防火墙了。
最初开发 Web 隧道是为了通过防火墙来传输加密的 SSL 流量。很多组织都会将所有流量通过分组过滤路由器和代理服务器以隧道方式传输,以提升安全性。但有些协议,比如加密 SSL,其信息是加密的,无法通过传统的代理服务器转发。隧道会通过一条 HTTP 连接来传输 SSL 流量,以穿过端口 80 的 HTTP 防火墙。
简单来说就是,隧道可按要求建立起一条与其他服务器的通信线路,届时使用 SSL等加密手段进行通信。隧道的目的是确保客户端能与服务器进行安全的通信。
缓存是指代理服务器或客户端本地磁盘内保存的资源副本。利用缓存可减少对源服务器的访问,因此也就节省了通信流量和通信时间。
缓存不仅可以存在于缓存服务器内,还可以存在客户端浏览器中。浏览器缓存如果有效,就不必再向服务器请求相同的资源了,可以直接从本地磁盘内读取。
另外,当判定缓存过期后,会向源服务器确认资源的有效性。若判断浏览器缓存失效,浏览器会再次请求新资源。
强缓存和协商缓存
HTTP 最初是一个匿名、无状态的请求 / 响应协议。服务器处理来自客户端的请求,然后向客户端回送一条响应。Web 服务器几乎没有什么信息可以用来判定是哪个用户发送的请求,也无法记录来访用户的请求序列。
产生如下几种用户识别机制
cookie 是当前识别用户,实现持久会话的最好方式。Cookie 技术通过在请求和响应报文中写入 Cookie 信息来控制客户端的状态。
可以笼统地将 cookie 分为两类:
会话 cookie
: 是一种临时 cookie,它记录了用户访问站点时的设置和偏好。用户退出浏览器时,会话cookie 就被删除了。持久 cookie
: 生存时间更长一些;它们存储在硬盘上,浏览器退出,计算机重启时它们仍然存在。通常会用持久 cookie 维护某个用户会周期性访问的站点的配置文件或登录名。Cookie 会根据从服务器端发送的响应报文内的一个叫做 Set-Cookie 的首部字段信息,通知客户端保存 Cookie。当下次客户端再往该服务器发送请求时,客户端会自动在请求报文中加入 Cookie 值后发送出去。
服务器端发现客户端发送过来的 Cookie 后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。
由于 cookie 不安全且保存数据有限制,session 占用服务器端内存,并且不适用于移动端,所以引入 token 机制。例如用户登录,只需要第一次登录的时候进行验证,登录成功之后生成一个 token 返回给客户端,客户端保存token 并且每次请求将token 放置在 HTTP 首部的Authorization字段即可。
JWT(Json Web Token)就是一种具体的实现机制。
JWT 本质上就是⼀段签名的 JSON 格式的数据。由于它是带有签名的,因此接收者便可以验证它的真实性。
JWT 主要包括三部分
Header(首部)
:描述 JWT 的元数据。定义了生成签名的算法以及 Token 的类型。Payload(负载)
:用来存放实际需要传递的数据。Signature(签名)
:服务器通过 Payload 、 Header 和⼀个密钥( secret ) 使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。认证就是要给出一些身份证明。
HTTP 定义了两个官方的认证协议:基本认证和摘要认证。今后人们可以随意设计一些使用 HTTP 质询 / 响应框架的新协议。
HTTP 提供了一个原生的质询 / 响应(challenge/response)框架,Web 应用程序收到一条 HTTP 请求报文时,服务器没有按照请求执行动作,而是以一个“认证质询”进行响应,要求用户提供一些保密信息来说明他是谁,从而对其进行质询。
基本认证
摘要认证
使用 HTTPS 时,所有的 HTTP 请求和响应数据在发送到网络之前,都要进行加密。HTTPS 在 HTTP 下面提供了一个传输级的密码安全层——可以使用SSL,也可以使用其后继者——传输层安全(Transport Layer Security,TLS)。
在对HTTPS进行讨论之前,先介绍几个术语:
密码
:对文本进行编码,使偷窥者无法识别的算法。密钥
:改变密码行为的数字化参数。对称密钥加密系统
:编 / 解码使用相同密钥的算法。
不对称密钥加密系统
:编 / 解码使用不同密钥的算法。
公开密钥加密系统
:一种能够使数百万计算机便捷地发送机密报文的系统。数字签名
:用来验证报文未被伪造或篡改的校验和。数字证书
:由一个可信的组织验证和签发的识别信息。
传统的 TLS 握⼿基本都是使⽤ RSA 算法来实现密钥交换的,在将 TLS 证书部署服务端时,证书⽂件中包含⼀对公私钥,其中公钥会在 TLS 握⼿阶段传递给客户端,私钥则⼀直留在服务端,⼀定要确保私钥不能被窃取。
在 RSA 密钥协商算法中,客户端会⽣成随机密钥,并使⽤服务端的公钥加密后再传给服务端。根据⾮对称加密算法,公钥加密的消息仅能通过私钥解密,这样服务端解密后,双⽅就得到了相同的密钥,再用它加密应⽤消息。
第一次握手
客户端⾸先会发⼀个「Client Hello」消息,消息⾥⾯有客户端使⽤的 TLS 版本号、⽀持的密码套件列表,以及⽣成的客户端随机数(Client Random),这个随机数会被服务端保留,它是⽣成对称加密密钥的材料之⼀。
第⼆次握⼿
当服务端收到客户端的「Client Hello」消息后,会确认 TLS 版本号是否⽀持,和从密码套件列表中选择⼀个密码套件,以及⽣成服务端随机数(Server Random)。
接着,返回「Server Hello」消息,消息⾥⾯有服务器确认的 TLS 版本号,也给出了随机数(Server Random),和选择的密码套件。
然后,服务端为了证明⾃⼰的身份,会发送「Server Certificate」给客户端,这个消息⾥含有数字证书。
数字证书是什么?
第三次握手
客户端验证完证书后,认为可信则继续往下⾛。接着,客户端就会⽣成⼀个新的随机数 (pre-master),⽤服务器的 RSA 公钥加密该随机数,通过「Change Cipher Key Exchange」消息传给服务端。
服务端收到后,⽤ RSA 私钥解密,得到客户端发来的随机数 (pre-master)。
客户端和服务端双⽅都共享了三个随机数,分别是 Client Random、Server Random、pre-master。于是,双⽅根据已经得到的三个随机数,⽣成会话密钥(Master Secret),也就是对称密钥,⽤于对后续的 HTTP请求/响应的数据加解密。
⽣成完会话密钥后,然后客户端发⼀个「Change Cipher Spec」,告诉服务端开始使⽤加密⽅式发送消息。
然后,客户端再发⼀个「Encrypted Handshake Message(Finishd)」消息,把之前所有发送的数据做个摘要,再⽤会话密钥(master secret)加密⼀下,让服务器做个验证,验证加密通信是否可⽤和之前握⼿信息是否有被中途篡改过。
在发送「Change Cipher Spec」之前传输的 TLS 握⼿数据都是明⽂,之后都是对称密钥加密的密⽂。
第四次握⼿
服务器也是同样的操作,发「Change Cipher Spec」和「Encrypted Handshake Message」消息,如果双⽅都验证加密和解密没问题,那么握⼿正式完成。
最后,就⽤「会话密钥」加解密 HTTP 请求和响应了。
RSA 算法的缺陷
使⽤ RSA 密钥协商算法的最⼤问题是不⽀持前向保密。因为客户端传递随机数(⽤于⽣成对称加密密钥的条件之⼀)给服务端时使⽤的是公钥加密的,服务端收到到后,会⽤私钥解密得到随机数。所以⼀旦服务端的私钥泄漏了,过去被第三⽅截获的所有 TLS 通讯密⽂都会被破解。
第一次握手
客户端⾸先会发⼀个「Client Hello」消息,消息⾥⾯有客户端使⽤的 TLS 版本号、⽀持的密码套件列表,以及⽣成的随机数(Client Random)。这和上面利用RSA算法的过程相同。
第⼆次握⼿
服务端收到客户端的消息之后会返回「Server Hello」消息,消息⾯有服务器确认的 TLS 版本号,也给出了⼀个随机数(Server Random),然后从客户端的密码套件列表选择了⼀个合适的密码套件。
接着,服务端为了证明⾃⼰的身份,发送「Certificate」消息,会把证书也发给客户端。
因为服务端选择了 ECDHE 密钥协商算法,所以会在发送完证书后,发送「Server Key Exchange」消息。
这个过程服务器做了三件事:
为了保证这个椭圆曲线的公钥不被第三⽅篡改,服务端会⽤ RSA 签名算法给服务端的椭圆曲线公钥做个签名。
随后,就是「Server Hello Done」消息,⾄此,TLS 两次握⼿就已经完成了,⽬前客户端和服务端通过明⽂共享了这⼏个信息:Client Random、Server Random 、使⽤的椭圆曲线、椭圆曲线基点、服务端椭圆曲线的公钥。
第三次握手
客户端收到了服务端的证书后,⾃然要校验证书是否合法,确认证书的真实性,再⽤证书的公钥验证签名,这样就能确认服务端的身份。
之后,客户端会⽣成⼀个随机数作为客户端椭圆曲线的私钥,然后再根据服务端前⾯给的信息,⽣成客户端的椭圆曲线公钥,然后⽤「Client Key Exchange」消息发给服务端。
至此,所有的信息都已经共享,最终的会话密钥,就是⽤「客户端随机数 + 服务端随机数 + x(ECDHE 算法算出的共享密钥,通过椭圆曲线,椭圆基点,对方公钥和自己的私钥计算x坐标得到) 」三个材料⽣成的。
算好会话密钥后,客户端会发⼀个「Change Cipher Spec」消息,告诉服务端后续改⽤对称算法加密通信。
接着,客户端会发「Encrypted Handshake Message」消息,把之前发送的数据做⼀个摘要,再⽤对称密钥加密⼀下,让服务端做个验证,验证下本次⽣成的对称密钥是否可以正常使⽤。
第四次握⼿
最后,服务端也会有⼀个同样的操作,发「Change Cipher Spec」和「Encrypted Handshake Message」消息,如果双⽅都验证加密和解密没问题,那么握⼿正式完成。于是,就可以正常收发加密的 HTTP 请求和响应了。
HTTP/1.1 相⽐ HTTP/1.0 性能上的改进:
HTTP/1.1 的缺陷
HTTP/1.1 的优化
HTTP/2 相⽐ HTTP/1.1 性能上的改进:
头部压缩
⼆进制格式
数据流
多路复⽤
服务器推送
HTTP/2 的缺陷
HTTP / 3 的改进
参考文献
《图解HTTP》
《HTTP权威指南》
《计算机网络-自顶向下方法》
https://mp.weixin.qq.com/s/bUy220-ect00N4gnO0697A